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
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
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 batchids 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
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
data.affected counts genuine
unread→read transitions here — re-marking already-read rows returns zero.
cURL
200 OK
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
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 onGET/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
theme block survives):
cURL
200 OK
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 theX-Devotel-SignatureHMAC 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 isGET /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
unread_only=truerestricts to rows withread_at: null.kindis the exact event type (the full kind catalog lives in the guide under kinds);severityis one ofinfo|warning|error.source_pillaris the product pillar that minted the row (cpaas,ucaas,ccaas,aiaas,cxaas,cspaas,naas,verify,system);categoryis 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: truemeans passpagination.cursorback as thecursorquery 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
unread_only=true — drop the flag or set it to false to see the row again:
cURL
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
cURL
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
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
404
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/notificationsstring (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/recipientsstring (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/settingsstring (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/globalstring (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/streamstring (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-countstring (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/previewstring
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-allstring
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-tokenstring
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}/readstring
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/settingsstring
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.