Skip to main content

Push Notifications API

Send push notifications to your end users’ iOS and Android devices through Apple Push Notification service (APNs) and Firebase Cloud Messaging (FCM). Orbit holds your provider credentials and exposes a single send endpoint that fans out to the right transport. Base path: /api/v1/push Authentication: API key (X-API-Key) or session JWT.

The notification object

Every device token your mobile app registers, every push you send, and the per-device delivery rows they produce all share the same { data, meta } envelope — this page documents that object end to end: register a token, target a send, and read the delivery status back. The notifications[] array a send returns is one entry per device, so one transport failure never masks the others.

Using the SDKs

The push route has no first-class helper yet — each SDK’s generic escape hatch keeps auth, retries, and the response envelope identical to the typed resources while returning a body you type yourself. The samples below run the same call as the curl example, in the fixed language order used across the hand-written pages.
The Python, Go, Ruby, and PHP SDKs are core-scope — they wrap the 8 core resources (messaging, voice, contacts, campaigns, verify, numbers) and reach everything else through the untyped client.request() / client.Request(...) / $client->request(...) escape hatch shown above: Python SDK, Go SDK, Ruby SDK, PHP SDK. Raw curl and the SDK escape hatches resolve to the same endpoint. Full SDK index at SDK quickstart.

Device tokens

Your mobile app registers each device’s push token after the user grants permission. Re-register on token rotation; APNs and FCM both rotate tokens periodically.

Register a device token

POST /api/v1/push/register and POST /api/v1/push/device-tokens are the same operation — the first exists for older SDK clients; new integrations should call /device-tokens. Supply the native APNs or FCM token for ios / android / huawei, or a Web Push subscription envelope for platform: web. Registering a token that already exists just refreshes its owner and last-seen time — it does not create a duplicate.
201 Created returns the token’s Orbit id — save it if you want to target specific devices when sending:

List device tokens

GET /api/v1/push/device-tokens lists the tenant’s registered tokens, newest first, capped at 200 rows. Pass ?user_id=<id> to scope the list to one user.
200 OK returns the rows — use the id values to target a send by device_token_ids, and enabled to see whether a token currently receives pushes:

Token lifecycle — register / refresh / deregister

A device’s token is a lifecycle, not a one-time write. Register once when the OS first hands the push token over. Refresh by re-registering the same endpoint every time the OS rotates the token — APNs and FCM both rotate periodically, and a rotated token that is not re-registered stops receiving pushes. Deregister with DELETE /api/v1/push/device-tokens/{id} on sign-out so a retired device no longer receives pushes.
201 Created returns the stored token row. meta.request_id and meta.timestamp appear on every response — success and error alike — so quote them when reconciling a lifecycle event in your own audit trail:
Refresh follows the same call: re-registering a token that already exists just refreshes its owner and last-seen time — it does not create a duplicate — and sending app_install_id retires the install’s previous token on a reinstall, so the new token is the only live one. Check last_seen_at and the enabled flag in the list response to spot a token the SDK has not refreshed for a long stretch.

Unregister a device token

DELETE /api/v1/push/device-tokens/{id} removes a token so the device no longer receives pushes — for example on sign-out.
Success is 204 No Content with an empty body. An unknown id returns 404 Not Found (NOT_FOUND).

Sending

Example — send to a user’s devices

Target a send with either user_ids (every registered device for those users) or device_token_ids (specific device tokens). At least one of the two is required.
To notify specific devices instead, pass device_token_ids with the ids returned from /api/v1/push/device-tokens:
Either variant returns 201 Created on an immediate send. The response carries one notifications[] entry per targeted device, so an APNs rejection or a frequency-cap skip on one device never masks the others’ delivery:
Each entry in the response notifications[] array carries a transport field — apns, fcm, webpush, or hms — identifying which transport was used to reach that device, alongside its deviceTokenId and delivery status. A status of sent, failed, or skipped is per-device; when a device could not be reached, its entry carries an error describing why.

List sent notifications

GET /api/v1/push/notifications returns the tenant’s notification log, newest first — the per-device rows recorded by /push/send — with delivery status and the delivered/opened timestamps the SDK ack endpoints set. Page older rows with ?cursor=<created_at of the last row>&limit=50; each page is capped at 200 rows.
200 OK returns the page:

Scheduled pushes

To send later, add an ISO 8601 send_at to POST /api/v1/push/send. When send_at is in the future the notification is stored instead of dispatched and the request returns 202 Accepted; a background worker replays it through the same fan-out and suppression logic when the time arrives. Omit send_at (or pass a past instant) to send immediately.
202 Accepted confirms the push was queued, not sent — read it back or cancel it with the scheduled-push id:
Manage scheduled pushes that have not yet been sent: List the queue with GET /api/v1/push/scheduled — soonest send_at first, capped at 200 rows:
Read one scheduled push by id — including its full payload, attempt count, and any error from a failed dispatch:
An unknown id returns 404 Not Found (NOT_FOUND).

Cancelling a scheduled push

DELETE /api/v1/push/scheduled/{id} cancels a push that is still in the scheduled state. Cancel is a guarded status flip, not a hard delete — the row remains queryable with status: "cancelled".
A successful cancel returns 200 OK with the new status:
Cancellation only succeeds while the push is still pending. If the worker has already dispatched it (sent / failed) or it was cancelled by an earlier request, the endpoint returns 409 Conflict with error code SCHEDULED_PUSH_NOT_CANCELLABLE — the push has already left and can no longer be recalled. Branch on this code rather than treating the cancel as a silent success. An unknown id returns 404 Not Found (NOT_FOUND).

VoIP call devices

A separate registry from the device tokens above, used ONLY to deliver inbound-call pushes that wake a backgrounded or terminated mobile app — Apple PushKit (CallKit) on iOS, a high-priority FCM channel on Android. Registration is handled by the mobile app SDK; the list and revoke endpoints let a backend integration or admin tool audit and manage a user’s registered call devices. Register with the raw PushKit token on iOS, or the call-channel FCM token on Android. Re-registering the same token refreshes it and clears any prior revocation.
201 Created returns the registered device:
The list response includes revoked devices (revoked_at set) alongside active ones, so you can see why a device stopped receiving call pushes — a bad token is marked revoked rather than deleted, and re-registering the same token clears the revocation automatically. Results are capped at 200 rows.
200 OK returns the devices, newest-seen first:
Unregister with the device’s Orbit id:
Success is 204 No Content with an empty body; an unknown id returns 404 Not Found (NOT_FOUND).

See also