Skip to main content

Notifications API

Notifications endpoints exposed by the Devotel CPaaS API Base path: /api/v1/notifications Endpoint count: 13

Worked sequences

The endpoint list below documents each operation with its parameters. These four sequences cover the read-and-triage loop that powers a notification center of your own: poll the badge, page through the filtered feed, clear it either selectively or in bulk, and set the toggles that decide which categories each member gets notified about. Every request uses your API key (X-API-Key) against https://api.orbit.devotel.io. For the operator-facing breakdown of kinds, delivery channels and the console workflow, see Triage the Notification Center.

Sequence 1 — poll the badge, then read the feed

The loop an external notification center runs on a cadence (the dashboard bell runs it every ~30 seconds per tab): check the counter, and only fetch the filtered list when the counter moved. Step 1 — check the unread counter.
cURL
200 OK
Step 2 — list the filtered feed. unread_only, kind, severity, source_pillar, and category combine on the query string, newest rows first. meta.pagination.has_more tells you whether another page exists; pass meta.pagination.cursor back as cursor and treat it as opaque, keeping the same filters across pages. A malformed filter value is ignored rather than rejected — the list loads unfiltered.
cURL
200 OK
Here has_more is false, so this page is the whole result set — no cursor to pass. Neither this sequence nor the next has any write side effects, so the badge still reports 3 until Step 2 of Sequence 2 runs.

Sequence 2 — clear items, selectively or in bulk

Marking read and dismissing are addressed one row per request — there is no batch ids body on either. When you clear several rows at once (select-all in a notification UI, or a sync loop draining its backlog), loop over the ids and issue the per-row calls in parallel. For “clear everything”, prefer one call to read-all over N per-row calls. Step 1 — mark two rows read. PUT /{id}/read is idempotent, so a parallel-loop retry past the first success is safe; it returns the updated row with read_at populated.
cURL
200 OK
Step 2 — dismiss the other row. DELETE /{id} hides the row immediately for the caller. It takes a 404 NOT_FOUND when the row is org-wide (user_id: null) rather than addressed to you — those rows age out on their own expires_at clock instead of being dismissible.
cURL
200 OK
Step 3 — bulk-clear the rest. One call flips every remaining unread row to read and returns how many it touched. data.affected counts genuine unread→read transitions here — re-marking already-read rows returns zero.
cURL
200 OK
The Node SDK’s notifications.list / notifications.markRead / notifications.markAllRead / notifications.archive helpers wrap these exact routes. For the routes it has no helper for — the unread counter, the org digest toggles below, the /{id}/read PUT — use the generic orbit.request escape hatch:
Node.js

Sequence 3 — read + tune the org digest toggles

The per-organization email digest is the corporate preference surface: which categories are emailed, on what cadence, and to which members. Owner/admin leaves configure it; members inherit it. Security-critical kinds (sign-in alerts, API-key mints, role changes) ignore mute settings and always mint a bell row. Step 1 — read the current digest settings.
cURL
200 OK
categories: [] (empty array) means every category is emailed, not none. Step 2 — flip the toggles. PUT accepts any one of enabled, frequency (daily or weekly), or categories (from billing, security, messaging, campaigns, agents, system); it returns the full saved record so no follow-up GET is needed.
cURL
200 OK
Step 3 — review who’s opted in.
cURL
200 OK
PATCH /digest/recipients/{user_id} flips one member’s flag (owner/admin):
cURL

Sequence 4 — mute a category for your own seat (user-level)

The org digest above is a corporate preference; the per-seat mute is yours alone. Muting lives on GET/PUT /api/v1/settings/preferences — a namespaced user-preferences blob the dashboard uses for theme, locale and notification toggles, not under /notifications itself. Each category sits under the notifications key; a muted category still mints an in-app bell row (only outbound email/push/webhook delivery is suppressed), and security-critical kinds (sign-in, API-key, role-change events) override the mute entirely.
cURL
200 OK
Write back the whole map (unknown keys are preserved, so other surfaces’ theme block survives):
cURL
200 OK
The per-category toggles a member sets apply to their own outbound delivery only — the org digest loop (Sequence 3) still decides which categories the organization emails at all. Set both to agree: mute a row you never want, and expect the digest to follow.

Worked notification-channel samples

This page covers the second consumption shape of the notification stream. Sequences 1–4 above are the human-facing loop: pull the in-app bell feed, triage it, tune the digest. The samples below treat the same feed as a channel you program against — a service, not an operator, reading rows and applying filters. There are two consumption shapes on this page:
  • The dashboard-bell feed (list/read) — persistent rows you poll with GET /api/v1/notifications/ and clear with the mark-read and dismiss calls.
  • Subscription-based routing to outbound sinks — the per-user category subscription (Settings → notifications), the per-organization digest (/digest/settings), and the SSE stream (/stream) decide which rows leave the bell and land on email, push, or a live socket. Webhook endpoints (POST /api/v1/webhooks, event subscriptions documented under Webhooks) are the code-facing sibling: the same developer-failure kinds (dlq_alert, webhook_endpoint_disabled) that mint bell rows also fan out to your subscribed webhook endpoints. For the payload shape your webhook sink receives, see Consume webhooks; for verifying the X-Devotel-Signature HMAC on delivery, see Verify webhook signatures.

1. List the in-app feed

Read the feed the same way the dashboard bell does — newest first, with the query filters applied server-side. At its smallest the call is GET /api/v1/notifications/?unread_only=true; every other filter appends the same way. Every filter is optional; omitting one widens the feed rather than erroring.
cURL
200 OK
Filter semantics, in short:
  • unread_only=true restricts to rows with read_at: null.
  • kind is the exact event type (the full kind catalog lives in the guide under kinds); severity is one of info | warning | error.
  • source_pillar is the product pillar that minted the row (cpaas, ucaas, ccaas, aiaas, cxaas, cspaas, naas, verify, system); category is the cross-pillar facet (sla_breach, webhook_failure, billing, security, compliance, system, …). Combine a pillar with a category to answer “every SLA breach in CCaaS” style queries.
  • pagination.has_more: true means pass pagination.cursor back as the cursor query parameter on the next call, keeping the same filters. The cursor excludes its own row, so the next page starts strictly after it.

2. Mark read and re-list

Clearing a row is one call per id — PUT /{id}/read returns the updated row with read_at set, and is idempotent so a retry after a timeout is safe.
cURL
200 OK
Re-run the same filtered list and the cleared row no longer matches unread_only=true — drop the flag or set it to false to see the row again:
cURL
To drain the feed without tracking ids, POST /api/v1/notifications/read-all flips every remaining unread row in one call and returns data.affected (see Sequence 2 above).

3. Wire an outbound subscription

There is no per-row subscription object: routing to a sink is a small set of configuration writes. Pick the sink, then enable it — the same filters you pass on the list are the ones the outbound surfaces apply for you. Email ingest per user — mind your own seat’s category subscription so the digest only carries what you act on:
cURL
Organization digest — owner/admin subscribes the whole org to the categories that warrant email, on a daily or weekly cadence:
cURL
Both return the full saved record (200) so no follow-up GET is needed, and re-issuing the same write is idempotent — there is no duplicate-subscription conflict to handle. Live push channel — for near-real-time consumption without polling, mint a one-time token and open /stream; the rows that arrive match the same kind / severity / source_pillar / category facets you filter on the list:
cURL
When the token store is unavailable, sse-token returns ot: null with a fallback hint — connect with the legacy ?token=<api-key> parameter instead (server-side callers may use it from the start).

4. Errors

A filter value the schema does not know degrades instead of failing. GET / parses its query leniently: an unknown kind or severity, an over-length cursor, or an off-range limit falls back to “no filter” and the list still loads unfiltered — it never answers 422. This protects the bell feed against stale web bundles and SDK callers, and it means you should treat a 422 from this endpoint as a bug report, not an expected case. Strict paths are the writes. A malformed path id on PUT /{id}/read is rejected before the read:
422
A well-formed id that does not belong to you returns 404. The endpoint never reveals whether the row exists elsewhere:
404
When you consume the stream through webhooks rather than the bell feed, two more failure shapes matter — a dead-letter backlog (dlq_alert) and an endpoint the API auto-disabled after repeated failures (webhook_endpoint_disabled). Both mint a bell row AND a webhook event, so your sink sees the breakage even when nobody is watching the dashboard; the runbook for re-enabling the endpoint is under Webhook events.

List notifications

GET /api/v1/notifications
string (enum: true|false)
Sandbox opt-in for Clerk-session-authenticated requests. Set to true to route the call through the test-mode pipeline: no real provider delivery, no credits deducted, response meta.test_mode: true. Ignored for live API keys (dv_live_sk_*) — server-to-server clients must use a test-prefixed key (dv_test_sk_*) to exercise sandbox. Test-prefixed keys unconditionally enable sandbox regardless of this header.

List recipients

GET /api/v1/notifications/digest/recipients
string (enum: true|false)
Sandbox opt-in for Clerk-session-authenticated requests. Set to true to route the call through the test-mode pipeline: no real provider delivery, no credits deducted, response meta.test_mode: true. Ignored for live API keys (dv_live_sk_*) — server-to-server clients must use a test-prefixed key (dv_test_sk_*) to exercise sandbox. Test-prefixed keys unconditionally enable sandbox regardless of this header.

List settings

GET /api/v1/notifications/digest/settings
string (enum: true|false)
Sandbox opt-in for Clerk-session-authenticated requests. Set to true to route the call through the test-mode pipeline: no real provider delivery, no credits deducted, response meta.test_mode: true. Ignored for live API keys (dv_live_sk_*) — server-to-server clients must use a test-prefixed key (dv_test_sk_*) to exercise sandbox. Test-prefixed keys unconditionally enable sandbox regardless of this header.

List global

GET /api/v1/notifications/global
string (enum: true|false)
Sandbox opt-in for Clerk-session-authenticated requests. Set to true to route the call through the test-mode pipeline: no real provider delivery, no credits deducted, response meta.test_mode: true. Ignored for live API keys (dv_live_sk_*) — server-to-server clients must use a test-prefixed key (dv_test_sk_*) to exercise sandbox. Test-prefixed keys unconditionally enable sandbox regardless of this header.

List stream

GET /api/v1/notifications/stream
string (enum: true|false)
Sandbox opt-in for Clerk-session-authenticated requests. Set to true to route the call through the test-mode pipeline: no real provider delivery, no credits deducted, response meta.test_mode: true. Ignored for live API keys (dv_live_sk_*) — server-to-server clients must use a test-prefixed key (dv_test_sk_*) to exercise sandbox. Test-prefixed keys unconditionally enable sandbox regardless of this header.

List unread-count

GET /api/v1/notifications/unread-count
string (enum: true|false)
Sandbox opt-in for Clerk-session-authenticated requests. Set to true to route the call through the test-mode pipeline: no real provider delivery, no credits deducted, response meta.test_mode: true. Ignored for live API keys (dv_live_sk_*) — server-to-server clients must use a test-prefixed key (dv_test_sk_*) to exercise sandbox. Test-prefixed keys unconditionally enable sandbox regardless of this header.

Preview digest

POST /api/v1/notifications/digest/preview
string
Stripe-style idempotency token. Pass a stable, client-generated value (1-255 chars) to dedupe retries on transient timeouts. The same key+credential+path replays the original response for 24h on 2xx (5min on 4xx, 30s on 5xx). Returns 409 if a concurrent request with the same key is already in flight; replayed responses include the Idempotency-Replay: true response header.
string (enum: true|false)
Sandbox opt-in for Clerk-session-authenticated requests. Set to true to route the call through the test-mode pipeline: no real provider delivery, no credits deducted, response meta.test_mode: true. Ignored for live API keys (dv_live_sk_*) — server-to-server clients must use a test-prefixed key (dv_test_sk_*) to exercise sandbox. Test-prefixed keys unconditionally enable sandbox regardless of this header.

Read-all notifications

POST /api/v1/notifications/read-all
string
Stripe-style idempotency token. Pass a stable, client-generated value (1-255 chars) to dedupe retries on transient timeouts. The same key+credential+path replays the original response for 24h on 2xx (5min on 4xx, 30s on 5xx). Returns 409 if a concurrent request with the same key is already in flight; replayed responses include the Idempotency-Replay: true response header.
string (enum: true|false)
Sandbox opt-in for Clerk-session-authenticated requests. Set to true to route the call through the test-mode pipeline: no real provider delivery, no credits deducted, response meta.test_mode: true. Ignored for live API keys (dv_live_sk_*) — server-to-server clients must use a test-prefixed key (dv_test_sk_*) to exercise sandbox. Test-prefixed keys unconditionally enable sandbox regardless of this header.

Sse-token notifications

POST /api/v1/notifications/sse-token
string
Stripe-style idempotency token. Pass a stable, client-generated value (1-255 chars) to dedupe retries on transient timeouts. The same key+credential+path replays the original response for 24h on 2xx (5min on 4xx, 30s on 5xx). Returns 409 if a concurrent request with the same key is already in flight; replayed responses include the Idempotency-Replay: true response header.
string (enum: true|false)
Sandbox opt-in for Clerk-session-authenticated requests. Set to true to route the call through the test-mode pipeline: no real provider delivery, no credits deducted, response meta.test_mode: true. Ignored for live API keys (dv_live_sk_*) — server-to-server clients must use a test-prefixed key (dv_test_sk_*) to exercise sandbox. Test-prefixed keys unconditionally enable sandbox regardless of this header.

Update read

PUT /api/v1/notifications/{id}/read
string
required
—
string
Stripe-style idempotency token. Pass a stable, client-generated value (1-255 chars) to dedupe retries on transient timeouts. The same key+credential+path replays the original response for 24h on 2xx (5min on 4xx, 30s on 5xx). Returns 409 if a concurrent request with the same key is already in flight; replayed responses include the Idempotency-Replay: true response header.
string (enum: true|false)
Sandbox opt-in for Clerk-session-authenticated requests. Set to true to route the call through the test-mode pipeline: no real provider delivery, no credits deducted, response meta.test_mode: true. Ignored for live API keys (dv_live_sk_*) — server-to-server clients must use a test-prefixed key (dv_test_sk_*) to exercise sandbox. Test-prefixed keys unconditionally enable sandbox regardless of this header.

Update settings

PUT /api/v1/notifications/digest/settings
string
Stripe-style idempotency token. Pass a stable, client-generated value (1-255 chars) to dedupe retries on transient timeouts. The same key+credential+path replays the original response for 24h on 2xx (5min on 4xx, 30s on 5xx). Returns 409 if a concurrent request with the same key is already in flight; replayed responses include the Idempotency-Replay: true response header.
string (enum: true|false)
Sandbox opt-in for Clerk-session-authenticated requests. Set to true to route the call through the test-mode pipeline: no real provider delivery, no credits deducted, response meta.test_mode: true. Ignored for live API keys (dv_live_sk_*) — server-to-server clients must use a test-prefixed key (dv_test_sk_*) to exercise sandbox. Test-prefixed keys unconditionally enable sandbox regardless of this header.

Patch recipients

PATCH /api/v1/notifications/digest/recipients/{user_id}
string
required
—
string (enum: true|false)
Sandbox opt-in for Clerk-session-authenticated requests. Set to true to route the call through the test-mode pipeline: no real provider delivery, no credits deducted, response meta.test_mode: true. Ignored for live API keys (dv_live_sk_*) — server-to-server clients must use a test-prefixed key (dv_test_sk_*) to exercise sandbox. Test-prefixed keys unconditionally enable sandbox regardless of this header.

Delete notifications

DELETE /api/v1/notifications/{id}
string
required
—
string (enum: true|false)
Sandbox opt-in for Clerk-session-authenticated requests. Set to true to route the call through the test-mode pipeline: no real provider delivery, no credits deducted, response meta.test_mode: true. Ignored for live API keys (dv_live_sk_*) — server-to-server clients must use a test-prefixed key (dv_test_sk_*) to exercise sandbox. Test-prefixed keys unconditionally enable sandbox regardless of this header.