The outbound pause gate and the free-channel exemption
Every send pre-flights two flags on your organization:outboundPaused (soft
pause) and outboundBlocked (hard block). When either is set, GET /api/v1/billing/balance names the cause in outboundBlockReason, and the send
rejects with SENDING_PAUSED or SENDING_BLOCKED (402). One question decides
what happens next: is the hold trying to protect the wallet, or to hold the
account? Balance-driven pauses exempt free channels; account-lifecycle holds
exempt no channel. A null reason fails safe and is treated as a hold.
Two classes of holds
Balance-driven — “your wallet can’t pay for this send”
wallet_empty— auto-pause-on-zero. The balance monitor flipsoutbound_paused = trueas your balance approaches zero.billing_alert:<id>:<name>— a spend or balance threshold you set under Dashboard → Billing → Alerts. When the threshold trips, outbound pauses and the reason carries the alert’s id and display name.
Account-lifecycle — “the account may not send at all”
subscription_suspended/parent_subscription_suspended— the account (or, under a reseller, the parent account) is suspended.dunning_failed_3x/dunning— repeated failed payments.payment_dispute_active— a card dispute or chargeback is open.null/ unknown reason — an admin or fraud-console suspend writes the hold without a reason label. Absent a recognizable label, the gate fails safe and keeps blocking every channel.
Why the distinction matters — the free-channel exemption
The BYO-credential messaging channels are **0 platform price falls out of the default rate card). A balance-driven pause therefore must not refuse a free channel. Blocking WhatsApp at an empty wallet was a defect: the send costs the platform nothing and you pay Meta anyway. Send-time, an exempt balance-pause on a free channel is skipped — the bypass is logged and counted so the exemption stays observable.What’s exempted and what is not
- Balance-driven pauses (
wallet_empty,billing_alert:…) — bypass for $0-price channels. SMS, email, and every other paid channel reject. - Account-lifecycle holds (suspension, dunning, dispute, admin/fraud
suspend, any
nullor unknown reason) — rejected for every channel, free or paid, and that includes NULL: the gate never guesses an unknown cause into “probably just the balance.” - Reseller per-subaccount spend cap (
subaccount_spend_cap:…) — not exempted. Right after this gate, an authoritative per-send cap re-check runs for reseller child accounts and does not exempt free channels; honoring it here (then blocking at the cap) would be one inconsistent exception. The cap stays enforced at its own gate for every channel.
Recovery while the pause persists
Balance on a free channel? Your send proceeds — only the paid channel pauses:- Resend on a free channel. A
wallet_emptyor billing-alert pause with WhatsApp (or any $0 channel) still goes through. - Resolve or poll. For paid channels, treat 402 as terminal, top up or
trim your offending billing alert under Dashboard → Billing, then poll
GET /api/v1/billing/balanceuntiloutboundPausedflipsfalse. Resumed issued credits clear the cached flag immediately instead of waiting out the 5-minute balance-sync tick. - Fix the cause for lifecycle holds. Suspension and dunning recover by updating the payment method or contacting support — not by topping up.
Posture map — where each reason is written
Paid channels you’re billed for (SMS, MMS, RCS, email, voice, fax, Viber)
follow the same gate — the exemption applies only to the $0 list above.
Read the full billing pipeline on
Billing and wallet, the ledger mechanics on
Wallets, credits, and charges, and
the person-level operations in Billing overview and the
Billing API reference.