Push notifications end to end
This guide walks the full push lifecycle over the API and SDKs: register device tokens, choose a targeting mode, read per-device delivery results, schedule sends, and track delivery and open engagement. Follow it once and push becomes a closed loop — tokens in, notifications out, engagement acks back. For request and response schemas, see the push API reference. This page covers the workflow; the push channel page covers the full field catalog and provider credential setup for APNs, FCM, HMS, and VAPID. Both are linked rather than repeated below.1. Register device tokens
A device becomes reachable once it registers a token against an Orbit user. The SDK does this at first run — wire it before your first send, otherwise every send returnsNO_DEVICE_TOKENS.
Browser (Web Push). Use the OrbitPush surface of the Web SDK. It resolves the VAPID public key, subscribes via pushManager.subscribe, and POSTs the subscription envelope for you:
register() returns null when the user denies notification permission or the browser lacks PushManager (Safari < 16.4) — handle that branch in your onboarding UI. Serve the service-worker bootstrap from your site root so it can claim scope: "/".
iOS / Android / Huawei (native). The device obtains its APNs, FCM, or HMS Push Kit token from the OS push SDK, then registers it with POST /api/v1/push/device-tokens. A plain-HTTP call works for any native client:
platform— one ofios,android,huawei,web. Web clients passsubscription(thePushSubscription.toJSON()envelope) instead oftoken; registers are validated against SSRF so the endpoint must be a public HTTPS URL.app_install_id— a stable per-install identifier. Pass it and a reinstall that rotates the token retires the previous token instead of double-registering.- Tokens scoped to
(org, app, user);user_iddefaults to the authenticated caller when omitted.
app_install_id triggers retirement of the old row. To stop delivery without deleting, toggle PATCH /api/v1/push/device-tokens/:id with { "enabled": false }; to retire permanently, DELETE it.
Test one end-to-end — once your first token lands, fire POST /api/v1/push/test-send to validate the whole credential chain against the calling user’s own devices, with no campaign stats pollution.
2. Choose how to target
POST /api/v1/push/send takes exactly one of three targeting shapes:
Disabled tokens and STOP / channel-
push / channel-all suppression entries are filtered server-side before the fan-out, so a broadcast only reaches deliverable devices.
3. Handle per-device errors
An immediate send returns201 whether it reached one device or one hundred. Read notifications[] per device — a top-level success response does not mean every device accepted:
Top-level request failures are validation errors, not per-device results:
4. Schedule and batch
Supplysend_at (ISO-8601 with offset) on /push/send to hold the payload for a future instant. The request returns 202 with a scheduled-push id; the payload replays through the send path at send time, so the same targeting, caps, and suppression gates run identically.
Manage the queued push with the scheduled surface:
GET /api/v1/push/scheduled— list queue rows (filter bystatus:scheduled,sent,cancelled,failed).GET /api/v1/push/scheduled/:id— read one, including its full payload, attempt count, and last error.DELETE /api/v1/push/scheduled/:id— cancel while it is stillscheduled; returns409once the send has left.
user_ids batches over per-recipient sends — one send request issues one provider interaction. Scheduled sends have automatic back-pressure in the queued row, and the page lists recent results.
5. Track engagement
The two engagement events close the loop once the client SDK is integrated:push.delivered— the device received the render.push.opened— the user tapped the notification.
POST /api/v1/push/notifications/:id/ack with event: "delivered" | "opened" and an optional timestamp. The ack is idempotent — the webhook fires only on the first genuine delivered or opened transition, so duplicate acks do not re-emit.
Subscribe to both events in the webhook event catalog; also read GET /api/v1/push/notifications for the per-notification delivered_at / opened_at timestamps when reconciling analytics.
6. Production checklist
The channel page covers full provider setup (Apple APNs, Web VAPID, Huawei HMS); before launch, verify against the credential group each platform you enable depends on:- Credential groups are set atomically. APNs requires
DEVOTEL_APNS_KEY_ID+DEVOTEL_APNS_TEAM_ID+DEVOTEL_APNS_PRIVATE_KEYtogether; VAPID requiresDEVOTEL_VAPID_PUBLIC_KEY+DEVOTEL_VAPID_PRIVATE_KEY+DEVOTEL_VAPID_SUBJECT; HMS requiresDEVOTEL_HMS_APP_ID+DEVOTEL_HMS_APP_SECRET. A partial group fails every device in that transport with a per-devicefailed. - APNs host is right for the environment. Sandbox APNs tokens only resolve against the sandbox host, and
DEVOTEL_APNS_PRODUCTION=trueonly matters in a non-productionNODE_ENV— pick the matching pair before firing test sends. - VAPID keys rotated only when necessary. A key rotation invalidates every browser subscription; clients must re-subscribe.
- Token rotation and reinstalls handled. Pass
app_install_idat registration so rotated tokens retire their predecessors, and re-register on the SDK’s refresh callback. - Suppression and frequency caps are tested. A
422 NO_DEVICE_TOKENSon broadcast or a device-levelskippedrow is the expected posture once a user opts out. - Engagement wired. Install the service worker (or native ack hooks) so
push.delivered/push.openedreach your analytics loop, and subscribe to the webhook events above.