The outbound billing gate chain
When a paid send comes back with HTTP 402, the cause lives in one of four stages — and today those stages are described on three separate pages. The wallet, rating, and ledger page covers the balance pre-flight and the authorize-before-use model, the pause gate and free-channel exemption page covers the flags gate, and wallets, credits, and charges covers the atomic check-and-decrement. To reconcile a 402 you assemble the order from all three. This page narrates the whole chain once. The stages run in the same order for every channel, and the order is the point:- Pre-flight balance check — at
$0the reject isINSUFFICIENT_BALANCE(402). - Pause/block flags gate —
SENDING_PAUSED(soft) orSENDING_BLOCKED(hard), 402, with the free-channel exemption. - Per-channel fund path — the voice hold of up to 2 minutes at dial; per-segment SMS debit; per-token AI debit.
- Atomic check-and-decrement — the wallet never overdraws, and idempotent ledger rows collapse retries.
Stage 1 — pre-flight balance check
Every paid channel reads the wallet before it sends. An exhausted wallet rejects withINSUFFICIENT_BALANCE (402). An account-level hold still wins
by construction — a blocked tenant is refused even with money in the
wallet, because the next gate checks those flags too.
Stage 2 — pause/block flags gate
Right after the money check, the send hits two flags on your organization:outbound_paused (soft) and outbound_blocked (hard). Balance-driven
causes — wallet_empty from the balance monitor, or
billing_alert:<id>:<name> from a threshold you set — flip the soft flag;
account-lifecycle causes (suspension, dunning, dispute, admin hold, any
unknown null cause) flip the hard flag. Both reject with 402.
The balance-driven class has one deliberate exception: the $0-on-platform
free channels (WhatsApp, Instagram, Messenger, Telegram, LINE, Apple
Business Messages, push, web widget) are bypassed, because the send costs
the platform nothing and you pay the upstream provider directly. The
account-lifecycle class exempts no channel, and the gate never guesses an
unknown cause into “probably just the balance.” The reason taxonomy is
spelled out on
the pause gate and free-channel exemption.
Stage 3 — per-channel fund path
A send that clears both gates pays in its own unit:- Voice — one authorized hold. At dial, Orbit debits two minutes’ worth up front (minimum 2¢). An empty wallet means no hold, and no hold means the call never dials. At call end, Orbit debits only the delta above the hold (minimum 1¢ per call); an unanswered call refunds the hold in full. The FX rate locked at hold time is reused at settlement, so the two computations never disagree on currency. This is the only hold/reservation in the billing model — every other channel pre-flights without escrowing funds.
- SMS and messaging — per segment. The debit posts when the message is sent, one segment’s worth per billable segment, so a long message debits more than one.
- AI agents — per token. Each model call debits its token usage as it runs.
Stage 4 — atomic check-and-decrement
The wallet check and the decrement happen inside one server-side operation, so concurrent debits can’t race each other into an overdraft. Every movement carries an idempotency key naming the real-world event it pays for (message id, call id, checkout session id). The key caches the result for 24 hours, drives the ledger row’s deterministic identifier, and collapses retries to one row — concurrent attempts with the same key either collapse or get a 409DEDUCT_IN_FLIGHT to back off with.
Insufficient-balance outcomes are never cached, so top-up plus a same-key
retry works cleanly.
Worked examples — one SMS, one call, one AI conversation
Three sends walk the chain, each labelled at the gate that consumed it:- SMS send — clears the balance pre-flight, clears the flags gate,
debits one ledger row per billable segment at send time (
type: debit,reference: charge:sms_mccmnc:msg_…). A retry replays the same key and moves no money twice. - Voice call — clears the same two gates, then escrows up to two minutes before the provider leg is dialed. At hangup the settle computes the true charge and debits only the delta; an unanswered call releases the whole hold. The FX rate locked at hold time is the rate the settle reuses.
- AI-agent conversation — clears the same two gates, then each model call debits its tokens as it runs, per token. A refusal at the wallet mid-conversation surfaces at stage 1 of the next model call, not as a post-hoc debt.
Boundary — what is NOT in this chain
The chain decides whether funds move, and in what order the refusals fire. It deliberately excludes:- Rate resolution — how a destination’s per-unit price is attributed (pricing tables, tier lookup). Lives on pricing and rate resolution.
- Usage-metering rollups and committed-use meters — rollup engines and enterprise monthly-commit drawdown. Lives on committed-use drawdown.
- What-if previews — stateless rate simulations over your usage lanes; nothing persists, nothing gates. Lives on what-if pricing preview.
Integration rules
- Treat 402 as terminal.
INSUFFICIENT_BALANCE,SENDING_PAUSED, andSENDING_BLOCKEDare all 402 — do not hot-retry; resolve the cause. - Poll
GET /api/v1/billing/balance. Readoutbound_paused/outbound_blockedandoutbound_block_reasonuntil the flag flips false, then resume. - A degraded balance read never gates yours. The endpoint can return a
last-known value flagged
degraded: true; send-time checks run server-side regardless, so an exhausted wallet can never send on a stale display value. - Voice FX is locked at hold, reused at settle. The same rate computation runs twice with no currency drift.
Related pages
- Wallets, credits, and charges — the ledger mechanics behind stage 4 (units, idempotency, auto-top-up).
- The outbound pause gate and free-channel exemption — the stage-2 flags taxonomy.
- How billing meters your usage — the rating pipeline and the authorize-before-use summary.