Skip to main content

Worked push samples

The operation list below documents each endpoint with its parameters, but an integrator runs one chain: register a device token → send a push → read the delivery receipt → retire the token. These samples render that chain as ordered calls with the real response envelopes you branch on. The end-to-end guide is Push integration — client SDK setup is out of scope here; this section adds the per-operation worked bodies the operations list below doesn’t carry. Copy a request as written, substitute your own ids, and compare the response envelope. Every sample shows cURL and TypeScript, matching this page’s language note. Errors follow Devotel Orbit’s { error, meta } envelope.

1. Register the device

A device becomes reachable once it registers a token against an Orbit user. Re-registering the same token upserts (idempotent), and passing app_install_id retires the previous token on a reinstall.
cURL
TypeScript
201
Save the returned id — the send below targets devices by it. For platform: "web", supply the subscription envelope (PushSubscription.toJSON()) instead of token. Once your first token lands, fire POST /api/v1/push/test-send to validate the provider credential chain against the calling user’s own devices.

2. Send a push

POST /api/v1/push/send takes one of two targeting shapes: device_token_ids (specific devices, as here) or user_ids (everywhere a user registered; ["*"] broadcasts to every opted-in device, capped at 100,000 deliverable tokens). An immediate send returns 201; read notifications[] per device — a top-level success does not mean every device accepted.
cURL
TypeScript
201
Per-device status is sent (provider accepted), failed (provider rejected — read the error string; permanent failures retire the token), or skipped (frequency cap or suppression gated it). Supply a future send_at (ISO-8601) to hold the send for a later instant; a scheduled send answers 202 instead, and GET /api/v1/push/scheduled/{id} reads the queued row back until DELETE cancels it while still scheduled (409 once it has left).

3. Read send status

GET /api/v1/push/notifications is the delivery log: every per-device row carries its status, the delivered_at / opened_at receipts the client SDK acks, and any provider error. Poll it (or listen for the push.delivered / push.opened webhooks) to reconcile the send above to a delivered / failed / skipped breakdown.
cURL
TypeScript
200
Page the log with ?cursor=<last-row-created_at>&limit=<page-size> when it grows past one screen. A skipped row never leaves the send-time status — no receipt arrives for a gated device.

4. Errors

Two error classes show up on every integration — read the issues list on 422s and map 404s on ids your tenant has never seen. 422 — malformed registration. A device registers with an unknown platform (must be ios, android, huawei, or web), or with no token / subscription for the platform it claims:
The same 422 shape answers a send with neither device_token_ids nor user_ids supplied. A send that resolves to zero deliverable tokens answers NO_DEVICE_TOKENS (422), and user_ids: ["*"] over the recipient cap answers BROADCAST_TOO_LARGE (422). 404 — unknown device id. A token id not known to this tenant — a deleted record, a typo, or another tenant’s id:
The identification token an OS hands you on reinstall is new every time — re-register rather than updating a stale id, and DELETE /api/v1/push/device-tokens/{id} on logout retires the current one.

5. Categories, scheduled sends and wake telemetry

Three smaller surfaces round out the chain above: notification categories (the action-button sets clients render), scheduled sends you queued with send_at, and the device wake telemetry your client reports after each push wake. Read one category.
GET /api/v1/push/categories/{id}
A category record carries the wire-bound identifier your client matches on, the optional sound and its action buttons.
Create categories. POST /api/v1/push/categories echoes the stored record with 201. identifier must be UPPER_SNAKE_CASE and is immutable after create because clients cache it; to change one, delete and recreate. A marketing category typically carries actions; a transactional one often needs neither actions nor a sound.
A transactional category with no actions and no sound:
Read a queued scheduled send.
GET /api/v1/push/scheduled/{id}
Returns the full queued row, payload included, so you can inspect what will go out and at which send_at before the scheduler dispatches it.
Cancel it while still queued. DELETE /api/v1/push/scheduled/{id} is a guarded status change, not a hard delete: only a row still in scheduled status is cancelled. It answers 200 with the id and its new status, 404 for an unknown id and 409 once the push has left scheduled (sent or failed).
Report wake telemetry. Your client calls POST /api/v1/push/telemetry after a push wake with battery level, wake count and delivery latency; the dashboard aggregates it so operators can see the app is not draining devices. device_token_id and platform ("ios", "android", "web" or "huawei") are required and validated; battery_pct may be omitted where the platform does not expose it (web), and the authenticated user id is taken from the session, not from your body. The 201 echoes the accepted report.
Test your push configuration. POST /api/v1/push/test-send fires one fixed-shape notification at every enabled device owned by the calling operator and answers 200 with the sent / failed counts plus one verdict per device. Your configuration delivered everywhere when failed is 0 and sent is greater than zero — that is the verdict the dashboard’s tester renders as success.
When every device accepts, the envelope reads "tested": 2, "sent": 2, "failed": 0. Read the provider-level reason from the error string on each failed verdict. A permanent provider rejection such as BadDeviceToken also retires the dead token; register the device again to retest it. With no enabled device registered to the calling user, the endpoint answers NO_DEVICE_TOKENS (422) instead.

See also