Create an eSIM order
Create one eSIM, debit your agency balance (live only) and return the eSIM with its activation data.
/api/v1/esims/ordersPrices are computed on the server. quantity and every price field of the body are ignored (the response lists them in warnings), and every order creates exactly one eSIM: call the endpoint once per eSIM. The names of the traveler are optional and are not restricted to ASCII.
Send Content-Type: application/ (in any case). A form-encoded body also works, but a JSON body sent without the JSON content type is read as an empty form and answered with missing_field.
There is no idempotency key. While an order for a package is being processed, another order for the same package is refused with duplicate_request; on live so is an order for a package when the most recent confirmed order of your agency is for that same package and is less than 60 seconds old, whoever the traveler is. If a call times out, look for the booking with GET / before you send the order again.
If the supplier refuses the order the response has the supplier's status, nothing is debited and a failed booking is saved. In the sandbox the supplier creates a real test eSIM with a dummy activation code, no balance is debited, and the response (including its error bodies) carries balanceDebited, transactionCreated and payoutCreated, all false, with a snake_case twin each.
curl https://api.fx-port.com/api/v1/esims/orders \
-H "Authorization: Bearer fxp_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"package_id": "france-in-7days-1gb",
"first_name": "John",
"last_name": "Smith",
"email": "[email protected]",
"phone": "+15555550123",
"description": "Order for John Smith"
}'{
"environment": "sandbox",
"success": true,
"data": {
"order": {
"code": "20261101-106852",
"order_id": "20261101-106852",
"package_id": "france-in-7days-1gb",
"type": "sim",
"description": "Order for John Smith",
"esim_type": "local",
"created_at": "2026-11-01 15:23:22",
"package": "KPN France Data only-1 GB - 7 days",
"data": "1 GB",
"validity": 7,
"price": 468.84,
"currency": "DZD",
"total_price": 468.84,
"retail_price": 468.84,
"agency_currency": "DZD",
"supplier_currency": "USD",
"exchange_rate": 250,
"voice": null,
"text": null,
"coverage": ""
},
"sim": {
"iccid": "8900000540463077957",
"created_at": "2026-11-01 15:23:22",
"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=...",
"direct_apple_installation_url": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$lpa.airalo.com$DUMMY-2610301358-iKFxr-44741",
"apn_type": "automatic",
"apn_value": "N/A",
"apn": {
"ios": {
"apn_type": "automatic",
"apn_value": "N/A"
},
"android": {
"apn_type": "automatic",
"apn_value": "N/A"
}
},
"is_roaming": true,
"confirmation_code": null,
"msisdn": null,
"sharing": {
"access_code": 8803,
"link": "https://esims.cloud/v8hy7-usgkq6wg"
}
},
"iccid": "8900000540463077957"
},
"booking_id": "FX-1793546602916",
"message": "eSIM order placed successfully",
"balanceDebited": false,
"balance_debited": false,
"transactionCreated": false,
"transaction_created": false,
"payoutCreated": false,
"payout_created": false
}Request
Body
Required, JSON.
package_idstringrequiredThe id of a package from the catalog (its package_id).
At most 128 characters.
emailstringrequiredThe traveler's email address. FX-Port asks the supplier to send the QR code and a sharing link to it.
At most 254 characters.
phonestringrequiredThe traveler's phone number: at least 3 digits. Kept on the booking: it is not sent to the supplier.
At most 32 characters.
first_namestringThe traveler's first name. Any characters are accepted. Kept on the booking only.
At most 100 characters.
last_namestringThe traveler's last name. Any characters are accepted. Kept on the booking only.
At most 100 characters.
descriptionstringA note for your own records. It is sent to the supplier with the order and returned as order.description.
At most 500 characters.
agent_idstring (uuid)The id of the user of your agency who places the order. Optional: it appears on the booking as agent_id. A user of another agency is refused.
user_idstring (uuid)deprecatedLegacy name, still accepted. Use agent_id instead.
Previous name of agent_id.
customer_emailstringdeprecatedLegacy name, still accepted. Use email instead.
The name the booking rows use for email. Still accepted.
customer_phonestringdeprecatedLegacy name, still accepted. Use phone instead.
The name the booking rows use for phone. Still accepted.
customer_first_namestringdeprecatedLegacy name, still accepted. Use first_name instead.
The name the booking rows use for first_name. Still accepted.
customer_last_namestringdeprecatedLegacy name, still accepted. Use last_name instead.
The name the booking rows use for last_name. Still accepted.
Response
200The order was placed. The response has status 200, not 201.
successbooleantrue when the order was placed.
dataobjectThe order and its eSIM. sim is null when the supplier has not returned the eSIM yet: ask for the setup instructions later.
ShowHide 3 attributes
orderobjectThe order, with the amounts in your agency currency.
ShowHide 20 attributes
codestringdeprecatedLegacy name, still accepted. Use order_id instead.
The supplier's order code, for example 20261101-106852. Booking rows call it order_id. The older name of order_id: same value, use order_id.
order_idstringThe supplier's order code, for example 20261101-106852. Booking rows call it order_id. The snake_case twin of code, with the same value: use this name.
package_idstringThe package that was ordered.
typestringAlways sim.
descriptionstringnullableThe description you sent.
esim_typestringKind of eSIM plan, for example local.
created_atstringWhen the supplier created the order: YYYY-MM-DD HH:MM:SS, UTC, without a zone.
packagestringName of the package: operator, data allowance and validity.
datastringData allowance as text, for example 1 GB.
validityintegerValidity in days.
pricenumberWhat the traveler pays, in your agency currency. Equal to total_price here (in the catalog, price is a different amount: see the pricing guide).
currencystringCurrency of the amounts: the settlement currency of your agency.
total_pricenumberThe price to charge the traveler, in your agency currency. Your commission is total_price minus the amount debited from your balance.
retail_pricenumberThe price to charge the traveler, in your agency currency. Your commission is total_price minus the amount debited from your balance. Another name of total_price, with the same value.
agency_currencystringSame as currency.
supplier_currencystringCurrency the supplier prices the package in.
exchange_ratenumberRate used to convert supplier_currency to your agency currency.
voiceintegernullableVoice minutes included, or null.
textintegernullableSMS included, or null.
coveragestringCoverage text taken from the installation instructions. Can be an empty string.
simobjectnullableThe eSIM that was created. Treat the activation data as a secret of the traveler.
ShowHide 14 attributes
iccidstringThe ICCID of the eSIM. Keep it: usage, topups and instructions are asked by ICCID.
created_atstringnullableWhen the supplier created the eSIM: YYYY-MM-DD HH:MM:SS, UTC, without a zone.
lpastringnullableHost of the SM-DP+ server (the LPA address).
imsisstringnullableIMSI of the profile, when the supplier returns it.
qrcodestringnullableThe activation string the QR code encodes: LPA:1$<host>$<activation code>. Sensitive: show it only to the traveler.
qrcode_urlstringnullableLink to the QR code image.
direct_apple_installation_urlstringnullableLink that starts the installation on an iPhone.
apn_typestringnullableautomatic or manual.
apn_valuestringnullableThe APN to enter on a manual setup, or N/.
apnobjectnullableAPN settings per operating system.
ShowHide 2 attributes
iosobjectAccess point name (APN) settings for one operating system.
ShowHide 2 attributes
apn_typestringautomatic when the device configures the access point name by itself, manual when the traveler has to enter it.
apn_valuestringnullableThe access point name to enter on a manual setup; null or N/ when none is needed.
androidobjectAccess point name (APN) settings for one operating system.
ShowHide 2 attributes
apn_typestringautomatic when the device configures the access point name by itself, manual when the traveler has to enter it.
apn_valuestringnullableThe access point name to enter on a manual setup; null or N/ when none is needed.
is_roamingbooleannullableWhether the eSIM works through roaming.
confirmation_codestringnullableConfirmation code to enter at installation, when the operator requires one.
msisdnstringnullablePhone number of the eSIM, for the rare plans that include one.
sharingobjectnullableA shareable installation page for the eSIM.
ShowHide 2 attributes
access_codenumberCode the traveler enters on the sharing page.
linkstringPage where the traveler can open the eSIM details and QR code.
iccidstringThe ICCID of the eSIM: the same value as sim.iccid. When the supplier has not returned the eSIM yet it is the placeholder PENDING- followed by the order code.
booking_idstringThe FX-Port booking reference, FX- followed by a number. Keep it: it is the key of the booking in every other endpoint.
messagestringA confirmation sentence.
balanceDebitedbooleandeprecatedLegacy name, still accepted. Use balance_debited instead.
Sandbox only, always false: no balance was debited. The older name of balance_debited: same value, use balance_debited.
balance_debitedbooleanSandbox only, always false: no balance was debited. The snake_case twin of balanceDebited, with the same value: use this name.
transactionCreatedbooleandeprecatedLegacy name, still accepted. Use transaction_created instead.
Sandbox only, always false: no balance transaction was created. The older name of transaction_created: same value, use transaction_created.
transaction_createdbooleanSandbox only, always false: no balance transaction was created. The snake_case twin of transactionCreated, with the same value: use this name.
payoutCreatedbooleandeprecatedLegacy name, still accepted. Use payout_created instead.
Sandbox only, always false: no supplier payout was created. The older name of payout_created: same value, use payout_created.
payout_createdbooleanSandbox only, always false: no supplier payout was created. The snake_case twin of payoutCreated, with the same value: use this name.
Errors
| Status | Code | Example message |
|---|---|---|
| 400 | missing_field | package_id is required |
| 400 | invalid_email | email is not a valid email address |
| 400 | missing_field | phone is required |
| 400 | invalid_agent_id | agent_id is not a user of your agency. |
| 402 | insufficient_balance | Agency balance is insufficient. Available: 300.0 DZD, Required: 418.6 DZD. |
| 403 | read_write_permission_required | This endpoint requires an API key with read_write permission. |
| 404 | not_found | Package no-such-package-1gb not found in cache. Packages may still be loading — retry in a moment. |
| 409 | duplicate_request | An identical order is already being processed. Please wait before retrying. |
| 429 | rate_limit_exceeded | Too many requests hit the API too quickly. Please retry your request after the time specified in the `ratelimit-reset` header. |
| 429 | concurrency_limit_exceeded | Too many requests of this kind are already running for this API key; wait for one to finish and retry. |
| 503 | service_unavailable | Airalo token unavailable |
| 503 | balance_lock_timeout | Another operation on this balance is in progress. Please retry in a few seconds. |
The shape of the error body depends on the endpoint. See Errors for every shape and code.