Bitcall logoBitcall API
Products

eSIM

Provision and manage eSIM profiles through the gateway.

The eSIM product lets you browse a catalog of data plans, buy them, and read back the resulting eSIM profiles — with their activation material — through the same gateway you already use.

All eSIM endpoints are /v1/esim/* requests authenticated with your key id and secret — see Authentication.

Responses, pagination, errors and retry behaviour follow the shared Conventions and Errors rules — this page covers only what is specific to eSIM.

Every endpoint, with its full request and response schema, is in the eSIM API Reference.

A typical flow

Browse the catalog

Plans are returned at your selling prices. Browse them directly, or start from countries and regions:

await api('GET', '/v1/esim/plans', { query: { region: 'Global', currency: 'USD' } });
await api('GET', '/v1/esim/countries');

Each plan's id (24‑hex) is the planId you buy with.

Buy a plan

await api('POST', '/v1/esim/orders', {
  body: {
    planId: 'pln_9OOXVUPmNmI_MPiVZuWLcO7Q-id5EJsV1fae-tZihg',
    email: '[email protected]',
  },
});

planId is the reference from the catalog, sent back exactly as given — the previous example here showed a bare database id, which the API refuses. email is the delivery address and is required; without it the call fails 400 VALIDATION_ERROR with fields: ["body.email"].

Funds are reserved and captured, and the call returns 202 with an orderId. Once the order reaches delivered it carries the profiles it produced, each with the iccid that addresses it — and the activation credential, read in the next step.

Each call to this endpoint buys a plan. If it times out or fails without a clear answer, do not resend it — check GET /v1/esim/orders first to see whether an order was created, and only then decide whether to try again.

Read the activation credential

Poll the order until it is delivered, then read the credential from the profile:

const order = await api('GET', `/v1/esim/orders/${orderId}`);
const { iccid } = order.data.esims[0];
const { data } = await api('GET', `/v1/esim/profiles/${iccid}`);
const { activationCode, appleInstallUrl } = data;
// activationCode → "LPA:1$…"  — install this, or render it as a QR code
// appleInstallUrl → installs the profile in one tap on iOS

A profile is addressed by its ICCID. That is the value in the URL above, and it is published on the order's esims[] and on every row of GET /v1/esim/profiles. There is no separate profile id to look up.

The credential is a bearer credential. Treat activationCode as one: it installs the eSIM, and anyone holding it can. Every response carrying it is sent Cache-Control: no-store, private, so no cache or proxy retains it, and it is never written to a log. A client that lost one can read it again from the profile.

We do not return a qrCodeUrl — no image is hosted for you. The activationCode is everything a QR encodes, so generate the image client-side from that string if you need one.

Errors follow the standard gateway error format. The codes each endpoint can return are listed on that endpoint in the API Reference.

Order statuses

StatusMeaningWhat to do
pendingAccepted, not yet being provisioned.Poll.
processingBeing provisioned.Poll.
deliveredEvery eSIM is ready; the order's esims[] carry the ICCID and activation code.Install.
partially_deliveredSome eSIMs were delivered, some failed.Check the esims array.
failedNothing was delivered.You were not charged for undelivered items. Retry or contact support.
refund_pendingA return of the charge is under review.Wait for the decision.
refundedThe charge was returned.Nothing.
cancelledThe order was cancelled before it was provisioned.Nothing. You were not charged.
requires_reviewThe order needs a human.Contact support with the orderId.

When things go wrong

You seeMeaningDo
400 VALIDATION_ERRORA field is wrong; fields says which.Fix it.
409 PRODUCT_UNAVAILABLEThe plan cannot be sold right now.Choose another, or retry later.
409 INSUFFICIENT_BALANCENot enough balance.Top up, then send the purchase again. Nothing was charged, so nothing is duplicated.
409 CONFLICTThe order is not in a state that allows this — already cancelled, for instance.Check actions on the order first.
404 NOT_FOUNDNo such order or eSIM — or not yours.Check the id.

actions.canCancel already accounts for our eligibility rules, so read it rather than guessing from the status.

Full schemas are in the eSIM API Reference.

On this page