Skip to content

Ingestion API overview

Last updated

The Whisperr ingestion API takes your product events, user identities and consent records, and answers data requests about a user. Use this page when you call the API directly. If an SDK exists for your stack, use the SDK: it does the batching, retries and idempotency for you.

https://api.whisperr.net

All paths on this page start with /v1/.

EndpointKeyPurpose
POST /v1/events/batchpublishable or serverSend up to 500 events. See Track events.
POST /v1/events/trackpublishable or serverSend one event. See Track events.
POST /v1/identifypublishable or serverSet a user’s traits and contact channels. See Identify users.
POST /v1/consent/assertionsserver onlyRecord proof of a consent choice. See Contact consent.
GET /v1/users/{external_id}/exportserver onlyExport everything Whisperr stores about a user. See Export and erase a user.
DELETE /v1/users/{external_id}server onlyErase a user. See Export and erase a user.

Use /v1/events/batch for anything that sends more than one event. All SDKs use it. /v1/events/track is for one-off calls such as scripts and webhooks.

Whisperr has two kinds of ingestion key. Create them in the dashboard under Settings → API keys. Every workspace can create keys, also before the trial starts.

Dashboard labelPrefixWhere it runsWhat it can do
Appwpk_…Apps and browsersSend events and identify calls. It ships inside your app, so anyone can read it.
Serverwrk_…Your backend onlyEverything the app key can do. It can also always opt a contact in, change a user’s existing email address, record consent proof, and export or erase a user.

Keep the server key in your backend’s secrets. Never put it in client code. See Contact consent for when the app key can opt a contact in, and what only the server key can do. See API keys to create, replace or revoke a key.

Send the key in one of these two headers. Both work on every endpoint.

X-API-Key: wrk_your_server_key
Authorization: Bearer wrk_your_server_key

A request without a key gets 401 missing_api_key. A wrong or revoked key gets 401 invalid_api_key.

Put your server key in an environment variable, then send one event. The occurred_at and $message_id values are generated by the shell, so you can run the command more than once.

export WHISPERR_SERVER_KEY=wrk_your_server_key
curl https://api.whisperr.net/v1/events/track \
-H "X-API-Key: $WHISPERR_SERVER_KEY" \
-H "Content-Type: application/json" \
-d @- <<EOF
{
"external_user_id": "user_8842",
"event_type": "payment_failed",
"occurred_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"properties": { "amount_cents": 4900 },
"context": { "\$message_id": "$(uuidgen)" }
}
EOF

The answer is 202 Accepted:

{ "event": { "mapping_status": "pending" }, "processing": "queued" }

202 means Whisperr stored the request. It processes the event right after. See Accepted, then processed.

Yes, for the three ingestion endpoints. POST /v1/events/batch, POST /v1/events/track and POST /v1/identify allow cross-origin requests from any origin, with the publishable key. The consent, export and erase endpoints need the server key and do not allow cross-origin requests.

The Whisperr API rejects unknown fields. A misspelled field name fails with 400 instead of being ignored. This keeps every SDK on the same contract. In a batch, only the event with the problem is rejected. See Response handling.

LimitValue
Body of POST /v1/events/track or POST /v1/identify1 MiB
Body of POST /v1/events/batch10 MiB
Events per batch1 to 500
properties of one event, as JSON50 KB
traits of one identify, as JSON50 KB
channels per identify or event10
anonymous_id1 to 128 printable characters
$message_id200 bytes. A longer value is ignored, so the event is not deduplicated.

A body over its limit gets 413 payload_too_large.

Whisperr counts requests per API key in fixed one-minute windows. Every counted response carries these headers:

HeaderMeaning
X-RateLimit-LimitRequests this key may make in one window.
X-RateLimit-RemainingRequests left in the current window.
X-RateLimit-ResetEnd of the current window, in Unix seconds.

Over the limit, the answer is 429 rate_limited with a Retry-After header in seconds. Wait that long, then retry. All browsers of one app share the publishable key, so they share its budget.

Every error from the Whisperr API has the same JSON shape:

{
"error": {
"code": "invalid_request",
"message": "event_type must be snake_case",
"request_id": "8d2f6a0c1b3e4f5a"
}
}

Match on code. message is for people and can change.

StatuscodeMeaning
400invalid_jsonThe body is not one JSON object, or it has an unknown field.
400invalid_requestA field failed validation. message names it.
401missing_api_keyNo key in X-API-Key or Authorization.
401invalid_api_keyThe key is wrong or revoked.
403server_key_requiredExport and erase need the server key.
404user_not_foundExport found no user with this id.
404route_not_implementedThe path does not exist.
413payload_too_largeThe body is over its size limit.
429rate_limitedThe key is over its rate limit.
500internal_errorA fault on the Whisperr side. Retry.
502, 504upstream_unavailableThe Whisperr API did not answer. Retry.

POST /v1/consent/assertions uses a shorter error body. See Contact consent.

Every response carries an X-Request-Id header with the same value as error.request_id. To use your own id, send an X-Request-ID header.

Common questions

Can I call the Whisperr ingestion API from a browser?

Yes, for POST /v1/events/batch, POST /v1/events/track and POST /v1/identify with the publishable key (wpk_…). These three endpoints allow cross-origin requests from any origin. The other endpoints need the server key and do not answer browser preflight requests.

Does the Whisperr API need a Content-Type header?

The ingestion endpoints read the body as JSON whatever the Content-Type is. Send Content-Type: application/json anyway, as every Whisperr SDK does.

What happens when I send a field the Whisperr API does not know?

The request fails with 400 invalid_json. Whisperr rejects unknown fields so that a misspelled field name is never silently ignored. In a batch, only the event with the unknown field is rejected.

How do I trace a failed request with Whisperr support?

Every response carries an X-Request-Id header, and every error body carries the same value as error.request_id. Quote it when you report a problem.