Skip to main content

Notifications API

The Notifications API backs the dashboard bell: a per-user feed of in-app alerts — delivery failures, billing events, security sign-ins, port-status changes, agent handoffs, SLA breaches, and more. Read the feed with the list endpoints, mark rows read or dismiss them, and subscribe to GET /api/v1/notifications/stream to receive new notifications in real time. Base path: /api/v1/notifications Authentication: session JWT. Notifications are scoped to the signed-in user and their tenant, so these endpoints use your dashboard session rather than a workspace API key. The live stream supports a query-parameter token so browsers can authenticate EventSource connections (see Live stream).

The notification object

  • severity is one of info, warning, error — it drives the icon colour.
  • read_at is null until the notification is marked read.
  • source_pillar and category are coarse facets used by the dashboard filter chips. source_pillar is one of cpaas, ucaas, ccaas, aiaas, cxaas, cspaas, naas, verify, system. category is one of sla_breach, missed_call, campaign_result, agent_state, verification, webhook_failure, billing, security, compliance, system.
  • Notifications past their expires_at are filtered out of the list automatically.

List notifications — GET /api/v1/notifications/

Returns your notifications, newest first, in a cursor-paginated page.
Query parameters
To fetch the next page, pass pagination.cursor back as the cursor query parameter. When has_more is false, cursor is null and you have reached the end of the feed.

Unread count — GET /api/v1/notifications/unread-count

Returns { "count": <n> } — the number backing the bell badge.

Mark read — PUT /api/v1/notifications/{id}/read

Marks a single notification read and returns the updated row. Returns 404 if the id does not exist or is not yours.

Mark all read — POST /api/v1/notifications/read-all

Marks every unread notification read in one call. Returns { "affected": <n> } — the number of rows updated.

Dismiss — DELETE /api/v1/notifications/{id}

Deletes one of your notifications and returns { "deleted": "<id>" }. Returns 404 if the id does not exist or is not user-scoped.
Organization-wide notifications are shared across the workspace and cannot be dismissed by an individual user — they clear on their own when they expire. Dismiss applies only to your own user-scoped rows.

Live stream — SSE

GET /api/v1/notifications/stream pushes new notifications to the browser in real time over Server-Sent Events. Browsers cannot set request headers on an EventSource, so the stream accepts the session token as a query parameter. Two authentication paths are supported:
  • One-time token (preferred). Call POST /api/v1/notifications/sse-token to mint a short-lived token, then open the stream with ?ot=<token>. This keeps your long-lived session JWT out of server access logs and browser history.
  • Legacy JWT (fallback). Open the stream with ?token=<session-jwt> directly. Use this only when a one-time token is unavailable.

Mint a one-time token — POST /api/v1/notifications/sse-token

On success returns 201 with the token and its lifetime:
The token is valid for 90 seconds and is meant to be redeemed once, immediately, by opening the stream. Mint a fresh token each time you (re)connect. If the token store is briefly unavailable the endpoint degrades gracefully: it returns 200 with { "ot": null, "fallback": "token" } instead of an error. When you see ot: null, fall back to opening the stream with ?token=<session-jwt>.

Open the stream — GET /api/v1/notifications/stream

The stream opens with a connected event, then emits one event per new notification. The event name reflects the notification type (for example notification.created); the data: line carries the notification JSON.
A : heartbeat comment is sent every 30 seconds to keep the connection alive. If the stream ends, reconnect — mint a fresh one-time token first if you are using the ?ot= path.
Concurrency cap — 50 streams per tenant. Each tenant may hold at most 50 concurrent SSE connections to this endpoint across the whole platform. Opening another stream over the cap is rejected with 429 and error.code = TOO_MANY_CONNECTIONS. Close streams you no longer need (each open browser tab holds one), and reuse a single connection per tab rather than opening one per component.
The stream is for browser clients: connections must come from a trusted dashboard origin with a browser User-Agent. Non-browser clients are rejected — to consume the same events from a server, use the durable Webhooks push instead.

See also

  • Webhooks — durable, retry-handled push for server-side consumers.
  • Events API — the platform-wide live event firehose.