> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshoot INSUFFICIENT_BALANCE (402)

> One resolution tree for every 402 wallet rejection: immediate 402 on send and verify, the pre-renewal low-balance freeze on a held number, and renewal-failure recovery with auto-release reclaim. Top up, resend, and keep the wallet funded.

## Sprachhinweis

Wenn keine Übersetzung verfügbar ist, wird der englische Inhalt als Fallback angezeigt. Fehlercodes, API-Pfade und Codeblöcke bleiben unverändert.

# Troubleshoot `INSUFFICIENT_BALANCE` (402)

Orbit is prepaid pay-as-you-go: every outbound message, Verify OTP, call minute, and
held-number rental debits your wallet. When a charge can't clear, the wallet rejects it
with `402` and code `INSUFFICIENT_BALANCE` — nothing is dispatched and nothing is billed.

Because the same wallet backs sends, Verify, and number renewals, this page is one
resolution tree. Find the surface your 402 came from below, work that branch, and the
wallet fix at the end applies to all three.

<Note>
  `INSUFFICIENT_CREDITS` is the deprecated alias; match on `INSUFFICIENT_BALANCE` in new
  integrations and treat both as the same 402 condition. The full list is in the
  [error codes reference](/reference/error-codes).
</Note>

## Branch A — immediate 402 on send or verify

A send (`POST /messages`) or a Verify OTP (`POST /verify/send`) fails fast in the request
when the wallet pre-flight can't cover it. Queued sends (campaigns, scheduled messages)
surface the same rejection per-message as a terminal `failed` status with the same code.

The pre-flight estimates the worst-case cost and **holds** it against your available
balance before anything is dispatched:

1. **Estimate.** The per-channel rate for the destination (messaging), the per-OTP rate
   (Verify), or the per-minute rate (voice) is resolved to a worst-case `cost_cents`.
2. **Hold.** That estimate is held against available balance — your `balance_cents` minus
   any outstanding holds. If available balance can't cover the hold, the request is
   refused with `INSUFFICIENT_BALANCE` and never reaches the provider.
3. **Settle.** When the send completes (or fails on the wire), the hold releases and the
   actual cost is debited; the unused remainder returns to available balance.

Because the check compares against **available** balance, a burst of concurrent sends can
push you into a 402 even when the headline balance looks sufficient.

### Same pre-flight on Verify

A Verify 402 is not channel-specific. `POST /verify/send` deducts the first-channel OTP
cost through the same wallet and the same `INSUFFICIENT_BALANCE` code before any OTP
leaves — so the wallet fix below is identical whether your 402 came from SMS, WhatsApp,
voice, or Verify. Don't treat a Verify 402 as a Verify-channel problem.

### Diagnose the cause

| Cause | How to recognize it | Fix |
| - | - | - |
| Wallet empty or below the estimate | `balance_cents` reads at or near zero in Billing | Top up, then resend |
| Auto-top-up disabled | Wallet hit zero with no recharge event in the ledger | Enable auto-top-up (end of page) |
| Auto-top-up payment failed | A recharge attempt exists but the card was declined | Update the payment method in Billing, then re-enable or top up manually |
| `503 BALANCE_SERVICE_UNAVAILABLE` | Code is `BALANCE_SERVICE_UNAVAILABLE`, not `INSUFFICIENT_BALANCE` | Transient balance-read blip — retry with backoff; do not top up |
| Worst-case estimate exceeds balance | Sends to expensive destinations or long `max-minutes` calls reject while small sends succeed | Lower the estimate surface (shorter max call minutes) or fund to cover the worst case |
| Legacy integration matches the old code | Your client matches on `INSUFFICIENT_CREDITS` | Match on `INSUFFICIENT_BALANCE`; treat both as the same 402 |

### Fix and resend

1. Open **Billing** and confirm the balance reads below the estimate the send requires.
2. Choose **Top up**, pick an amount that covers your expected volume (one-off Stripe
   Checkout; credit never expires), and complete checkout.
3. Wait for the ledger entry — it posts as soon as the payment clears.
4. Resend the rejected requests. Queued sends that failed terminally are re-issued from
   your application; the platform does not auto-replay them.

<Warning>
  Don't blind-retry a 402. Without new funds each retry re-runs the same pre-flight and
  returns the same rejection. Gate retries on a fresh balance read, or enable auto-top-up
  so the wallet refills on its own.
</Warning>

## Branch B — pre-renewal low-balance freeze on a held number

A held number's monthly rental is charged from the same wallet each cycle. When your
balance can't cover an **upcoming** renewal, Orbit freezes the renewal before it's
attempted rather than letting the number lapse silently:

* Owner and admin users get an email ("add funds before your number renews") and an
  in-app `number_renewal` warning in the Notification Center, both linking to wallet
  top-up.
* The email is throttled and the in-app alert fires at most once per billing cycle, so a
  low balance doesn't pile up daily reminders.

Resolution is the wallet top-up at the end of this page: fund the wallet to cover the
monthly rental, and the renewal clears on its own. See
[number lifecycle](/numbers/lifecycle) for the renewal-reminder mechanics.

## Branch C — renewal failed: auto-release and reclaim

If the renewal charge is **attempted** with too little balance, it fails with a
`number_renewal_failed` notification. The number stays active and Orbit re-attempts the
charge once a day — once you top up, the next attempt renews automatically with no manual
step.

If the renewal stays unpaid past its grace window, the number auto-releases. Recovery
from there:

* `POST /api/v1/numbers/:id/retry-release` — re-issue a failed carrier release without
  re-purchasing.
* `POST /api/v1/numbers/:id/reclaim` — re-claim the parked number during its grace window
  without a fresh purchase (`409` if it isn't reclaimable — fall back to a new purchase).

Both endpoints and the renewal lifecycle are documented under
[number lifecycle](/numbers/lifecycle). The
[Notification Center](/guides/in-dashboard-notifications) lists what raises a
`number_renewal_failed` row.

## Keep the wallet funded (applies to all three branches)

Auto-top-up refills the wallet automatically so the pre-flight stops rejecting. It is
entirely tenant-configured — you set the trigger, recharge amount, payment method, and
ceilings; no support ticket required.

1. Dashboard → **Billing → auto-replenish**.
2. Set the **threshold**: when balance drops below this, a recharge fires.
3. Set the **recharge amount** charged to your saved payment method.
4. Optionally set spend ceilings (a daily or monthly cap) so a runaway integration can't
   drain the card — see [spend caps](/billing/spend-caps).

When auto-top-up is on and the payment method is healthy, a low balance recharges before
the next send, verification, or renewal hits the pre-flight.

## 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 from `GET /api/v1/me` as
  `organizationId`).
* The **exact error code** and the **request ID** of one rejected call.
* For a payment failure: the **checkout/payment reference** from the Billing ledger.

Besides `INSUFFICIENT_BALANCE`, the billing gate also stops outbound with
customer-configured pause/block flags and provider/hold faults —
[billing-gate outbound blocks](/troubleshooting/billing-pause-block-recovery)
covers every stop beyond the 402.

## See also

* [Billing recovery codes (parent runbook)](/troubleshooting/billing-recovery-codes) —
  the full generic-402 ladder: payment failure (`PAYMENT_REQUIRED`), hard block
  (`INSUFFICIENT_FUNDS`), auto-top-up recovery codes
* [Billing-gate outbound blocks](/troubleshooting/billing-pause-block-recovery) —
  pause/block flags, provider faults, voice hold faults, trial-credit gates
* [Billing overview](/billing/overview) — wallet model, ledger, top-ups, invoices
* [Spend caps](/billing/spend-caps) — ceilings and enforcement for top-up and spend
* [Number lifecycle](/numbers/lifecycle) — auto-renew, low-balance warnings, release, reclaim
* [Notification Center](/guides/in-dashboard-notifications) — `number_renewal_failed` and
  other billing alerts
* [Error codes reference](/reference/error-codes) — `INSUFFICIENT_BALANCE`, deprecated
  `INSUFFICIENT_CREDITS`, `BALANCE_SERVICE_UNAVAILABLE`


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.