Skip to content

Send server events

Facts reviewed:

Create an ingestion key in your app’s API Keys page. It needs events:write. Keep the secret in your server’s environment or secret store; browser installations use the public Tracking ID instead.

Set OWLEYE_INGEST_KEY securely in your environment, then replace owl_your_tracking_id below with the matching app ID:

Terminal window
curl --fail-with-body 'https://api.owleye.dev/v1/events/api' \
--header "Authorization: Bearer $OWLEYE_INGEST_KEY" \
--header 'Content-Type: application/json' \
--data '{
"site_id": "owl_your_tracking_id",
"event": {
"type": "external",
"name": "report_generated",
"page": { "path": "/reports" },
"data": { "records": { "format": "pdf", "scheduled": true } }
}
}'

timestamp is optional; omission uses the server’s current time. An explicit timestamp must be RFC 3339, no more than 90 days old or five minutes into the future. Page and environment fields are optional. Use primitive custom fields and keep personal identifiers out of the payload.

Server-originated events are not automatically joined to a visitor in their browser. Do not forge forwarded-address headers to try to assign visitor identity.

Use events instead of event, with 1–100 events belonging to the same app:

{
"site_id": "owl_your_tracking_id",
"events": [
{
"type": "external",
"name": "report_generated",
"data": { "records": { "format": "pdf" } }
},
{
"type": "external",
"name": "report_delivered",
"data": { "records": { "channel": "email" } }
}
]
}

The body limit is 1 MiB. An event name is 1–128 UTF-8 bytes, and each event’s data is bounded to 16 KiB. Custom event records allow up to ten primitive fields. The batch is validated before quota is reserved.

HTTP 202 does not always mean every event was stored. Inspect accepted, status, event_ids, and dropped when present:

Status Meaning
accepted All submitted events accepted
partially_accepted_monthly_limit Only the remaining monthly allowance was accepted
monthly_event_limit_reached Zero accepted because the allowance is exhausted

DNT and GPC headers do not change ingestion behavior. Apply the appropriate collection choices in the SDK or server-side sender before submitting events.

The API can also reject a paused app, a key for another app, malformed fields, or excessive traffic. Honor Retry-After on 429. This endpoint has no client-provided idempotency key: blindly replaying a request after a network timeout can duplicate events.