Skip to main content

Troubleshoot number purchase failures

Two response codes cover the purchase-step failures that aren’t rejected up front by the wallet, compliance, or sender-resolution gates:
  • 409 NUMBER_NO_LONGER_AVAILABLE — the DID you picked could not be claimed. Either another account bought it between your inventory search and checkout, or the response flags your own recently-released number still inside the carrier quarantine window.
  • 502 NUMBER_PROVISIONING_FAILED — the carrier order succeeded and the wallet was charged, but the platform-side step that registers the DID for inbound routing failed. Orbit rolls the purchase back: the charge is refunded to your wallet and the DID is released back to the carrier in the background.
Both are retryable, and both tell you nothing was left half-billed. Work the branch that matches your code, then the checklist below.
Checkout rejections that stop before a carrier order — 402 INSUFFICIENT_BALANCE, a 422 compliance-profile gate, or a sender-resolution conflict — are covered in the Buy and provision numbers guide and the insufficient-balance runbook. This page covers failures that happen at or after the carrier order.

Cause table

A bulk purchase surfaces the same codes per row in the failed[] array instead of failing the request: inspect each row’s error.code, and remember the batch returns 200 even when every row failed — gate on succeeded.length, not the status code.

Decision checklist

  1. Re-run the preflight. GET /api/v1/numbers/available returns live inventory — a row you saw minutes ago is not a reservation. Re-search, then forward the row’s id into the purchase call so checkout targets a still-listed DID. A 409 on a fresh search result means the race repeated; pick another row.
  2. Retry only the failed bulk rows. Failed rows in failed[] are never charged — debited_cents counts only succeeded — so re-submitting exactly the failed E.164s in a new POST /numbers/buy-bulk call is billing-safe. Do not replay the whole batch.
  3. Capture the request id. If the failure recurs, keep meta.request_id from the error response (not a wildcard pattern — the exact req_… value). Wildcard or partial ids are rejected when you query logs or open a ticket; the full string is the handle.
  4. Recheck the ledger on a 502. After NUMBER_PROVISIONING_FAILED, the Billing ledger shows the refund of the rolled-back charge before you retry, so the next purchase attempt draws against restored balance. If a 502 follows a cheaper “already charged” assumption, your retry is safe either way — the response only ever debits a succeeded purchase once.

What not to do

  • Do not blind-duplicate the checkout request. Re-issuing the identical purchase body against a stale search result re-runs the same losing race. Re-search first, then resubmit. For bulk purchases, replaying the whole batch re-attempts rows that already succeeded — always strip succeeded items first.
  • Do not treat a 409 as “try the same number harder”. The number is gone. Retrying the exact E.164 without a fresh search hit just repeats the rejection.
  • Do not double-buy after a 502. A NUMBER_PROVISIONING_FAILED response has already refunded the debit — you are not paying twice by retrying, but you are not owed the number either; the compensating release sends it back to the carrier.

When to escalate

Open a support ticket when the same DID keeps failing across a fresh search and a clean retry, or when a 502 recurs on more than one purchase attempt within a few minutes — that pattern points at a platform-side provisioning step that needs operator attention, not a client-side fix. Include:
  • The exact meta.request_id of one failed call (single and bulk responses both carry one; bulk failures also have a pollable order_id — bring that too).
  • The E.164 and country of the number, and the error code you received.
  • For recently_released_by_self 409s, whether you expect the ~30-day quarantine to have expired already.

See also