Skip to main content

Troubleshooting: sends rejected with INSUFFICIENT_BALANCE (402)

Orbit is prepaid pay-as-you-go: every outbound message, call minute, and agent invocation debits your wallet, and the wallet is checked before the send leaves the queue. When the check cannot clear, the API rejects the request with a 402 and code INSUFFICIENT_BALANCE — the send never reaches the provider. This page explains how that pre-flight works, what each cause looks like, and how to get the wallet funded (and stay funded) before you re-send. Linear sends (a message POST, a call originate) fail fast in the request. Queued sends (campaigns, scheduled messages) surface the same rejection on the per-message row as a terminal failed status with code INSUFFICIENT_BALANCE.

How the wallet pre-flight works

Before a send is dispatched, Orbit estimates its worst-case cost and places a hold on the wallet for that amount:
  1. Estimate. The per-channel rate for the destination (messaging) or the configured per-minute rate (voice) is resolved, and the worst-case cost is computed. For WhatsApp Calling this is max-minutes × per-minute rate, and the sending surface caps that estimate explicitly.
  2. Hold. The estimated amount is held against the available balance. If the available balance cannot cover the hold, the request is refused with INSUFFICIENT_BALANCE and nothing is dispatched.
  3. Settle. When the send completes (or fails on the wire), the hold is released and the actual cost is debited; the unused estimate remainder returns to the available balance.
Because the check compares the estimate against the available balance (current balance minus outstanding holds), a burst of concurrent sends can push you into a 402 even when the headline balance looks sufficient.

Where to see balance and holds

The dashboard’s Billing section shows the current balance, recent ledger entries, and outstanding holds. The Billing API returns the same values — see the Billing overview for the read endpoints and ledger semantics. If a small set of writes is holding a large share of the wallet, the ledger view is where you find them.

Causes, mapped to fixes

Work the table top to bottom — the first row that matches your setup is the fix.
INSUFFICIENT_CREDITS is deprecated; new integrations should match on INSUFFICIENT_BALANCE. Both appear in the error codes reference, which also lists the fail-closed BALANCE_SERVICE_UNAVAILABLE (503) and the WhatsApp-calling pre-flight variant that can return either 402 or 503.

Top up and re-send

  1. Open Billing in the dashboard and confirm the balance reads below the estimate the send requires.
  2. Choose Top up, pick an amount that covers your expected volume (top-ups are one-off Stripe Checkout payments; credit never expires), and complete checkout.
  3. Wait for the confirmation — the ledger entry posts as soon as the payment clears.
  4. Re-send the rejected requests. For queued sends that failed with INSUFFICIENT_BALANCE, re-issue them from your application; the platform does not auto-replay terminal failures.
Do not blind-retry a 402. Without new funds each retry re-runs the same pre-flight and returns the same rejection — and if the retry loop fans out, every attempt still burns a pre-flight estimate. Gate retries on a fresh balance read, or better, enable auto-top-up so the wallet refills without your loop noticing.

Configure auto-top-up (tenant-controlled)

Auto-top-up refills the wallet automatically so sends stop failing at the pre-flight. It is entirely tenant-configured — you set the trigger, the recharge amount, the payment method, and the ceilings, and no support ticket is required to change any of it.
  1. Open Dashboard → Billing → auto-replenish.
  2. Set the threshold: when the balance drops below this amount, a recharge fires.
  3. Set the recharge amount charged to your saved payment method.
  4. Optionally set spend ceilings (a daily or monthly cap on top-ups) so a runaway integration cannot drain the card — details in Spend caps, budget alerts, and auto-cutoff.
When auto-top-up is on and the payment method is healthy, a low balance triggers a recharge before the next send hits the pre-flight. If the recharge itself fails (declined card), the rejection pattern returns — update the payment method and the trigger fires again on the next low reading.

When to escalate

Open a support ticket when the rejection persists after a funded top-up, or when every rejection is a BALANCE_SERVICE_UNAVAILABLE 503 that outlasts a few minutes of retries. Include:
  • Your tenant ID (dashboard → Settings → Organization; also returned by GET /api/v1/me as organizationId).
  • The exact error code (INSUFFICIENT_BALANCE or BALANCE_SERVICE_UNAVAILABLE) and the request ID of one rejected call.
  • For a payment failure: the checkout/payment reference shown on the Billing page’s ledger entry.
That set lets support pull the wallet ledger, the pre-flight decision, and the payment attempt without a back-and-forth.

See also