Get StartedYour first order

First eSIM order

Buy a country eSIM and receive an LPA activation code + ICCID

eSIMs are mobile data plans delivered as an LPA Activation Code — a string the customer's device parses (typically by scanning a QR code rendered on the client side). The order endpoint is hybrid sync/async: it always returns 200 OK, waiting up to 60 seconds for fulfilment. Branch on the status field — DELIVERED (activation code inline), PENDING (needs polling), or FAILED. Never branch on HTTP status.

Three requests after authentication: list country plans, place the order, (optionally) poll if pending.

This page assumes HOST and TOKEN are exported as described on the authentication step.

1. Find a country plan

eSIM products are organised per country/region. country_code is ISO 3166-1 alpha-3 (three-letter uppercase, e.g. JPN, USA, IND), matching the topup vertical.

curl "$HOST/api/v1/esim/products?country_code=JPN" \
  -H "Authorization: Bearer $TOKEN"

Then fetch variants for the chosen product:

curl "$HOST/api/v1/esim/products/712/variants" \
  -H "Authorization: Bearer $TOKEN"

Each variant has amount, data_amount_gb, and validity_days. The walkthrough uses Japan 1 GB / 7 days at $4.50.

Echo the variant's amount exactly

(product_id, amount) is how Octopus Cards resolves to a variant. Don't compute the amount client-side — pass back the exact amount from the variant response.

2. Place the order

curl -X POST "$HOST/api/v1/esim/orders" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": 712,
    "amount": 4.50,
    "quantity": 1,
    "client_reference": "FIRST_ESIM_001"
  }'

The HTTP response is always 200 OK. Branch on the status field — three outcomes to handle:

statusWhat to do
DELIVEREDBest case. activation_code (LPA string) and iccid are in the response. Render the QR / LPA immediately.
FAILEDRejected. Surface failure_reason. Wallet auto-refunded.
PENDINGFulfilment took longer than the 60s inline deadline. Move to step 3 — poll.

Successful response (truncated):

{
  "id": 9302481,
  "client_reference": "FIRST_ESIM_001",
  "status": "DELIVERED",
  "product_name": "Japan eSIM",
  "country_code": "JPN",
  "data_amount_gb": 1.0,
  "validity_days": 7,
  "amount": 4.50,
  "currency": "USD",
  "activation_code": "LPA:1$rsp.example.com$ACTIVATION-TOKEN-XYZ",
  "iccid": "8910300000123456789",
  "activation_status": "NOT_INSTALLED"
}

Capture the activation code on first delivery

Once the customer installs the eSIM, subsequent fetches of GET /esim/orders/:id omit the activation_code field — it's a single-use credential and Octopus Cards burns it on install. If the UI ever needs to re-show the QR, persist the code at the point of delivery.

3. (Optional) Poll if pending

When the create response returned status: "PENDING", poll the detail endpoint until terminal.

ORDER_ID=9302482
deadline=$((SECONDS + 300))   # 5 minutes
while [ $SECONDS -lt $deadline ]; do
  resp=$(curl -s "$HOST/api/v1/esim/orders/$ORDER_ID" \
    -H "Authorization: Bearer $TOKEN")
  status=$(echo "$resp" | jq -r '.status')
  case "$status" in
    DELIVERED|FAILED|CANCELLED) echo "Terminal: $status"; break ;;
  esac
  sleep 10
done

What just happened

  1. Listed country eSIM products and their data-plan variants.
  2. Placed an order with (product_id, amount). Quantity is always 1 — eSIM orders are single-eSIM.
  3. Either received the LPA activation code inline (status: "DELIVERED") or polled the detail endpoint until terminal (status: "PENDING"DELIVERED).

The customer's device parses the LPA string — scanning a QR is the universal path; iOS 17.4+ also accepts an apple-system-settings://cellular?addCellularPlan=… deep link.

Next

After completing this flow, the others are short cousins of the same pattern. Then:

On this page