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.