# Setup and installation instructions

> Retrieve the QR code, the APN and the installation instructions for a booking or an ICCID.

Source: https://fx-port.com/docs/esims/instructions

Two endpoints return what a traveler needs to install an eSIM: the **setup data of an order** (the QR code, the
activation data and the installation steps, with the prices of the booking) and the **installation steps of an
ICCID** in the language of your choice. Both contain the activation data of the eSIM: show it only to the
traveler who owns the eSIM.

## Setup instructions by booking

`GET /api/v1/esims/bookings/{booking_id}/setup-instructions`

**Permission:** Read

Returns the original order of an eSIM booking as JSON: the QR code and the activation data, the APN settings, the
steps to install by QR code or by hand (as HTML), and the agency-facing prices. Use it to build your own
confirmation or setup page.

`booking_id` is the reference of an **order**: the `booking_id` of the order response or of the
[bookings list](https://fx-port.com/docs/esims/bookings.md) (`FX-` followed by a number).

- A **topup** booking has no setup data: `404` with the message "Setup data not available for this booking".
- A booking that does not exist and a booking of **another agency** are answered the same way: `404 booking_not_found`.
- A reference that is not valid (more than 100 characters, or characters other than letters, digits and `._:=+~-`)
  is refused with `400 invalid_booking_id`.


**cURL**

~~~bash
curl --request GET \
  --url https://api.fx-port.com/api/v1/esims/bookings/FX-1793546602916/setup-instructions \
  --header 'Authorization: Bearer fxp_test_YOUR_KEY'
~~~

**Node.js**

~~~js
const response = await fetch("https://api.fx-port.com/api/v1/esims/bookings/FX-1793546602916/setup-instructions", {
  headers: { Authorization: "Bearer fxp_test_YOUR_KEY" },
});
const { data: order } = await response.json();
const sim = order.sims[0]; // sim.qrcode_url, sim.sharing.link, sim.direct_apple_installation_url ...
~~~


### Response example

A sandbox order (the HTML and the QR link shortened with `...`):

~~~json
{
  "environment": "sandbox",
  "success": true,
  "data": {
    "code": "20261101-106852",
    "order_id": "20261101-106852",
    "currency": "DZD",
    "package_id": "france-in-7days-1gb",
    "quantity": 1,
    "type": "sim",
    "description": "Order for John Smith",
    "esim_type": "local",
    "validity": 7,
    "package": "KPN France Data only-1 GB - 7 days",
    "data": "1 GB",
    "price": 468.84,
    "created_at": "2026-11-01 15:23:22",
    "manual_installation": "<p><b>eSIM name:</b> ...</p><ol><li>Settings > Cellular/Mobile > Add Cellular/Mobile Plan.</li>...</ol>",
    "qrcode_installation": "<p><b>eSIM name:</b> ...</p>...",
    "installation_guides": { "en": "https://www.airalo.com/help/getting-started-with-airalo" },
    "text": null,
    "voice": null,
    "sims": [
      {
        "created_at": "2026-11-01 15:23:22",
        "iccid": "8900000540463077957",
        "lpa": "lpa.airalo.com",
        "imsis": null,
        "qrcode": "LPA:1$lpa.airalo.com$DUMMY-2610301358-iKFxr-44741",
        "qrcode_url": "https://sandbox.airalo.com/qr?expires=1879860202&id=473564&signature=...",
        "apn_type": "automatic",
        "apn_value": "N/A",
        "is_roaming": true,
        "confirmation_code": null,
        "apn": {
          "ios": { "apn_type": "automatic", "apn_value": "N/A" },
          "android": { "apn_type": "automatic", "apn_value": "N/A" }
        },
        "msisdn": null,
        "direct_apple_installation_url": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$lpa.airalo.com$DUMMY-2610301358-iKFxr-44741",
        "direct_android_installation_url": "https://esimsetup.android.com/esim_qrcode_provisioning?carddata=LPA:1$lpa.airalo.com$DUMMY-2610301358-iKFxr-44741",
        "sharing": { "access_code": 8803, "link": "https://esims.cloud/v8hy7-usgkq6wg" }
      }
    ],
    "total_price": 468.84,
    "retail_price": 468.84,
    "agency_currency": "DZD"
  }
}
~~~

| Field | Meaning |
|---|---|
| `code` | The supplier's order code. `order_id` carries the same value |
| `price`, `total_price`, `retail_price` | What the traveler pays, in your agency currency (`currency` and `agency_currency`). The three fields have the same value. `price` here is the retail price, see [eSIM pricing](https://fx-port.com/docs/esims/pricing.md) |
| `package_id`, `package`, `data`, `validity`, `esim_type` | The package that was ordered: id, name, allowance, validity in days and kind |
| `quantity` | Always `1` |
| `manual_installation`, `qrcode_installation` | HTML with the steps to install by entering the activation data, and by scanning the QR code. Show it as HTML, or strip the tags |
| `installation_guides` | Links to the supplier's general guide, by language |
| `sims[]` | The eSIM of the order: `iccid`, the activation data (`lpa`, `qrcode`, `qrcode_url`), the install links (`direct_apple_installation_url`, `direct_android_installation_url`), the APN settings (`apn_type`, `apn_value`, `apn` per platform) and `sharing`, a page where the traveler can open the eSIM (`link` and `access_code`). Keys without a value are `null` |
| `created_at` | When the supplier created the order: `YYYY-MM-DD HH:MM:SS`, UTC, without a zone |

## Installation instructions by ICCID

`GET /api/v1/esims/instructions/{iccid}`

**Permission:** Read

Structured, step-by-step installation instructions for one ICCID, per operating system, in the language of the
`locale` parameter. The ICCID is 18 to 22 digits (it may be written in groups of spaces or hyphens, `8944 4654 0000 0123 456`)
and must belong to an eSIM **your agency ordered** in this
environment; any other ICCID is answered `404 iccid_not_found`.

~~~bash
curl --request GET \
  --url 'https://api.fx-port.com/api/v1/esims/instructions/8900000540463077957?locale=en' \
  --header 'Authorization: Bearer fxp_test_YOUR_KEY'
~~~

| Query parameter | Description |
|---|---|
| `locale` | Language of the steps: `en`, `fr`, `ar`, `de`, `es`, `it`, `ja`, `ko`, `pl`, `pt`, `ro`, `ru`, `sq`, `tr`, `vi` or `zh`. Any spelling of a locale is read and only its language counts (`fr-CA`, `zh-Hans`, `pt_BR`, `fr_FR.UTF-8`, or a whole `Accept-Language` value such as `fr-FR,fr;q=0.9`: the first language is used). Another well-formed language is answered in English with a `coerced_value` warning; a value that is not a locale is refused with `400 invalid_locale`. Falls back to `Accept-Language`, then `en` |

### Response example

Steps shortened to the first ones:

~~~json
{
  "environment": "sandbox",
  "success": true,
  "data": {
    "data": {
      "instructions": {
        "language": "EN",
        "ios": [
          {
            "model": null,
            "version": "15.0,14.0,13.0,12.0",
            "direct_apple_installation_url": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$lpa.airalo.com$DUMMY-2610301358-iKFxr-44741",
            "installation_via_qr_code": {
              "steps": {
                "1": "Go to “Settings”, tap “Cellular/Mobile Data”, then tap “Add Cellular/Data Plan” on your device.",
                "2": "Scan the QR code available on the  app, then tap “Add Cellular/Data Plan”.",
                "3": "Choose a label for your new eSIM plan."
              },
              "qr_code_data": "LPA:1$lpa.airalo.com$DUMMY-2610301358-iKFxr-44741",
              "qr_code_url": "https://sandbox.airalo.com/qr?expires=1879860202&id=473564&signature=..."
            },
            "installation_manual": {
              "steps": { "1": "Go to “Settings”, tap “Cellular/Mobile Data”, ...", "2": "Tap “Enter Details Manually” ..." },
              "smdp_address_and_activation_code": "LPA:1$lpa.airalo.com$DUMMY-2610301358-iKFxr-44741",
              "smdp_address": "lpa.airalo.com",
              "activation_code": "DUMMY-2610301358-iKFxr-44741"
            },
            "network_setup": {
              "steps": { "1": "Go to “Cellular/Mobile Data”, then select the recently downloaded eSIM ...", "2": "..." },
              "apn_type": "automatic",
              "apn_value": "N/A",
              "is_roaming": true
            }
          }
        ],
        "android": [ { "...": "same shape, with direct_android_installation_url and the steps of Android phones" } ]
      }
    },
    "meta": { "message": "success" }
  }
}
~~~

The steps are in `data.data.instructions`, one entry per family of devices in `ios` and `android`.
`model` and `version` say which devices an entry is written for (`null` means all). `steps` is an object whose keys are the
step numbers `"1"`, `"2"`, ...


> [!NOTE]
> The steps are generic text of the supplier: some say that the activation details are "available on the app".
> Show the QR code and the activation data of the order (`sims[]` above, or the order response) next to the steps in
> your own product.


## Sending instructions directly to the traveler

You do not have to relay the QR code yourself: the `email` of an [eSIM order](https://fx-port.com/docs/esims/orders.md) is passed to
the supplier, who emails the QR code and a sharing link to that address as soon as the eSIM is created. This is the
recommended flow whenever you have the traveler's own email. Use the endpoints on this page when you need to
render the QR code or the instructions inside your own product. On a [topup](https://fx-port.com/docs/esims/topups.md) the email is only
kept on the booking: nothing is emailed.

## Deprecated: the setup link

`GET /api/v1/esims/bookings/{booking_id}/setup-url` is **deprecated**: use
[setup instructions](#setup-instructions-by-booking) instead. It still answers, with the install links of the
order only (no supplier data), and the response carries a `deprecated_endpoint` warning:

~~~json
{
  "success": true,
  "fullUrl": "https://esims.cloud/v8hy7-usgkq6wg",
  "setup_url": "https://esims.cloud/v8hy7-usgkq6wg",
  "links": {
    "sharing_link": "https://esims.cloud/v8hy7-usgkq6wg",
    "direct_apple_installation_url": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$...",
    "direct_android_installation_url": "https://esimsetup.android.com/esim_qrcode_provisioning?carddata=LPA:1$...",
    "qrcode_url": "https://sandbox.airalo.com/qr?..."
  }
}
~~~

`fullUrl` and `setup_url` are the first link that exists (the sharing link, else the Apple or Android link, else the QR
image). `links` lists the links the order has. An order without any link is answered `404 setup_link_not_available`.

## Errors

| HTTP | `code` | Cause |
|---|---|---|
| `400` | `invalid_booking_id` | The booking reference is not valid |
| `400` | `invalid_iccid`, `invalid_locale` | The ICCID or the locale is not valid |
| `401` | `authentication_required`, `invalid_api_key` | Missing or invalid API key |
| `404` | `booking_not_found` | No eSIM booking of your agency has this reference |
| `404` | `not_found` | The booking is a topup, which has no setup data |
| `404` | `iccid_not_found` | The ICCID is not an eSIM of your agency in this environment |
| `429` | `rate_limit_exceeded` | Over 6,000 requests per minute for this key |
| `503` | `service_unavailable`, `service_busy` | The supplier connection or the service is busy (instructions by ICCID). Retry in a few seconds |

When the supplier cannot give the instructions, the answer carries the supplier's own status.
