Lifecycle
Installation, activation, ICCID lookup, and the one-time security model
An eSIM has two parallel lifecycles:
- Order lifecycle —
PENDING→DELIVERED/FAILED/CANCELLED. Managed by Octopus Cards. - Activation lifecycle —
NOT_INSTALLED→ACTIVE→EXPIRED. 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
| State | What it means | When it transitions |
|---|---|---|
NOT_INSTALLED | Activation code issued; not yet downloaded to a device. | Initial state once the order is DELIVERED. |
ACTIVE | eSIM profile installed and ready (or actively passing data). | When the device downloads the LPA profile and the operator confirms. |
EXPIRED | Validity 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:
- At storage: the activation code is encrypted at rest (same envelope as voucher codes). It is decrypted on the way out only.
- At read: once
is_installedistrue, theactivation_codefield is omitted fromGET /esim/orders/:idand never appears inGET /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-XYZThree parts separated by $:
| Part | Example | Meaning |
|---|---|---|
| Scheme + version | LPA:1 | Protocol identifier. Always LPA:1. |
| SM-DP+ address | rsp.example.com | The Subscription Manager Data Preparation server that holds the profile. |
| Matching ID | ACTIVATION-TOKEN-XYZ | The 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 (
qrcodenpm package,github.com/skip2/go-qrcodein 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 outcome | Wallet | Result |
|---|---|---|
DELIVERED + is_installed: true | Stays debited | Customer can use the eSIM. |
DELIVERED + is_installed: false after validity expires | Stays debited | Customer never installed; no refund. They miss the window. |
FAILED | Auto-refunded | No profile issued. |
CANCELLED (admin) | Refunded | Manual cancellation by ops. |
FAILED but auto-refund did not complete | Stays debited | Rare; 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_INSTALLED→ACTIVE→EXPIRED). - 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/:idauto-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.