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
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
Code
One-call
Code
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
| Field | Rule |
|---|---|
reference | Required. Stable, human-readable id, unique per partner and environment. |
quoteId | Required for the quote shape. Must be an open, unexpired quote owned by the partner. |
currency | Required for the one-call shape. USD, EUR or GBP. |
items | Required for the one-call shape. At least one line, maximum 200 lines and 5,000 units. |
Idempotency-Key | Required 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
Code
| Field | Meaning |
|---|---|
purchaseId | Use this for status, redemption and support. |
status | processing, completed or failed. |
amountMinor | Debited total in minor units. |
currency | Wallet currency used. |
orderIds | Child orders in the purchase. |
balanceAfterMinor | Available 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
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 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
Code
| Format | Returns |
|---|---|
qr | QR SVG only. |
lpa | LPA text only. |
both | LPA 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.
Code
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
| Status | Code | Meaning |
|---|---|---|
| 400 | validation_error | Bad body, missing reference, bad currency or quantity. |
| 402 | insufficient_funds | Wallet balance too low. No order or debit. |
| 403 | account_not_active | Onboarding review incomplete. |
| 403 | forbidden | Key lacks orders:write. |
| 404 | quote_not_found | Quote absent, accepted or not owned. |
| 409 | out_of_stock | No sellable stock. |
| 409 | price_changed | Quote or expected total no longer matches. |
| 409 | quote_expired | Quote older than 15 minutes. |
| 409 | not_redeemable | Purchase not fulfilled or no credentials. |
| 429 | rate_limited / quota_exceeded | Order or redeem limit reached. No order or batch created. |
Related
- Quotes for the pricing step.
- Wallet for the debit source.
- Webhooks for
order.completedandorder.processing. - Approval flow for threshold decisions.