Push broadcasts: setup and pre-send caps
A push broadcast is a wildcard send —user_ids: ["*"] on
POST /api/v1/push/send — that fans one notification out to every opted-in
device in your tenant. Run this checklist once, before the first wildcard
send, so the two pre-send gates (audience size, per-minute rate) never trip
on a live campaign.
Each step below is a tenant-owned control you configure once; nothing on
this page is platform-internal.
1. Provider credentials
Orbit holds your provider credentials and fans a send out to the right transport from the singlePOST /api/v1/push/send endpoint. Configure each
provider you need — a send fans out per-device, so an iOS-only tenant needs
only APNs, and unconfigured providers return per-device status: "failed"
entries rather than a top-level error.
Bring-your-own credentials cover all four providers in the same channel,
and the per-send pricing page still shows a flat platform fee — no per-push
provider charge on the unified send. The detailed table per provider, with
the exact variable names and where to source each value, lives on the
Push channel reference (sections Apple Push setup,
Google / FCM, Web Push setup, and Huawei Push setup).
Rotate only when a key is compromised. Rotating APNs / VAPID / HMS
credentials invalidates every existing subscription — end users must
re-subscribe. FCM access tokens rotate automatically (GKE metadata-server
tokens refresh on a 1-hour TTL); no manual action is needed on GKE.
POST /api/v1/push/test-send:
it fires one fixed test notification to your own registered devices and
returns a per-token verdict per provider — never a broadcast, and never
counted as a tracked send.
2. Payload shape per provider
One request shape covers every provider; the per-platform override objects fine-tune a single transport without forking your send code.- APNs (iOS) —
title(≤ 500) +body(≤ 4000) are required;badge,thread_id,interruption_level,relevance_score, andmutable_contentmap straight onto the APNs payload; keep the whole payload under the 4 KB provider cap. - FCM (Android) —
channel_idpicks the Android notification channel,priorityisnormalorhigh, andttl_secondsbounds how long FCM stores an offline device. - Web Push (VAPID) —
topicmust match^[A-Za-z0-9_-]{1,32}$, andactionsis capped at 2 per RFC 8294; atopicwith disallowed characters is rejected with HTTP 422 before the provider is ever called.
user_ids list for the wildcard ["*"] and the same
shape becomes a broadcast — one request that fans out to every
deliverable device in your tenant:
notifications[] verdict row
per targeted device — a failed or skipped row never fails the
request:
send_at on the same payload and the response flips to
202 with the scheduled push id; the scheduler dispatches it at that
time.
3. Broadcast caps (pre-send gates)
Two gates refuse a broadcast before dispatch — neither reaches the provider, no wallet hold is taken, and both are deterministic (retrying the identical shape computes the same refusal). Read theerror.details
envelope to tell which gate fired.
BROADCAST_TOO_LARGE (422)
The audience gate refuses the whole wildcard before any device is
targeted; error.message carries the deliverable-device count it
counted:
CHANNEL_RATE_LIMITED (429)
The velocity gate names its channel in error.details so a push
refusal is never confused with an SMS or email one. The response also
carries the standard rate-limit headers — back off for Retry-After
seconds before retrying:
Raising the push envelope
Read your current overrides, the cluster defaults, and the sanity ceiling in one call:push envelope lives under the same endpoint as every outgoing
channel (owner-only to write). PUT replaces the whole overrides
map — send every channel you want active or the omitted ones revert
to the cluster default on the next send:
PUT echoes the same { overrides, defaults, channels, max_per_minute } shape as the GET, so you can confirm the write
landed without a second read.
Fix the gate at its own surface — split a 422 into explicit user_ids
batches, or throttle / raise the push override for a 429. The full
refusal-decoding runbook, with the details envelope shapes and the
recovery sequence, is the
push broadcast pre-send gates
page.
4. Device-token lifecycle and pruning
Device tokens are the audience a broadcast counts; keep them current or the audience gate over- or under-counts.-
Register once per (org, app, user) — the mobile SDK (
@devotel/orbit-mobile) or web SDK (@devotel/orbit-web) callsPOST /api/v1/push/device-tokenswith the device’s APNs / FCM / VAPID subscription. Re-registering the same token is an idempotent upsert — it refresheslast_seen_atand never duplicates the row.The 201 echoes the stored row, including theidyou use forPATCH/DELETEbelow:Repeat the samePOSTafter an OS token refresh or on every app start — the upsert returns the sameid, it never inserts twice. For web push, send"platform": "web"with the browserPushSubscription.toJSON()envelope undersubscriptioninstead oftoken. - Rotate automatically — the SDK hands a refreshed platform token to the same endpoint; the platform resolves the rotation server-side.
-
Prune dead tokens — a token the provider reports as permanently gone
(APNs
410, FCMUNREGISTERED) is removed automatically on the delivery path, so the next send skips it. Disable a noisy device without deleting it viaPATCH /push/device-tokens/:id(enabled: false), or delete outright viaDELETE /push/device-tokens/:id.The 200 returns the updated row with its newenabledflag; a disabled token stays on file but every send path skips it, and the audience gate no longer counts it.DELETEon the same URL drops the row entirely with a 204 and no body.
5. Delivery receipts
A send returns 201 immediately with a per-device verdict array; durability lives in the delivery log, not the send response.-
Per-device verdicts — each entry in
notifications[]carriesstatus(sent/failed/skipped),delivered_at,opened_at, and any providererror.totalandsentaggregate the page. -
Delivery log — page through
GET /api/v1/push/notificationswith acursorto reconcile a broadcast after the fact. The log is newest-first and capped at 200 rows per page;cursoris thecreated_atof the last row you already hold, so page forward until a page returns fewer rows than yourlimit.Each row carries the per-devicestatus,delivered_at/opened_atreceipts, and theerrorstring when the provider refused that device — the row-level trail behind thenotifications[]verdicts the send itself returned. -
Webhooks — when the SDK is integrated,
push.deliveredandpush.openedfire as webhook events so your pipeline gets receipts without polling.
6. When to prefer push vs SMS or the inbox
Push is the right surface for your installed base (no carrier cost,
per-device receipts, capability flags like badge / sound / mutable-content).
SMS still wins for reach across phone numbers, and the inbox wins when the
user needs to reply.
See also
- Push channel — the full send/reference surface, per-platform field tables, and the common-errors table.
- Push broadcast pre-send gates — decode
BROADCAST_TOO_LARGEandCHANNEL_RATE_LIMITED. - Push token expiry — per-device provider verdicts and token pruning.
- Per-channel rate overrides — the tenant-owned
pushper-minute envelope. - Push notification categories — curated APNs / FCM category registry.