REST API overview
Base URL: your deployment, e.g. https://push.example.com. All endpoints are under /v1. Bodies are JSON; validation is strict and errors say which field.
An interactive OpenAPI browser is served at /docs on the API itself.
Authentication
| Key | Header | Prefix | For |
|---|---|---|---|
| Secret key | Authorization: Bearer sk_live_<appId>_<secret> | sk_ | your backend, CI. Everything its scopes allow. |
| Public key | x-app-key: pk_live_<appId>_<secret> | pk_ | SDKs. Register a device, update that device's user. |
| (none) | — | — | POST /v1/e/:kind/:msgId — the signed msgId is the credential. |
The app id is embedded in the key, so verification is one indexed lookup plus a bcrypt compare, cached for API_KEY_CACHE_TTL (60 s). A public key on a server endpoint answers 403 forbidden regardless of scopes. Keys are created and revoked in Settings › API keys.
Scopes
| Scope | Endpoints |
|---|---|
subscriptions:write | POST/PATCH/DELETE /v1/subscriptions/* |
subscriptions:read | POST /v1/segments/preview |
users:read / users:write | GET / PATCH,DELETE /v1/users/* |
notifications:send | POST /v1/notifications |
campaigns:read | GET /v1/campaigns/:id/stats |
campaigns:write | POST /v1/campaigns, /send, /cancel, /experiment/winner |
Rate limits
Fixed 60-second windows, per API process:
| Bucket | Default | Key |
|---|---|---|
| subscribe / rotate | SUBSCRIBE_RATE_LIMIT = 10/min | client IP |
/v1/notifications | SEND_RATE_LIMIT = 100/min | API key |
/v1/e/* | EVENT_RATE_LIMIT = 600/min | client IP |
Over the limit: 429 rate_limited with the seconds to wait in the message. Client IP comes from x-forwarded-for (first hop) or x-real-ip — make sure your proxy sets one.
Errors
One shape everywhere:
{
"error": {
"code": "invalid_body",
"message": "notification payload is invalid",
"details": [
{ "path": "content._default", "message": "_default \"fr\" is not one of: en, tr" },
{ "path": "target", "message": "provide exactly one of externalIds, userIds, subscriptionIds, segment or all" }
]
}
}
Full code list: Errors.
Idempotency
POST /v1/notifications and POST /v1/campaigns accept Idempotency-Key (≤ 255 chars); replays within 24 h return the original response with Idempotent-Replayed: true. Details: Idempotency.
Availability
GET /health→200 {"status":"ok","db":"ready"}or503while MongoDB is unreachable.- While Mongo is down every data endpoint answers
503 database_unavailablerather than queueing. - Without Redis the API does not start at all.
Endpoint index
| Method & path | Key | Scope | Page |
|---|---|---|---|
POST /v1/subscriptions | pk / sk | subscriptions:write | Subscriptions |
PATCH /v1/subscriptions/:id | pk / sk | subscriptions:write | |
POST /v1/subscriptions/:id/login | pk / sk | subscriptions:write | |
POST /v1/subscriptions/:id/logout | pk / sk | subscriptions:write | |
PATCH or POST /v1/subscriptions/:id/user | pk / sk | subscriptions:write | |
DELETE /v1/subscriptions/:id | pk / sk | subscriptions:write | |
POST /v1/subscriptions/rotate | pk / sk | subscriptions:write | |
POST /v1/subscriptions/:id/session | pk / sk | subscriptions:write | |
GET /v1/users/:id · /by-external-id/:externalId | sk | users:read | Users |
PATCH /v1/users/:id · /by-external-id/:externalId | sk | users:write | |
DELETE /v1/users/:id · /by-external-id/:externalId | sk | users:write | |
POST /v1/notifications | sk | notifications:send | Notifications |
POST /v1/campaigns | sk | campaigns:write | Campaigns |
POST /v1/campaigns/:id/send · /cancel | sk | campaigns:write | |
POST /v1/campaigns/:id/experiment/winner | sk | campaigns:write | |
GET /v1/campaigns/:id/stats | sk | campaigns:read | |
POST /v1/segments/preview | sk | subscriptions:read | Segments |
GET /v1/segments · GET /v1/segments/:id · POST /v1/segments/:id/count | sk | subscriptions:read | |
POST /v1/segments · PUT · DELETE /v1/segments/:id | sk | campaigns:write | |
GET /v1/templates · GET /v1/templates/:id | sk | campaigns:read | Templates |
POST /v1/templates · PUT · DELETE /v1/templates/:id | sk | campaigns:write | |
POST /v1/e/:kind/:msgId | none | — | Events |
GET /health | none | — | |
/v1/admin/* | ADMIN_API_TOKEN | — | dashboard only; 404 when unset |
Conventions
- Ids are 24-hex MongoDB ObjectIds as strings.
- Timestamps are ISO 8601 UTC.
PATCHbodies are partial and must contain at least one field.- The admin surface (
/v1/admin) is not a public API: it exists for the dashboard, needs the shared token, and has no stability promise.