Push device tokens and scheduled sends
Reading order matters here. Push integration is the umbrella: it takes you through the API loop from credential setup to engagement acks — register tokens, send, read per-device results, wirepush.delivered / push.opened. This page is the data plane that loop runs against — the two registries it owns:
- the device-token registry (
/push/device-tokens— plus/push/voip-devicesfor call-push tokens), and - the scheduled-send queue (
/push/scheduled) that asend_aton/push/sendenqueues into.
1. Token transports: alert, web, and VoIP
One registration call, three transports.POST /api/v1/push/device-tokens upserts on the token string — re-registrations refresh, they never duplicate:
422 INVALID_PUSH_ENDPOINT.
VoIP tokens are a separate registry. A call-push token (iOS PushKit, or Android’s high-priority call channel) goes to POST /api/v1/push/voip-devices, not the device-token registry. Keeping the two apart prevents a marketing send from ever addressing a VoIP entitlement:
2. The registry’s guarantees
- Idempotent upsert. Registration is keyed on the token string: re-registering the same token refreshes
last_seen_atand upserts the mutable fields. Register on every SDK token-refresh callback; it is safe to be noisy. - Install-lifecycle dedup. Pass a stable
app_install_idand a reinstall that rotates the OS token retires the install’s previous row — one live token per install instead of a growing pile. - Provider-feedback retirement. FCM / APNs / Web Push report dead tokens back at send time (
Unregistered,BadDeviceToken,410 Gone). Those tokens are deleted from the registry on the send that discovers them — no manual sweep for the common decay case. - Pause vs revoke.
PATCH /api/v1/push/device-tokens/:idwith{ "enabled": false }pauses delivery while keeping the row;DELETEretires it permanently. VoIP devices useDELETE /api/v1/push/voip-devices/:id. - Fail-closed gating. Every send resolves only rows in the registry — unknown device ids do not send, and disabled or suppressed tokens are filtered before the fan-out. The registry is the target resolution, not a hint.
3. The scheduled-send contract
A futuresend_at (ISO-8601 with offset) on POST /api/v1/push/send persists the full payload into the scheduled queue and returns 202 with the scheduled push id. Past-or-now values fall through to an immediate send — clock skew never silently eats a push. Scheduled sends are one-off; model recurring sends by enqueueing each occurrence from your own cadence (a campaign or journey step).
The dispatcher claims due rows about once a minute and replays the stored payload through the same send path an immediate send takes — targeting, suppression, and frequency caps run identically. Two contract points to build against:
Cancel-before-dispatch. Cancel a row with DELETE /api/v1/push/scheduled/:id while it is still scheduled. The cancel is a guarded status change, not a hard delete — once the dispatcher has moved the row to sent or failed, the cancel returns 409 SCHEDULED_PUSH_NOT_CANCELLABLE instead of silently succeeding. Race-safe by design: if your cancel and the dispatcher cross, exactly one wins and you get an honest answer either way.
Failure is observable, not perpetual. A failed dispatch retries with exponential backoff (2^attempts minutes, capped); after five attempts the row lands at failed with last_error recorded. Read the row before retrying — a bad target means fix the payload and enqueue a new send; a transient blip means cancel and re-enqueue if it no longer applies.
Target the broadcast or a user group the same way you would on an immediate send: user_ids: ["*"] for the whole opted-in registry (capped at 100,000 deliverable tokens — prune first, section 5), or an explicit user_ids batch. Scheduling does not change the targeting semantics, only when they resolve.
4. Worked example: register, batch a schedule, read results
Register the tokens — say one Android alert device and one browser:5. Operating the registry
Reads and management writes:
Writes require the owner, admin, or developer role — the same gate the dashboard’s Device Tokens and Scheduled pages apply.
Prune before big sends: broadcasts resolve only enabled, unsuppressed tokens up to a 100,000-device cap. Disabling tokens from churned installs — and letting provider feedback delete the dead ones — keeps your headroom real and protects deliverability.
Everything above is the data plane the push integration umbrella loops over: credential setup at the channel layer, tokens registered into this registry, sends resolved and scheduled against it, engagement acks closing the loop. When you are deciding which page to follow — the umbrella to integrate, this page to operate — that split is the whole answer.