Wallets, credits, and charges: how usage metering works
Orbit meters usage against a prepaid wallet: one balance per organization, one append-only ledger, and one pause gate on outbound sending. Use this page to model that correctly in your integration — which unit to do arithmetic in, why a wallet debit is sub-cent precise, and what a 402 pause response means for your retry logic. The surface map of every billing endpoint is the Billing overview; the request/response schemas are the Billing API reference. This page is the concept both of those assume.The prepaid ledger — one balance, append-only history
Every organization holds a single wallet. The wallet has a balance and a ledger:- The balance is the spendable remainder, read live from
GET /api/v1/billing/balance. - The ledger is the append-only history of every movement that produced
that balance, read from
GET /api/v1/billing/transactionswith cursor pagination. Rows are never updated or deleted — a reversal is a new row of the opposite sign, so you can replay the ledger to reconcile the balance against your own records.
150), while the charge ledger tracks sub-cent precision in
micro-cents (1 cent = 1,000,000 micro-cents; 1 USD = 100,000,000
micro-cents). That headroom exists because real usage rates are sub-cent — a
per-email or per-LLM-token rate doesn’t divide cleanly into whole cents.
Charges accumulate in integer micro-cents so a million tiny debits sum
exactly; a 2-decimal float would drift.
Credit paths vs. debits at usage time
A ledger row is a credit (positiveamount_minor) or a debit
(negative), tagged with a type and a human-meaningful reference:
- Top-up credit — a card checkout or crypto payment settles:
- Other credits — refunds and trial/admin grants land
(
type=refund,admin_grant, …) as positive rows the same way. - Usage debit — every send, call, or AI request debits the wallet as it
happens, with the
referencenaming what was charged:
Pause semantics — when outbound sending stops
Outbound sending (messages, calls) is gated by the org’s pause state. Read it onGET /api/v1/billing/balance:
outbound_paused: true— sends are currently refused.outbound_block_reason— the machine-readable cause when set (wallet_emptyat a $0 balance,billing_alert:<id>:<name>when a billing alert’s pause action fired,dunning_failed_3xafter repeated payment failures). Harder hold classes — a suspended subscription (yours or an ancestor reseller’s) or an active payment dispute — flip the outcome toSENDING_BLOCKED, same HTTP 402. The send-time checks fire in order: first the balance pre-flight (INSUFFICIENT_BALANCEon an empty wallet), then the flags gate (SENDING_PAUSED/SENDING_BLOCKED), then the fund-consuming charges (the voice hold, or the post-send ledger debit). An account-level block beats a balance check by construction: a blocked tenant is refused even with money in the wallet.payment_failure_count— consecutive card payment failures (0–3). At 1 or 2, dunning is in progress and the dashboard warns the owner to update the card; at 3 the account is frozen until it’s fixed.
outbound_paused flips to false, then resume. The dashboard alert tells the
owner the same thing.
Units — dollars, cents, and micro-cents
GET /api/v1/billing/balance returns the same balance in three
representations, plus the ledger’s ISO 4217 currency:
Conversion:
micro_cents = cents × 1,000,000 = usd × 100,000,000. If you
project your own remaining runway, subtract your per-unit rates from
balance_micro_cents in integer math — a float projection will disagree with
the ledger on small amounts.
Degraded balance — the last-known fallback
The balance read has a cache-first fast path. During a short availability blip the endpoint can return the last-known value instead of an error, flagged on the response:degraded: true appears, treat the number as stale by at most the
in-flight charges of the last few seconds — useful for display, not for
gating your own sends. The dashboard shows a “Balance recently degraded”
notice rather than letting the widget flash a phantom zero. The send-time
pause gate is evaluated server-side at send time regardless of this flag, so a
degraded read can never let a genuinely-exhausted wallet send.
Auto-top-up feeds the same ledger
Auto-top-up keeps the wallet funded without a human watching it: when the balance crosses your configured threshold, Orbit charges the saved payment method off-session and credits the wallet.- An evaluator runs on a 15-minute schedule and compares the balance to your
threshold_minor. - A 5-minute cooldown separates consecutive auto-charges.
- No more than one auto-charge per hour fires even after the cooldown clears, and a per-day cap (default 5) bounds runaway exposure.
credit_purchase row against the same balance. Configure it via
GET/PUT/DELETE /api/v1/billing/auto-topup or Dashboard → Billing →
auto-replenish; the full field semantics are on the
Billing overview.
Authorize-then-debit — and the one authorized hold
The pipeline is check balance first, then debit as usage is rated — usage is authorized before it runs, not debited post-hoc, and the ledger has no negative or “pending settlement” state. The charge itself is atomic: the check and the decrement happen inside one server-side operation, so concurrent debits can’t race each other into an overdraft. Per channel, the same pattern shows up twice:- Every send and call goes through a wallet pre-flight. A send on a
sub-cent channel reads the balance up front and fails with
INSUFFICIENT_BALANCE(HTTP 402) when it can’t pay. Voice goes further: it needs a funded hold before the provider leg is even dialed (below). - Voice holds up to 2 minutes at dial time. This is the only hold/reservation in the billing model. When you place a call, Orbit resolves the destination’s per-minute rate and debits two minutes’ worth up front (minimum 2¢). An empty wallet means no hold, and no hold means the call never dials. When the call ends, Orbit computes the true charge from the call record — per-minute rate, one-minute billing increments, minimum 1¢ per call — debits only the difference above what the hold already took, and refunds the hold in full when the call never answered. The FX rate locked at hold time is reused at settlement, so the two computations can never disagree on currency. For rated channels other than voice there is no reservation — the send pre-flight checks the balance but never escrow funds against it.
INSUFFICIENT_BALANCE; it never posts a partial or negative row. Because
the wallet is prepaid, that refusal is the whole enforcement model — there
is no post-hoc debt to collect.
How the idempotency key collapses retries
The “debits are idempotent” guarantee above has a concrete mechanism. Every balance mutation can carry an idempotency key — a caller-supplied token that names the actual-world event being paid for (a message id, a call id, a checkout session id). The key plays two roles:- Result cache. Before a debit runs, the key is claimed atomically;
the completed result is then stored under it for 24 hours. A retry with
the same key returns the stored result without moving money again, and
two concurrent calls with the same key either collapse to one debit or
the second is refused with
DEDUCT_IN_FLIGHT(409) — safe to retry with the same key after a short backoff. AnINSUFFICIENT_BALANCEoutcome is never cached: it clears the key, so topping up and retrying the same key with funds works. - Ledger reference. The same key is stamped onto the
credit_transactionsrow and drives its deterministic identifier — the same(organization, idempotency key)pair always resolves to the same ledger row, so retries collapse to one row instead of double-billing. Replayed balance responses carryidempotentReplay: trueso callers don’t re-fire side effects (webhooks, notifications, audit entries) on a deduplicated retry.
Next steps
- The outbound billing gate chain — the ordered chain (pre-flight → flags → fund path → atomic decrement) every send walks, narrated once.
- Spend alerts, burn rate, and the velocity-anomaly model — the threshold-alert, burn-rate, and anomaly-detection instruments layered on top of this ledger and its pause gate.
- Billing overview — endpoint surface map (top-ups, alerts, invoices, statements, refunds).
- Billing API reference — request/response schemas for every endpoint named here.
- How billing meters your usage — the rating pipeline behind each debit (included quantity, graduated tiers, FX on converted top-ups), and the authorize-before-use model of this ledger.