The two-phase authorize lifecycle
Most wallet charges on Orbit are synchronous — the platform rates the usage and debits it in one shot, and the ledger appends one row. That path fits any flow whose price is knowable before the money moves: a message send, a top-up, a per-minute voice settle. A second class of flow does not fit that shape: the number purchase flow, a KYC-gated order, a bulk capacity reservation, a conference event. In those flows the platform quotes a figure, then hands the order to a third-party leg that can succeed, fail, or sit in review for days. Pricing is exactly once — the flow either settles at the quoted figure or abandons it — and the danger is letting that flow tie up the wallet incorrectly in one of two ways:- Charge at the end — the wallet race. While the third-party leg runs, other concurrent flows keep spending from the same balance; by settlement time the wallet may no longer cover the original quote. The quote said yes, the third-party leg succeeded, and the settle debit fails — an instance of the delivered-but-unbilled class.
- Charge at the start — the abandoned-order class. The wallet debits at the moment of authorization, but nothing gives the funds back when the order fails or times out: the balance sits locked for weeks until someone turns the operation into a manual refund request.
State machine: open, authorized, settled, released
Any multi-leg flow names its own hold id (for example the order id on a number purchase) and walks the same machine:- open → authorized — when the flow begins, the authorize phase places a solvency-checked hold for the quoted figure. Succeeds only if the wallet covered it at authorize time; a wallet below the quote refuses the whole flow before the third-party leg is ever dialed.
- authorized → settled — when the third-party leg completes, the capture phase validates the final settled figure against the authorized figure. An attempt to settle for more than the authorized figure fails — if the carrier’s final price came back higher mid-flight, the flow releases this hold and re-authorizes rather than settling past the quote.
- authorized → released — when the flow abandons (a compliance review that never closes, an order you cancel, a retry that established the reservation was no longer needed), the release phase credits back the authorized figure exactly once. This is the humane path that prevents the abandoned-hold money leak from lingering for months.
Idempotency and the charge-key contract
Every authorize debit is keyed on the hold id with ahold:<holdId>
idempotency key, and the release phase re-derives its own one-shot credit
key from the same hold id. Two consequences matter for an
integration:
- Retry-safe authorize. If a network blip drops the authorize response but the debit went through, retrying with the same hold id collapses to the cached result and does not move the wallet again — the same retry semantics the idempotency and safe retries concept page guarantees for any balance mutation. Replayed authorize responses are flagged so your own code does not re-fire side effects (order creation, webhook emission, downstream leg submission) on a deduplicated authorize.
- Exactly-once release. The releasing compensation also derives its idempotency key from the hold id, so a concurrently retried release collapses to one credit. Both the hold’s original debit and the release credit stay in the ledger — finance reconciliation sees both, not a silent refund.
hold:<holdId> key collision-free across
flows.
Where this applies: multi-leg flows
Any flow whose funds must be locked before a long-running third-party leg completes is a candidate:- Number (DID) purchase — a hold is placed at order time so the exact inventory quote is locked while the carrier leg runs.
- KYC order intake — an order with a compliance review step locks the amount while the review is in flight.
- Bulk reservation — a capacity reservation holds the quoted figure while a bulk scan decides which members to commit.
- Conference billing — a provisional authorize against a quoted ceiling while the event is live.
How it differs from the synchronous charge
The synchronous debit that rates and charges a usage event in one call — the default debits described on wallets, credits, and charges — is correct whenever the platform can compute the price before money moves. The two-phase authorize is the alternative for the class of flow that cannot:
If a flow has no long third-party leg — a message send, a per-min voice
settle, an AI token charge — the synchronous path is the right tool and
cheaper to administer. The two-phase authorize primitive exists
precisely for the handful of flows where the quote escapes synchronous
settlement.
Release: what an abandoned hold does
Two distinct failure shapes exist on the money path — the over-reserve-forever leak (charge up-front on an abandoned flow) and the under-reserve retroactive race (charge at settle time). The release phase exists to close the first; the authorize phase exists to close the second. What happens on the abandon path matters for ops:- A hold that abandons stays open on the wallet until the release phase runs. There is no automatic timeout sweeper — the flow owns the decision, and nothing about the platform guesses “the flow probably finished by now” into auto-compensation. If the third-party leg completed, the settle capture may still validate and clear the hold correctly even long after authorize.
- The release credit compensates the original authorize debit exactly once, even under concurrent abandoned-order retries, so the wallet ends neither locked nor double-credited.
Related pages
- Wallets, credits, and charges — the ledger and units both charge types post into.
- How billing meters your usage — the rating pipeline that selects between a synchronous charge and a two-phase authorize.
- Idempotency and safe retries — the 24-hour replay window both phases ride on.
- Buying numbers — the DID purchase flow walk that exercises authorize, capture, and release end to end.