HTTP API
Simple REST endpoints. No API keys for ingestion — first-party by design.
1 Ingest events
POST /api/canopy/event — accepts single or batch events. No auth, first-party only.
Request
POST /api/canopy/event
Content-Type: application/json
{
"s": "canopy_example_com", // site ID (required)
"n": "pageview", // event name (required)
"u": "https://example.com/pricing",
"p": "/pricing",
"r": "https://google.com",
"t": "Pricing - Example",
"w": 1440,
"props": { "utm_source": "newsletter" },
"sid": "abc123" // optional session ID
}Fields
| Field | Required | Description |
|---|---|---|
| s | yes | Site ID (e.g. canopy_example_com) |
| n | yes | Event name (pageview, checkout, etc.) |
| u | no | Full URL |
| p | no | Path (/pricing) |
| r | no | Referrer URL |
| t | no | Page title |
| w | no | Viewport width |
| props | no | Custom object (any JSON) |
| sid | no | Session ID (auto-generated if omitted) |
Response
202 Accepted (empty body)
Returns 202 immediately — event is queued. Batch multiple events by sending an array.
2 Query stats
GET /api/canopy/stats — requires auth (dashboard session). Returns aggregated counts.
Query params
| Param | Type | Description |
|---|---|---|
| site | string | Site ID (required) |
| range | string | 7d (default) | 24h | 30d |
| groupBy | string | day | hour | path | referrer | event |
Example response
GET /api/canopy/stats?site=canopy_example_com&range=7d
{
"site": "canopy_example_com",
"range": "7d",
"totals": {
"visitors": 12400,
"pageviews": 28700,
"events": 11200,
"uniques": 8400
},
"series": [
{ "day": "2024-01-15", "visitors": 1800, "pageviews": 4200 },
{ "day": "2024-01-16", "visitors": 1920, "pageviews": 4500 }
],
"topPaths": [
{ "path": "/", "views": 4200, "uniques": 3100 },
{ "path": "/pricing", "views": 2800, "uniques": 2100 }
],
"topReferrers": [
{ "ref": "https://newsletter.example.com", "count": 1200 }
]
}3 Webhooks
Subscribe to funnel drops, thresholds, anomalies. POST /api/canopy/webhooks (auth required).
Event types
funnel.drop
Funnel conversion drops > threshold
threshold.breach
Metric exceeds configured limit
anomaly.detected
Unusual traffic pattern detected
site.verified
Domain verified via DNS
Payload
{
"event": "funnel.drop",
"site": "canopy_example_com",
"timestamp": "2024-01-15T14:32:00.000Z",
"data": {
"funnel": "checkout",
"steps": ["view", "cart", "checkout"],
"conversion": 0.021,
"previous": 0.033,
"drop": 0.36
}
}4 Site verification
Prove domain ownership via DNS TXT record. Unlocks verified badge and webhook trust.
Verify endpoint
GET /api/verify?site=canopy_example_com
{
"site": "canopy_example_com",
"status": "pending",
"record": "canopy-site=abc123xyz",
"instructions": "Add TXT record @ with value above"
}After DNS propagates, re-check — status becomes verified and badge appears on dashboard.
5 Errors
| Code | Meaning |
|---|---|
| 400 | Missing required field (s or n) |
| 404 | Unknown site ID |
| 422 | Invalid JSON or field validation failed |
| 429 | Rate limited (per-site, generous) |
| 500 | Internal error — check logs |
Start sending events
No API keys for ingestion. Just POST to your endpoint.