Track events
Record product events — churn signals — for a user.
| Endpoint | Body |
|---|---|
POST /v1/events/batch |
{ "events": [ <event>, … ] } — ≤ 500 events |
POST /v1/events/track |
a single <event> |
The event object
Section titled “The event object”{ "external_user_id": "user_8842", "event_type": "payment_failed", "occurred_at": "2026-06-14T12:00:00.000Z", "properties": { "amount_cents": 4900 }, "context": { "$message_id": "f7a1c2e0-4b3d-4f6a-9c1e-8d2b5a7e3f10" }}external_user_id string, required
Section titled “external_user_id string, required”Your own stable user id — the same id you use in your database and pass to
identify. Frontend and backend events with the same id
land on one timeline.
event_type string, required
Section titled “event_type string, required”Lowercase snake_case, matching ^[a-z0-9]+(?:_[a-z0-9]+)*$. The server
rejects anything else. Validate before sending so one bad name can’t poison
an otherwise-valid batch. See Event design for naming
guidance.
occurred_at string
Section titled “occurred_at string”When the event happened — RFC3339 UTC with millisecond precision and a Z
suffix (2026-06-14T12:00:00.000Z). Must be within +5 minutes / −30 days
of now. Capture it at event time, not send time, so buffered or offline
events keep their real time.
properties object
Section titled “properties object”Free-form event attributes. An empty value serializes as {}, never [].
context object, must contain $message_id
Section titled “context object, must contain $message_id”Free-form metadata. $message_id is required: a per-event idempotency
key — any stable unique string (UUID recommended). It must stay identical
across retries of the same event so the server can deduplicate at-least-once
delivery. See Delivery, retries & idempotency.
Batch example
Section titled “Batch example”curl -X POST https://api.whisperr.net/v1/events/batch \ -H "Content-Type: application/json" \ -H "X-API-Key: $WHISPERR_API_KEY" \ -d '{ "events": [ { "external_user_id": "user_8842", "event_type": "payment_failed", "occurred_at": "2026-06-14T12:00:00.000Z", "properties": { "amount_cents": 4900 }, "context": { "$message_id": "f7a1c2e0-…" } }, { "external_user_id": "user_9917", "event_type": "trial_expired", "occurred_at": "2026-06-14T12:00:01.250Z", "properties": {}, "context": { "$message_id": "0b6d9e44-…" } } ] }'Batches are capped at 500 events. Split larger backlogs into multiple requests, each with its own retry state.