Skip to main content

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 on GET /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.
When a purchase reverses — the end user’s plan failed to provision, the order was cancelled — the external service files a refund through the same internal door and the reversal lands as a credit row that references the original order:
  • 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.
Reconciliation stays one-order-one-pair: every purchase order ends in either one debit row (sold) or a debit + credit pair (reversed), and the org balance never moves without a row, so the Ledger reconciliation story — replay the ledger and the balance reconciles — holds for this lane too.

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.
You never see a refund request with an amount of its choosing to credit — the door carries no amount by design, because Orbit re-reads the real debit every time.

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.