eSIM reseller billing model
The travel-eSIM reseller lane sells consumer travel eSIM inventory — prepaid data plans an end user installs by scanning a QR code — out of a standalone eSIM service that Orbit runs as an external partner system. Every purchase in that lane debits your org’s prepaid wallet, and every reversal credits it back along the same wallet rails. That is a deliberately different billing driver from the one the Connectivity SIM model documents for IoT/M2M data-only lines, and this page gives you the mental model the connectivity page does not: where the money moves, what the ledger rows look like, and what disappears (and what must not disappear) when the external service has a bad day. For the mechanics of the ledger itself — append-only rows, micro-cent accounting, paused-send flags — start at Wallets, credits, and charges. This page is the reseller lane’s overlay on that model.Why this lane is its own billing model
Connectivity SIMs and travel eSIMs are different inventory with different billing drivers:
The lane is architected so that the external service never holds the
wallet. Orbit’s spendable balance lives behind one atomic wallet
implementation, and a service that moved money by writing the database
directly would leave the customer’s balance untouched while corrupting the
ledger. So the eSIM service can’t touch the wallet at all: it buys and
refunds through two internal service-to-service endpoints on the Orbit API,
and the Orbit wallet implementation does the move.
This is the same split the public SDK sees: you buy travel eSIMs from the
reseller surface; the charges arrive in your wallet from the internal door.
Neither you nor the service can spend wallet balance without one of those two
doors.
The wallet ledger rows a purchase produces
A travel-eSIM purchase lands as a single debit row on the org ledger, visible onGET /api/v1/billing/transactions and in the Billing dashboard
alongside your SMS / voice / AI debits:
- The reference names the order —
charge:esim_purchase:<order-id>for a first purchase,charge:esim_topup:<order-id>for a plan top-up — so finance can split first-purchase revenue from top-up revenue and a single order always points at a single row. - The amount is the tenant-facing price of the order, with sub-cent prices preserved precisely in Orbit’s micro-cent accounting (the ledger tracks millionths of a cent; see Wallets, credits, and charges). Fractional-cent prices — a per-MB rate, a promotional rounding — are billed at their true value and the sub-cent remainder banks rather than rounding up on every purchase.
- The credit’s amount equals exactly what left the wallet for that order — Orbit re-derives it from its own ledger instead of trusting the caller, and the value matches even when sub-cent arithmetic split the original charge between the whole-cent balance and the carry remainder.
- A repeated refund request for the same order is a short-circuited replay: it reports the original refund amount and touches no money. The reversal lands exactly once, and the ledger shows it landed.
The two internal doors, at a tenant level
The reseller lane’s two doors are internal endpoints — they authenticate with a signed service-to-service token, not your developer API key, so you call them only through the eSIM reseller surface, not directly. Their shape is worth understanding because it defines what your ledger can contain:- Charge. The service sends the order id it is selling, the order kind
(first purchase or top-up), and the price. The wallet’s idempotency key is
derived from
(kind, order-id)— deterministically, on the server — so a retried request for one order charges once. A per-call amount ceiling refuses a price that exceeds Orbit’s configured maximum before any wallet call happens, and every accepted charge appends an audit entry naming the order. A charge below one cent is refused at the door: Orbit will only accept a charge it can later refund, and a sub-cent-moved-nothing debit would leave no ledger row for a refund to compensate. - Refund. The service sends the same
(kind, order-id)pair plus a free-text reason (for example, that provisioning failed). Orbit looks the charge up in its own ledger, credits exactly that amount back, and one-shot semantics on two independent layers — the durable ledger row and the wallet claim — make a retried refund report its own earlier result. A refund is refused rather than credited blindly when no charge row exists for the order — inventing money on an unproven charge is the one failure the lane never commits.
Failure-mode expectations
The lane runs debit then provision then compensate — a purchase debits the wallet first, the external service provisions the SIM, and a non-success refunds. Orbit has no reservation primitive on this path, so the honest recovery when an order fails mid-flight is the compensating refund, and both sides treat it as the only reversal.- Inventory vs money can only diverge for an operation, never for a direction. If the external service times out or is unreachable after the wallet moved, the refund door closes the money gap: the order compensates and the credit lands. The refund refusing “no charge on record” is what stops the worse divergence — a credit for an order the wallet never moved.
- A drained wallet refuses the purchase, not the refund. Wallet
insufficiency (
INSUFFICIENT_BALANCE, HTTP 402) is rejected before any debit, so a purchase either debits or doesn’t happen — it never half-opens. - A retried identical request collapses. The deterministic idempotency
key makes re-sending the same charge or refund safe; a
DEDUCT_IN_FLIGHT(409) on a retry means the identical charge is still in flight, so the service retries it with a short backoff instead of starting over. - Every move is auditable. Both doors append a per-call audit entry naming the order id and kind, so a purchase, a refund, or a refused credit always leaves a trail to reconcile against — you can audit the lane end to end from the ledger.
What this is not
- Not your public API. The charge/refund pair is internal — the signed token scopes it to the external service, so your developer key cannot reach it and your ledger rows arrive through the reseller surface, not by calling these endpoints yourself.
- Not IoT plan billing. Travel-eSIM orders own this lane; a SIM provisioning pipeline that bills usage on a managed ICCID belongs to the Connectivity SIM model.
- Not a runbook. The reseller surface’s buy/refund UI and this lane’s service internals live elsewhere; this page is the concept those assume.
Cross-links
- Wallets, credits, and charges — the ledger model both lanes’ rows live on (units, idempotency, degradation).
- Connectivity SIM model — the plan+metered-data billing driver this lane deliberately differs from.
- Provisioning and test mode — how Orbit separates test traffic from production traffic.
- Ledger reconciliation — how finance replays this lane’s purchase/refund pairs.