Skip to content

Record an event

The ingest endpoint every SDK speaks — one batch, one project, a per-event answer and never a silent drop.

4 min read Updated Suggest an edit
POST/api/event

For servers, mobile apps, background jobs and anything else that cannot run the browser tracker. Every DataVisitors SDK speaks this protocol; you can speak it directly.

Plain text
POST https://api.datavisitors.com/api/eventAuthorization: Bearer YOUR-INSTALL-KEYContent-Type: application/json

The key is an event-sending key for the project. It can write events there and read nothing back.

Give your servers their own sending key rather than the browser's install key. A key locked to browser origins refuses a request that carries no Origin header, which is every request a server makes — see Allowed origins.

The envelope

One request carries one project's batch.

protocol yes

1. It bumps only on a breaking shape change.

sdk yes

A name and a version, so a dashboard can tell your integration apart from the tracker's.

events yes

At most 100 events, at most 512 KB for the whole batch.

event_id yes

A ULID or UUIDv7 you mint. It is the deduplication key.

event_name yes

[a-z][a-z0-9_]{0,63}. The sg_ prefix is the platform's.

occurred_at yes

When it happened on your side. No further back than 7 days, no further forward than 10 minutes.

anonymous_id, session_id with consent

^[A-Za-z0-9_-]{8,64}$. Absent under stateless.

user_id identified only

Only when context.consent is identified.

context.consent yes

stateless, anonymous or identified. pending is refused on purpose.

properties no

At most 32 keys, depth 2, string values up to 1 KB.

environment no

[a-z][a-z0-9_-]{0,31}, default production.

Unknown top-level fields are refused outright — a typo in an envelope field is a bug you want to hear about. Unknown property keys are your own schema and are kept.

The answer

  • The 202 is returned after the event is stored. An acknowledged event is a stored event.
  • rejected carries one entry per refused event, each with its index in your batch and a stable code. Nothing is ever dropped without a result — Error codes lists every code.
  • regime is the privacy regime the server resolved for this visitor. Under optout, a batch sent with no identifiers is answered with the anonymous_id and session_id the server derived, so a browser can adopt them. A server-side sender can ignore all of this.

Statuses other than 202 refuse the whole request: 400 for a batch that cannot be read or fails a rule, 401 for a key that is unknown or revoked, 403 when the key's origin rules refuse this request, 405 for anything but POST, 429 when you are sending too fast, and 503 when storage is briefly unavailable — retry that one.

Sending safely

  • Mint an event_id per event and keep it across retries. A retried batch is de-duplicated by that id for 24 hours, or the project's last 100,000 events, whichever is shorter. A restart of the service forgets the window, so at-least-once is the floor we promise.
  • Retry 429 and 5xx only. A 400, 401 or 403 will be refused identically next time; fix the request instead.
  • A key that names its websites refuses every other one with 403. A leading www. is forgiven; any other subdomain is a different website and needs its own entry. Refused requests are counted under Tracking → Ignored traffic.
  • One identity per visitor, not per process. A server that mints a fresh anonymous_id per request turns every request into a new visitor. Counting correctly has the whole failure and its fix.
  • Do not send page views from the server when the browser tracker is installed. They are the same page load counted twice, under two identities.

Most integrations should use a maintained SDK instead of speaking this by hand — see SDKs.