Documentation

Orders

An order is the first state-changing commerce step. It rechecks the quote, price and stock, debits the wallet once and creates a purchase with child orders. The whole request is atomic: a failure creates no purchase and no debit.

Code
POST /v1/orders Idempotency-Key: order-2026-001

Required scope: orders:write. The account must have completed review (onboardingStatus: active); otherwise the API returns 403 account_not_active.

Two request shapes

Quote-based

JSONCode
{ "reference": "booking-123", "quoteId": "22222222-2222-4222-8222-222222222222" }

One-call

JSONCode
{ "reference": "booking-124", "currency": "USD", "expectedTotalMinor": 1618, "items": [ { "offerId": "11111111-1111-4111-8111-111111111111", "quantity": 2 } ] }

The one-call shape creates an internal quote, compares expectedTotalMinor with the fresh total and then purchases. It does not skip the quote; it hides the extra round trip. If the total differs, the API returns 409 price_changed with the new totalMinor and the fresh quote, and does not charge.

expectedTotalMinor is optional. Use it for any autonomous caller so a price move cannot silently exceed the mandate.

Rules

FieldRule
referenceRequired. Stable, human-readable id, unique per partner and environment.
quoteIdRequired for the quote shape. Must be an open, unexpired quote owned by the partner.
currencyRequired for the one-call shape. USD, EUR or GBP.
itemsRequired for the one-call shape. At least one line, maximum 200 lines and 5,000 units.
Idempotency-KeyRequired header. Unique per logical order. Reuse it for retries.

A successful order consumes the quote and changes its status to accepted.

Reference and idempotency are different

reference is your business identifier. Idempotency-Key is the transport replay key. Keep both stable for the same logical order. Do not reuse a reference for a different purchase, and do not generate a new idempotency key to "retry" an uncertain order.

Response

JSONCode
{ "purchaseId": "33333333-3333-4333-8333-333333333333", "status": "processing", "amountMinor": 1618, "currency": "USD", "orderIds": ["44444444-4444-4444-8444-444444444444"], "balanceAfterMinor": 8382 }
FieldMeaning
purchaseIdUse this for status, redemption and support.
statusprocessing, completed or failed.
amountMinorDebited total in minor units.
currencyWallet currency used.
orderIdsChild orders in the purchase.
balanceAfterMinorAvailable balance after the debit. On an idempotent replay no new debit occurs, so this can be 0.

A processing status means at least one unit is awaiting upstream fulfilment. The wallet is already debited. Poll GET /v1/orders/{purchaseId} until the status is terminal.

Status model

Code
processing ──> completed │ └────────> failed completed all child orders fulfilled processing at least one child order is paid or awaiting fulfilment failed at least one child order is fulfillment_failed or replaced partially_fulfilled target contract, not live cancelled target contract, no cancel endpoint live

Child order statuses are paid, fulfilled, fulfillment_failed, cancelled and refunded. A purchase status is derived from its children. partially_fulfilled is part of the target contract but is not returned by the current service.

Read orders

Code
GET /v1/orders GET /v1/orders/{purchaseId}

GET /v1/orders returns the latest 50 purchase operations for the partner. GET /v1/orders/{purchaseId} returns the purchase and its child orders. Both require orders:read and share the order-read limit.

The response currently includes a child order store field for internal compatibility. Do not display, store or rely on it. The public contract is that upstream store and product identifiers are not exposed; treat any internal field as non-contractual and report it if it appears in a customer-facing surface. This documentation does not reproduce its values.

Delivery timing

GET /v1/delivery measures completed deliveries over the last 30 days. The clock starts at payment, or at order creation for wallet orders, and stops when the eSIM is issued and attached to a fulfilled order. The response reports sampleSize, medianMs, p90Ms, under10sPercent, fastestMs and slowestMs. A small sample stays a small sample; do not turn it into a public claim without enough volume.

GET /v1/orders/{purchaseId} also returns a delivery object with status, startedAt, completedAt and durationMs for that purchase.

Redemption

Code
POST /v1/orders/{purchaseId}/redeem Idempotency-Key: redeem-2026-001
JSONCode
{ "format": "both" }
FormatReturns
qrQR SVG only.
lpaLPA text only.
bothLPA text and QR SVG. Default.

Maximum 20 eSIMs per batch. A retry with the same idempotency key returns the same fixed batch, not the next 20. The response contains batchId, format, items (orderItemId, esimCodeId, iccid, lpa) and qrSvgs when requested.

JSONCode
{ "batchId": "55555555-5555-4555-8555-555555555555", "format": "both", "items": [ { "orderItemId": "66666666-6666-4666-8666-666666666666", "esimCodeId": "77777777-7777-4777-8777-777777777777", "iccid": "8985...REDACTED", "lpa": "LPA:1$REDACTED" } ], "qrSvgs": ["<svg>REDACTED</svg>"] }

GET /v1/orders/{purchaseId}/redeem is Planned. Use the POST form.

Redemption output is bearer secret

LPA text and QR SVG can install and use the eSIM. Deliver them only into the partner's entitled secure channel. Do not log, cache, email, forward or show them to a model. Every reveal is audited.

Payment failures

The current service returns 402 insufficient_funds when available wallet credit is below the total. The target contract adds 402 payment_required with a funding_url and an approval_required state for orders above a threshold. Neither new shape is live in every deployment yet. Handle both if present, and never ask an agent for card details.

Errors

StatusCodeMeaning
400validation_errorBad body, missing reference, bad currency or quantity.
402insufficient_fundsWallet balance too low. No order or debit.
403account_not_activeOnboarding review incomplete.
403forbiddenKey lacks orders:write.
404quote_not_foundQuote absent, accepted or not owned.
409out_of_stockNo sellable stock.
409price_changedQuote or expected total no longer matches.
409quote_expiredQuote older than 15 minutes.
409not_redeemablePurchase not fulfilled or no credentials.
429rate_limited / quota_exceededOrder or redeem limit reached. No order or batch created.