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.
Send one event
Section titled “Send one event”Set OWLEYE_INGEST_KEY securely in your environment, then replace owl_your_tracking_id below with the matching app ID:
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.
Send a batch
Section titled “Send a batch”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.
Read the result
Section titled “Read the result”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.