Integration guide

Purchase and deliver an eSIM

Ordering is a single synchronous call. It returns only when eSIMs exist, or when the order has failed and your balance has been put back.

The call

node
const res = await fetch("https://app.journeystack.co/api/public/v1/orders", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.JS_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": booking.reference,
  },
  body: JSON.stringify({
    plan_id: plan.id,
    quantity: 1,
    days: plan.validity.billed_per_day ? booking.nights : undefined,
    customer: { name: traveller.name, email: traveller.email, reference: booking.reference },
  }),
});

const payload = await res.json();
if (!res.ok) throw new OrderError(payload.error.code, payload.error.message);
await saveEsims(booking.id, payload.data.esims);

What happens inside

  • We validate the body and confirm the plan is still active.
  • We price the order at your tier in your currency and record it as pending.
  • We debit your prepaid balance. Not enough funds → 402 insufficient_funds and nothing else happens.
  • We allocate the eSIM profiles from our network inventory.
  • Success → the order is completed and the response carries the eSIMs. Failure → a full refund, order marked failed, 502 provider_error.

Issuance is immediate, not asynchronous: there is no polling step and no pending state you need to handle after a 2xx. Typical end-to-end time is a few seconds; allow a 60 second client timeout.

Quantity and days

  • quantity is 1–50 and issues that many independent profiles on one order.
  • days is 1–365 and only valid on plans where validity.billed_per_day is true. Sending days on a fixed plan is rejected with invalid_request.
  • price.unit is already multiplied by days; price.total is unit × quantity and is exactly what leaves your balance.

Delivering

Each eSIM comes with `qr_code_url` (an image you can embed or attach), `activation_code` (the LPA string, which is all a device actually needs) and `smdp_address` for manual entry. Store the eSIM ID against your booking so you can re-fetch and resend at any point rather than ordering again.

Never re-order to resend

A resend is `GET /esims/{id}`. Placing a second order issues a second eSIM and charges you again — the idempotency key is what protects you when you genuinely do not know whether the first attempt landed.