Troubleshoot push broadcast pre-send gates
BROADCAST_TOO_LARGE (422) and CHANNEL_RATE_LIMITED (429) are deterministic pre-send refusals, not provider failures: your broadcast never reaches APNs / FCM / HMS / Web Push, so nothing is dispatched and no wallet hold is taken. Both gates fire on the whole-org broadcast surface — user_ids: ["*"] on POST /api/v1/push/send, or channel: "push" on the unified POST /api/v1/messages pipeline — and both are tenant-owned controls you can deliberately size.
This page answers: which gate fired, where the ceiling is sized, how to read the cause from the details envelope, and the one re-send that verifies the fix.
A push wildcard send is
user_ids: ["*"], not device_token_ids. The audience cap (this page) counts the deliverable audience — enabled device tokens minus push/all suppression opt-outs — not the raw row count. Provider-side per-device verdicts (APNs 410 / FCM UNREGISTERED, token expiry) return 201 and grade per device in notifications[]; decode those on
Push token expiry.The two gates
Distinguish the envelope a refusal came from:
- Dedicated push route (
POST /api/v1/push/sendwithuser_ids: ["*"]) → onlyBROADCAST_TOO_LARGE(audience gate) applies pre-flight. Neither gate fires per-device; per-devicesent/skipped/failedrows sit in thenotifications[]array of a201. - Unified pipeline (
POST /api/v1/messageswithchannel: "push") → every per-channel fraud gate (rate cap → spend cap → blocklist) runs before the dedicated send executes;CHANNEL_RATE_LIMITEDsurfaces here.
POST /api/v1/messages fan-out) can also trip the spend-cap codes (SMS_DAILY_SPEND_CAP / CHANNEL_DAILY_SPEND_CAP / VOICE_DAILY_SPEND_CAP) on the same request — the per-channel ops ceilings. Those are covered on
Spend-cap refusals; this page covers the rate and audience caps only.
Cause table
Read theerror.details envelope from the refusal to scope which lever to pull.
Fix table
Fix the gate at its own surface, then re-send once.What NOT to try
- Retrying the identical payload.
BROADCAST_TOO_LARGEnever clears itself: the audience gate is a pure function of your token registry — re-running the same wildcard computes the same refusal and burns per-minute rate headroom on the whole API surface. Retry only after the audience is under the cap. - Splitting a wildcard into multiple wildcard sends. Two
user_ids: ["*"]sends target the same 100,001+ devices and both refuse. Only explicituser_ids(or suppression narrowing) reduces the projected count. - Looping a 429 retry. The rate gate is a hard per-minute window — a tight retry loop just spends your own envelope inside the window. Back off ≥ the window (60s) before the first retry after a raise.
- Assuming the refusal is a provider fault. Both codes are platform pre-flights; the provider was never called. If you saw
201with per-devicefailedentries, this page is the wrong runbook — decode per-device verdicts on Push token expiry.
Surface the cause
Read the refused response’serror.details envelope — the ERROR_CODES member for both codes declares the same diagnostic shape:
BROADCAST_TOO_LARGE (422)
message already counts the deliverable audience; the request_id is the handle to cite on escalation.
CHANNEL_RATE_LIMITED (429)
details.channel scopes the lever: push here — raise push, never sms. The window: "1m" and limit values tell you what the sliding window evaluated.
Recovery sequence
-
Read the refusal envelope — confirm the exact
codeanddetails.channelbefore touching anything. -
Fix the gate.
BROADCAST_TOO_LARGE: re-send to explicituser_ids—
CHANNEL_RATE_LIMITEDon push: either throttle the burst and re-send ≥60s later, or raise thepushoverride (owner-only) —
Remember: PUT replaces the wholeoverridesmap — send every channel you want active, or omitted channels revert to the cluster default instantly. - Re-send once. One clean request after the fix proves the gate re-evaluated; do not parallel-fan the re-send.
-
Verify. For the rate gate,
GET /api/v1/settings/compliance/channel-rate-overridesmust show yourpushoverride alongside thedefaultsentry. For the audience gate, the same payload now returns201with per-device results innotifications[].
When to escalate
Escalate only when the refusal persists after the fix on the surface the envelope names. Include:- Your tenant ID (
GET /api/v1/me→organizationId). - The full error envelope —
request_id,code,message, anddetailsexactly as returned. - For
BROADCAST_TOO_LARGE: thedeliverable device countthe message reported and the suppression scope in use. - For
CHANNEL_RATE_LIMITED: the currentpushoverride value (GET /api/v1/settings/compliance/channel-rate-overrides) and the observed send rate.
See also
- Push channel — the broadcast surface, its common-errors table, and the per-device verdict model.
- Per-channel rate overrides — the tenant-owned envelope the
CHANNEL_RATE_LIMITEDgate reads. - Fraud caps — fraud floors, blocks, and the channel velocity model.
- Spend-cap refusals — the daily-envelope codes a wildcard send can also trip (
SMS_DAILY_SPEND_CAP,CHANNEL_DAILY_SPEND_CAP,VOICE_DAILY_SPEND_CAP). - Push token expiry — per-device provider verdicts inside a
201. - Error codes reference — the canonical list of every enforcement code.