Skip to content
eSIM API

eSIM API

Operational

Sell travel eSIMs for 200+ countries and regions: list the plans, buy one, and hand your customer the QR code and activation code from the same response.

Version v1 · 6 endpoints

Examples use the sandbox base URL https://sandbox.flightota.com/api/v1. The sandbox has no eSIM plans yet, so eSIM certification is arranged with your account manager. Plans, prices and orders below are shown from production data.

Partner API — eSIM

Sell travel eSIMs for 200+ countries and regions: list the plans, buy one, and hand your customer the QR code and activation code from the same response. Orders are paid from your FlightOTA deposit and issued at once.

Getting started

EnvironmentBase URLKey prefix
Sandboxhttps://sandbox.flightota.com/api/v1ak_test_
Productionhttps://api.flightota.com/api/v1ak_live_
  1. Issue a key in the agent portal under API access, enabled for eSIM (included in the Business and Enterprise API plans).
  2. Whitelist your server's public IP on the key.
  3. Send X-Api-Key and X-Api-Secret on every request, with Accept: application/json and Content-Type: application/json.
  4. Call GET /ping first; it returns your agency code and the IP we saw.

Conventions. Money is {"amount": <integer minor units>, "currency": "BDT"} in your billing currency. A key with Amounts switched on the API access page gets currency units instead (15161.94). Times are UTC, RFC 3339. Order references look like TFS-XXXXXXXX (orders made before 28 Sep 2026 keep their ES- reference). Errors are {"error": {"code": "…", "message": "…"}}; branch on code. Ignore fields you do not know.

Sandbox. The sandbox has no eSIM plans yet, so eSIM certification is arranged with your account manager. Plans, prices and orders below are shown from production data.

Order flow

Three calls: choose a place, choose a plan, buy it. The order answer already carries everything the traveller needs to install the eSIM; we also e-mail it to the address you give.

CallPurpose
GET /esims/placesEvery country and region with plans
GET /esims/places/{code}One place's plans and prices
POST /esims/ordersBuy 1–5 eSIMs of one plan
GET /esims/orders, GET /esims/orders/{order_ref}Read orders back

Places and plans

All places

GET /esims/places. Countries first, then regions (Europe, Asia, Global…). from is the cheapest plan at your price.

json
{
  "currency": "BDT",
  "places": [
    {
      "code": "AE",
      "name": "United Arab Emirates",
      "kind": "country",
      "image": "https://…/places/ae.png",
      "plans": 12,
      "from": {"amount": 38500, "currency": "BDT"}
    }
  ]
}

kind is country or region. A country's code is its ISO code; a region has its own code, as listed.

One place's plans

http
GET /esims/places/AE
json
{
  "place": {
    "code": "AE",
    "name": "United Arab Emirates",
    "kind": "country",
    "image": "https://…/places/ae.png",
    "plans": [
      {
        "id": "burj-mobile-7days-1gb",
        "title": "1 GB - 7 days",
        "data": "1 GB",
        "amount_mb": 1024,
        "days": 7,
        "unlimited": false,
        "voice": null,
        "text": null,
        "operator": "Burj Mobile",
        "operator_image": "https://…/operators/burj-mobile.png",
        "plan_type": "data",
        "activation": "first-usage",
        "kyc": false,
        "rechargeable": true,
        "info": [
          "5G data-only eSIM.",
          "Rechargeable online with no expiry.",
          "Operates on the Etisalat network in the United Arab Emirates."
        ],
        "coverage": ["AE"],
        "price": {"amount": 38500, "currency": "BDT"}
      }
    ]
  }
}

(Plan details are production data for the UAE; the price is illustrative, yours depends on your agreement.)

FieldMeaning
idThe plan to order
data, amount_mb, unlimitedData allowance
daysValidity from activation
voice, textMinutes and SMS when included, else null (data-only)
activationfirst-usage: the validity starts when the eSIM first connects at the destination. Otherwise it starts on installation
kyctrue when the operator requires identity verification before use
rechargeableCan be topped up later
coverageCountries the plan works in
priceWhat your deposit pays per eSIM

A place with no plans answers 404 place_not_found.

Order an eSIM

POST /esims/orders: the deposit pays and the eSIM is issued in the same call. Send an Idempotency-Key header; repeating the same request with the same key returns the order already made and buys nothing more.

FieldRequiredNotes
place_codeYesFrom places, e.g. AE
plan_idYesA plan's id
quantityNo1–5, default 1. One SIM per traveller; all on the same plan
emailYesThe traveller's e-mail; the eSIM and install guide are sent there
nameNoThe traveller's name, for the e-mail
client_referenceNoYour own reference, up to 64 characters
http
POST /api/v1/esims/orders
Idempotency-Key: sim-7731
json
{
  "place_code": "AE",
  "plan_id": "burj-mobile-7days-1gb",
  "quantity": 1,
  "email": "guest@example.com",
  "name": "Arif Rahman",
  "client_reference": "SIM-7731"
}

Response 201 Created

json
{
  "status": "issued",
  "order": {
    "order_ref": "TFS-4K7PQ2MX",
    "state": "issued",
    "created_at": "2026-09-22T14:40:03Z",
    "issued_at": "2026-09-22T14:40:05Z",
    "client_reference": "SIM-7731",
    "total": {"amount": 38500, "currency": "BDT"},
    "place": {"code": "AE", "name": "United Arab Emirates", "kind": "country"},
    "plan": {
      "id": "burj-mobile-7days-1gb",
      "title": "1 GB - 7 days",
      "data": "1 GB",
      "days": 7,
      "activation": "first-usage",
      "…": "…"
    },
    "quantity": 1,
    "validity_days": 7,
    "email": "guest@example.com",
    "sims": [
      {
        "iccid": "8910300000000012341",
        "lpa": "LPA:1$smdp.example.com$ABC-123-XYZ",
        "smdp_address": "smdp.example.com",
        "activation_code": "ABC-123-XYZ",
        "confirmation_code": null,
        "qr_url": "https://…/qr/ABC-123-XYZ.png",
        "apple_install_url": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$smdp.example.com$ABC-123-XYZ",
        "apn": {"type": "automatic", "value": "internet"},
        "roaming": true
      }
    ],
    "installation": {
      "qr": "1. Go to Settings > Mobile Data…",
      "manual": "1. Go to Settings > Mobile Data > Add eSIM > Enter details manually…"
    }
  }
}

(Codes are illustrative; the shape is exactly what the API returns.)

Response 202 Accepted

Rarely, the eSIM provider does not answer the order in time and it may still have been placed. The order is charged and comes back as "status": "pending" with "state": "ticketing" and no sims yet. Do not order it again: our team confirms it with the provider, then the SIM codes appear on GET /esims/orders/{order_ref}, or the charge is returned to your deposit.

Giving the eSIM to the traveller

FieldUse it for
qr_urlA picture of the QR code to show or print. The traveller scans it in phone settings
lpaThe QR content as text, if you draw the QR yourself
smdp_address + activation_codeManual installation ("Enter details manually")
apple_install_urlOne-tap install on iPhone (iOS 17.4 or later)
apnSet by the phone when automatic; otherwise enter value as the APN
roamingTurn data roaming on for this eSIM
installation.qr, installation.manualStep-by-step text to show

An eSIM can be installed only once: tell the traveller to install it before the trip, on Wi-Fi, and not to delete it.

HTTPerror.codeMeaning
400idempotency_key_requiredAdd the header
422validation_failedSee errors
402insufficient_fundsDeposit too low; nothing charged
409supplier_refusedThe plan was not issued; nothing charged; choose another
410plan_unavailableThe plan is no longer sold; load the place again
409duplicate_request, request_in_progressKey reused with another body, or still running
503supplier_unavailableNothing charged; retry

Read and list orders

GET /esims/orders/{order_ref} returns {"order": {…}} in the shape above, with the SIM codes, any time. Another agency's reference answers 404 order_not_found.

GET /esims/orders, newest first, 25 a page:

Query parameterNotes
client_referenceYour own reference
created_from, created_toYYYY-MM-DD
page, per_pageper_page up to 100
json
{
  "orders": [{"order_ref": "TFS-4K7PQ2MX", "state": "issued", "…": "…"}],
  "page": 1,
  "per_page": 25,
  "total": 1,
  "last_page": 1
}

An issued eSIM is final: it cannot be cancelled or refunded through the API. Top-ups and usage checks are not in the API yet.

Errors and go-live

Access errors

HTTPerror.codeFix
401unauthenticated, credential_inactiveCheck the key and secret; issue a new key if revoked
403ip_whitelist_required, ip_not_whitelistedWhitelist your server's IP
403product_not_allowedThe key is not enabled for eSIM; your API plan must include it
403partner_inactive, subscription_*, environment_not_activatedAccount or plan not active; live keys work after certification
429rate_limitedWait a minute
503supplier_unavailableRetry; nothing was charged

Go-live checklist

  • GET /ping from your whitelisted server
  • Places list with search by name, countries and regions
  • A place's plans shown with data, days, activation and KYC flag
  • An order placed with an Idempotency-Key; the repeat returns the same order
  • QR picture, manual codes and the iPhone install link shown to the traveller
  • 402 insufficient_funds and 410 plan_unavailable shown in plain words
  • Orders read back by order_ref and by client_reference

Questions and certification: your account manager, or support from the agent portal.

Endpoint reference

Getting started

Ping

GET/ping

Call GET /ping first; it returns your agency code and the IP we saw.

Parameters

This endpoint takes no parameters.

Errors

  • 401unauthenticatedCheck the key and secret; issue a new key if revoked.
  • 401credential_inactiveCheck the key and secret; issue a new key if revoked.
  • 403ip_whitelist_requiredWhitelist your server's IP.
  • 403ip_not_whitelistedWhitelist your server's IP.
  • 403product_not_allowedThe key is not enabled for eSIM; your API plan must include it.
  • 403partner_inactiveAccount or plan not active; live keys work after certification.
  • 403subscription_*Account or plan not active; live keys work after certification.
  • 403environment_not_activatedAccount or plan not active; live keys work after certification.
  • 429rate_limitedWait a minute.
  • 503supplier_unavailableRetry; nothing was charged.
curl -X GET "https://sandbox.flightota.com/api/v1/ping" \
  -H "X-Api-Key: $FLIGHTOTA_API_KEY" \
  -H "X-Api-Secret: $FLIGHTOTA_API_SECRET" \
  -H "Accept: application/json"
Response · 200
{
  "ok": true,
  "partner": "PTR-000020",
  "key_id": "ak_test_your_key_id",
  "environment": "test",
  "from_ip": "203.0.113.10",
  "server_time": "2026-09-22T13:25:02+00:00"
}
Error · 401
{
  "error": {
    "code": "unauthenticated",
    "message": "Check the key and secret; issue a new key if revoked."
  }
}

Places and plans

All places

GET/esims/places

Countries first, then regions (Europe, Asia, Global…). from is the cheapest plan at your price. kind is country or region. A country's code is its ISO code; a region has its own code, as listed.

Parameters

This endpoint takes no parameters.

Errors

  • 401unauthenticatedCheck the key and secret; issue a new key if revoked.
  • 401credential_inactiveCheck the key and secret; issue a new key if revoked.
  • 403ip_whitelist_requiredWhitelist your server's IP.
  • 403ip_not_whitelistedWhitelist your server's IP.
  • 403product_not_allowedThe key is not enabled for eSIM; your API plan must include it.
  • 403partner_inactiveAccount or plan not active; live keys work after certification.
  • 403subscription_*Account or plan not active; live keys work after certification.
  • 403environment_not_activatedAccount or plan not active; live keys work after certification.
  • 429rate_limitedWait a minute.
  • 503supplier_unavailableRetry; nothing was charged.
curl -X GET "https://sandbox.flightota.com/api/v1/esims/places" \
  -H "X-Api-Key: $FLIGHTOTA_API_KEY" \
  -H "X-Api-Secret: $FLIGHTOTA_API_SECRET" \
  -H "Accept: application/json"
Response · 200
{
  "currency": "BDT",
  "places": [
    {
      "code": "AE",
      "name": "United Arab Emirates",
      "kind": "country",
      "image": "https://…/places/ae.png",
      "plans": 12,
      "from": {
        "amount": 38500,
        "currency": "BDT"
      }
    }
  ]
}
Error · 401
{
  "error": {
    "code": "unauthenticated",
    "message": "Check the key and secret; issue a new key if revoked."
  }
}

Places and plans

One place's plans

GET/esims/places/{code}

One place's plans and prices. (Plan details are production data for the UAE; the price is illustrative, yours depends on your agreement.) A place with no plans answers 404 place_not_found.

Parameters

ParameterInDescription
code
stringrequired
pathA place's code.
Example: AE

Errors

  • 404place_not_foundA place with no plans.
  • 401unauthenticatedCheck the key and secret; issue a new key if revoked.
  • 401credential_inactiveCheck the key and secret; issue a new key if revoked.
  • 403ip_whitelist_requiredWhitelist your server's IP.
  • 403ip_not_whitelistedWhitelist your server's IP.
  • 403product_not_allowedThe key is not enabled for eSIM; your API plan must include it.
  • 403partner_inactiveAccount or plan not active; live keys work after certification.
  • 403subscription_*Account or plan not active; live keys work after certification.
  • 403environment_not_activatedAccount or plan not active; live keys work after certification.
  • 429rate_limitedWait a minute.
  • 503supplier_unavailableRetry; nothing was charged.
curl -X GET "https://sandbox.flightota.com/api/v1/esims/places/AE" \
  -H "X-Api-Key: $FLIGHTOTA_API_KEY" \
  -H "X-Api-Secret: $FLIGHTOTA_API_SECRET" \
  -H "Accept: application/json"
Response · 200
{
  "place": {
    "code": "AE",
    "name": "United Arab Emirates",
    "kind": "country",
    "image": "https://…/places/ae.png",
    "plans": [
      {
        "id": "burj-mobile-7days-1gb",
        "title": "1 GB - 7 days",
        "data": "1 GB",
        "amount_mb": 1024,
        "days": 7,
        "unlimited": false,
        "voice": null,
        "text": null,
        "operator": "Burj Mobile",
        "operator_image": "https://…/operators/burj-mobile.png",
        "plan_type": "data",
        "activation": "first-usage",
        "kyc": false,
        "rechargeable": true,
        "info": [
          "5G data-only eSIM.",
          "Rechargeable online with no expiry.",
          "Operates on the Etisalat network in the United Arab Emirates."
        ],
        "coverage": [
          "AE"
        ],
        "price": {
          "amount": 38500,
          "currency": "BDT"
        }
      }
    ]
  }
}
Error · 404
{
  "error": {
    "code": "place_not_found",
    "message": "A place with no plans."
  }
}

Order an eSIM

Order an eSIM

POST/esims/orders

The deposit pays and the eSIM is issued in the same call. Rarely, the eSIM provider does not answer the order in time and it may still have been placed. The order is charged and comes back as "status": "pending" with "state": "ticketing" and no sims yet. Do not order it again: our team confirms it with the provider, then the SIM codes appear on GET /esims/orders/{order_ref}, or the charge is returned to your deposit. An eSIM can be installed only once: tell the traveller to install it before the trip, on Wi-Fi, and not to delete it.

Send an Idempotency-Key header; repeating the same request with the same key returns the order already made and buys nothing more. This call pays from your deposit.

Parameters

ParameterInDescription
Idempotency-Key
stringrequired
headerRepeating the same request with the same key returns the order already made and buys nothing more.
Example: sim-7731
place_code
stringrequired
bodyFrom places, e.g. AE.
plan_id
stringrequired
bodyA plan's id.
quantity
integer 1–5optional
body1–5, default 1. One SIM per traveller; all on the same plan.
email
stringrequired
bodyThe traveller's e-mail; the eSIM and install guide are sent there.
name
stringoptional
bodyThe traveller's name, for the e-mail.
client_reference
stringoptional
bodyYour own reference, up to 64 characters.

Errors

  • 400idempotency_key_requiredAdd the header.
  • 422validation_failedSee errors.
  • 402insufficient_fundsDeposit too low; nothing charged.
  • 409supplier_refusedThe plan was not issued; nothing charged; choose another.
  • 410plan_unavailableThe plan is no longer sold; load the place again.
  • 409duplicate_requestKey reused with another body.
  • 409request_in_progressStill running.
  • 503supplier_unavailableNothing charged; retry.
  • 401unauthenticatedCheck the key and secret; issue a new key if revoked.
  • 401credential_inactiveCheck the key and secret; issue a new key if revoked.
  • 403ip_whitelist_requiredWhitelist your server's IP.
  • 403ip_not_whitelistedWhitelist your server's IP.
  • 403product_not_allowedThe key is not enabled for eSIM; your API plan must include it.
  • 403partner_inactiveAccount or plan not active; live keys work after certification.
  • 403subscription_*Account or plan not active; live keys work after certification.
  • 403environment_not_activatedAccount or plan not active; live keys work after certification.
  • 429rate_limitedWait a minute.
curl -X POST "https://sandbox.flightota.com/api/v1/esims/orders" \
  -H "X-Api-Key: $FLIGHTOTA_API_KEY" \
  -H "X-Api-Secret: $FLIGHTOTA_API_SECRET" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: sim-7731" \
  -H "Content-Type: application/json" \
  -d '{
  "place_code": "AE",
  "plan_id": "burj-mobile-7days-1gb",
  "quantity": 1,
  "email": "guest@example.com",
  "name": "Arif Rahman",
  "client_reference": "SIM-7731"
}'
Response · 201
{
  "status": "issued",
  "order": {
    "order_ref": "TFS-4K7PQ2MX",
    "state": "issued",
    "created_at": "2026-09-22T14:40:03Z",
    "issued_at": "2026-09-22T14:40:05Z",
    "client_reference": "SIM-7731",
    "total": {
      "amount": 38500,
      "currency": "BDT"
    },
    "place": {
      "code": "AE",
      "name": "United Arab Emirates",
      "kind": "country"
    },
    "plan": {
      "id": "burj-mobile-7days-1gb",
      "title": "1 GB - 7 days",
      "data": "1 GB",
      "days": 7,
      "activation": "first-usage",
      "…": "…"
    },
    "quantity": 1,
    "validity_days": 7,
    "email": "guest@example.com",
    "sims": [
      {
        "iccid": "8910300000000012341",
        "lpa": "LPA:1$smdp.example.com$ABC-123-XYZ",
        "smdp_address": "smdp.example.com",
        "activation_code": "ABC-123-XYZ",
        "confirmation_code": null,
        "qr_url": "https://…/qr/ABC-123-XYZ.png",
        "apple_install_url": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$smdp.example.com$ABC-123-XYZ",
        "apn": {
          "type": "automatic",
          "value": "internet"
        },
        "roaming": true
      }
    ],
    "installation": {
      "qr": "1. Go to Settings > Mobile Data…",
      "manual": "1. Go to Settings > Mobile Data > Add eSIM > Enter details manually…"
    }
  }
}
Response · 202 pending
{
  "status": "pending",
  "order": {
    "order_ref": "TFS-…",
    "state": "ticketing",
    "sims": [],
    "…": "…"
  }
}
Error · 400
{
  "error": {
    "code": "idempotency_key_required",
    "message": "Add the header."
  }
}

Read and list orders

Read an order

GET/esims/orders/{order_ref}

Returns the order in the shape above, with the SIM codes, any time. Another agency's reference answers 404 order_not_found.

Parameters

ParameterInDescription
order_ref
stringrequired
pathThe order reference, TFS-XXXXXXXX.
Example: TFS-4K7PQ2MX

Errors

  • 404order_not_foundAnother agency's reference.
  • 401unauthenticatedCheck the key and secret; issue a new key if revoked.
  • 401credential_inactiveCheck the key and secret; issue a new key if revoked.
  • 403ip_whitelist_requiredWhitelist your server's IP.
  • 403ip_not_whitelistedWhitelist your server's IP.
  • 403product_not_allowedThe key is not enabled for eSIM; your API plan must include it.
  • 403partner_inactiveAccount or plan not active; live keys work after certification.
  • 403subscription_*Account or plan not active; live keys work after certification.
  • 403environment_not_activatedAccount or plan not active; live keys work after certification.
  • 429rate_limitedWait a minute.
  • 503supplier_unavailableRetry; nothing was charged.
curl -X GET "https://sandbox.flightota.com/api/v1/esims/orders/TFS-4K7PQ2MX" \
  -H "X-Api-Key: $FLIGHTOTA_API_KEY" \
  -H "X-Api-Secret: $FLIGHTOTA_API_SECRET" \
  -H "Accept: application/json"
Response · 200
{
  "order": {
    "order_ref": "TFS-4K7PQ2MX",
    "state": "issued",
    "…": "the shape above, with the SIM codes"
  }
}
Error · 404
{
  "error": {
    "code": "order_not_found",
    "message": "Another agency's reference."
  }
}

Read and list orders

List orders

GET/esims/orders

Newest first, 25 a page. An issued eSIM is final: it cannot be cancelled or refunded through the API. Top-ups and usage checks are not in the API yet.

Parameters

ParameterInDescription
client_reference
stringoptional
queryYour own reference.
created_from
YYYY-MM-DDoptional
queryCreated on or after this date.
created_to
YYYY-MM-DDoptional
queryCreated on or before this date.
page
integeroptional
queryThe page.
per_page
integeroptional
queryper_page up to 100.

Errors

  • 401unauthenticatedCheck the key and secret; issue a new key if revoked.
  • 401credential_inactiveCheck the key and secret; issue a new key if revoked.
  • 403ip_whitelist_requiredWhitelist your server's IP.
  • 403ip_not_whitelistedWhitelist your server's IP.
  • 403product_not_allowedThe key is not enabled for eSIM; your API plan must include it.
  • 403partner_inactiveAccount or plan not active; live keys work after certification.
  • 403subscription_*Account or plan not active; live keys work after certification.
  • 403environment_not_activatedAccount or plan not active; live keys work after certification.
  • 429rate_limitedWait a minute.
  • 503supplier_unavailableRetry; nothing was charged.
curl -X GET "https://sandbox.flightota.com/api/v1/esims/orders" \
  -H "X-Api-Key: $FLIGHTOTA_API_KEY" \
  -H "X-Api-Secret: $FLIGHTOTA_API_SECRET" \
  -H "Accept: application/json"
Response · 200
{
  "orders": [
    {
      "order_ref": "TFS-4K7PQ2MX",
      "state": "issued",
      "…": "…"
    }
  ],
  "page": 1,
  "per_page": 25,
  "total": 1,
  "last_page": 1
}
Error · 401
{
  "error": {
    "code": "unauthenticated",
    "message": "Check the key and secret; issue a new key if revoked."
  }
}