API ReferenceeSIM

Lifecycle

Installation, activation, ICCID lookup, and the one-time security model

An eSIM has two parallel lifecycles:

  1. Order lifecyclePENDINGDELIVERED / FAILED / CANCELLED. Managed by Octopus Cards.
  2. Activation lifecycleNOT_INSTALLEDACTIVEEXPIRED. Managed by the customer's device and the mobile operator.

Order delivery only means an activation code has been issued — the customer still has to install it. This page covers the second lifecycle.

The Activation States

StateWhat it meansWhen it transitions
NOT_INSTALLEDActivation code issued; not yet downloaded to a device.Initial state once the order is DELIVERED.
ACTIVEeSIM profile installed and ready (or actively passing data).When the device downloads the LPA profile and the operator confirms.
EXPIREDValidity window has elapsed.Set automatically once validity_days have passed since first use.

activation_status is read from the order endpoint:

curl "$HOST/api/v1/esim/orders/9302481" \
  -H "Authorization: Bearer $TOKEN" | jq '.data.activation_status'

is_installed is a convenience boolean — true when the device has downloaded the profile, regardless of whether data has flowed yet.

Refresh-on-read Behaviour

GET /api/v1/esim/orders/:id automatically refreshes installation status for orders that are DELIVERED but not yet is_installed. So a single fetch gives you the latest known state — no separate "refresh" endpoint needed.

If you only need to know whether the customer has installed yet, poll the order endpoint at a sensible cadence:

  • Initial install (post-delivery): every 30–60 seconds for the first 10 minutes after issuing the code.
  • After 10 minutes uninstalled: drop to every 5–10 minutes for an hour, then stop. Re-fetch on demand if the customer reports issues.
  • After install: only refresh on demand (e.g. customer claims "no data" — fetch to check activation_status == ACTIVE).

The list endpoint (GET /esim/orders) does not refresh — it returns whatever was last persisted. Use the detail endpoint for live state.

One-Time Install Security

The LPA activation code is a single-use credential. Once the device downloads the profile, the code becomes useless on the operator side and would be a credential-leak if echoed back.

Octopus Cards enforces this in two ways:

  1. At storage: the activation code is encrypted at rest (same envelope as voucher codes). It is decrypted on the way out only.
  2. At read: once is_installed is true, the activation_code field is omitted from GET /esim/orders/:id and never appears in GET /esim/orders. Only the ICCID remains visible.

This means your UI must capture the activation code on first delivery and persist it on your side until the customer has installed. Treat the DELIVERED + NOT_INSTALLED window as the only chance to render the QR code or LPA string.

DELIVERED ─┬─► customer scans QR / pastes LPA

           └─► you fetch with GET /esim/orders/:id


           is_installed: true  ──►  activation_code: <omitted>

The LPA Activation Code

The activation_code field is an LPA Activation Code as defined by GSMA SGP.22 — a string like:

LPA:1$rsp.example.com$ACTIVATION-TOKEN-XYZ

Three parts separated by $:

PartExampleMeaning
Scheme + versionLPA:1Protocol identifier. Always LPA:1.
SM-DP+ addressrsp.example.comThe Subscription Manager Data Preparation server that holds the profile.
Matching IDACTIVATION-TOKEN-XYZThe per-profile token.

Customers redeem this by either:

  • Scanning a QR code that encodes the LPA string. Your UI generates the QR on the client side (qrcode npm package, github.com/skip2/go-qrcode in Go, etc.).
  • Pasting the LPA string into the device's eSIM setup screen (iOS Settings → Cellular → Add eSIM → Enter Activation Code).

Octopus Cards delivers the string; QR encoding happens on your side.

ICCID — the SIM serial

Once delivered, every eSIM has an ICCID (Integrated Circuit Card Identifier) — a 19–20-digit identifier you can use for:

  • Customer-side support ("what's your SIM number?")
  • Logging and reconciliation
  • Linking the order to inbound fulfilment webhooks

The ICCID is visible immediately on DELIVERED orders, persists after install, and does not get masked like the activation code does. ICCIDs are not sensitive credentials.

Refunds and the Lifecycle

Order outcomeWalletResult
DELIVERED + is_installed: trueStays debitedCustomer can use the eSIM.
DELIVERED + is_installed: false after validity expiresStays debitedCustomer never installed; no refund. They miss the window.
FAILEDAuto-refundedNo profile issued.
CANCELLED (admin)RefundedManual cancellation by ops.
FAILED but auto-refund did not completeStays debitedRare; ops reconcile. Surface as "processing" to the customer until resolved.

Once an eSIM is ACTIVE, refunds are not available — the customer has consumed the credential. Treat first install as the point-of-no-return for billing.

Top-ups and Replacement Plans

The current API does not expose plan top-ups or in-place validity extensions. To give a customer more data on the same eSIM, the customer purchases a new eSIM order (which produces a new activation code).

For "renew my plan" UX, render a new order against the same product with the same ICCID surfaced in your UI for the customer's reference. Octopus Cards treats it as an independent order.

Summary

  • Two lifecycles: order (PENDING → terminal) and activation (NOT_INSTALLEDACTIVEEXPIRED).
  • The activation code is single-use: capture it on first delivery, render the QR, and never expect it back from Octopus Cards once installed.
  • GET /esim/orders/:id auto-refreshes installation status on every call.
  • The ICCID is the stable identifier across the lifecycle; the activation code is not.
  • Refunds only apply when the eSIM never made it to ACTIVE.

On this page