Create Order
Create a new eSIM order using your account balance.
Endpoint
POST /api/v1/business/esims/order
AI prompt — Create Order
Paste into ChatGPT, Claude, Cursor, Copilot or any coding agent to generate this part of your integration. Built-in recommendation: Send packageCode + idempotency_key, persist the whole response, render QR from lpaString, complete the rare pending order with bounded polling.
Show prompt text (84 lines)
# eSIMfly Business API — Create Order (POST /esims/order) — prompt for AI coding assistants
TASK: implement eSIM purchase. This is one of the few eSIMfly calls that legitimately runs inside
a customer request. Persist everything from the response; use idempotency keys; handle the rare
pending order with bounded polling.
COMMON RULES (apply to every eSIMfly request)
- Base URL: https://esimfly.net/api/v1/business
- Headers on every call: RT-AccessCode (esf_...), RT-RequestID (fresh UUID v4 per request; reuse -> 400 DUPLICATE_REQUEST),
RT-Timestamp (ms since epoch; >5 min old -> 401 INVALID_TIMESTAMP),
RT-Signature = UPPERCASE hex HMAC-SHA256(secretKey, timestamp + requestId + accessCode + rawBody).
rawBody = "" for GET; for POST/PUT sign the exact body string you send, with Content-Type: application/json.
- Keep access code + secret key server-side in env vars. Never ship them to a browser or mobile app.
- Responses: { success: true, ... } or { success: false, error|message, code }. Branch on `success` and `code`.
- Rate limits are per API key, shown in the business dashboard (typically 100/minute, 1,000/hour, 10,000/day) -> RATE_LIMIT_EXCEEDED. Pace bulk work at <= 1 req/s.
- Read `currency` from responses (USD | IQD | EUR for enterprise). Never hard-code it. IQD amounts are integers.
- Package codes are opaque strings: store and send back verbatim, never parse or prefix them.
- Node.js/TypeScript: use the official SDK instead of raw HTTP — `npm install @esimfly/sdk`
(https://github.com/eSimfly-Official/esimfly-sdk-nodejs); it implements these rules. Other languages: implement the contract below.
- Full multi-endpoint prompt: https://docs.esimfly.net/llm/esimfly-api-full-prompt.txt
ENDPOINT
POST https://esimfly.net/api/v1/business/esims/order
Body (JSON — sign the exact string you send):
{
"packageCode": "<package_code from OUR synced packages table>", // required, opaque
"quantity": 1, // optional, 1..10 (max 10 per order)
"idempotency_key": "<our order id>", // optional but ALWAYS send it (<= 200 chars, unique per purchase)
"recurring": false // optional, only for O2/Vodafone packages with is_recurring = true
}
Do NOT send price/packageName/duration — the API looks them up and charges the CURRENT cost.
RESPONSE 200 (completed)
{ success: true, message, orderReference, esimId, packageName, newBalance, currency,
lpaString, qrCodeUrl ("data:image/png;base64,..."), directAppleInstallUrl, paymentMethod: "balance",
status: "completed", amount, profit, final_price, processing_time_ms,
esims: [{ iccid, lpaString, qrCodeUrl, directAppleInstallUrl, directAndroidInstallUrl?, status: "New",
imsi, msisdn, sim_status, esim_status, profile_status, unlimited,
total_volume (bytes; 1073741824 = 1 GB), total_duration (days), expired_time, isPending: false }] }
RESPONSE 200 (pending — rare; a few packages are provisioned asynchronously and take a few minutes)
{ success: true, orderReference, packageName, status: "pending_details", amount, isPending: true, esims: [] }
RESPONSE 200 (idempotent replay) — same idempotency_key as an already-completed order:
{ success: true, duplicate: true, orderReference, esimId, packageName, status: "completed", esims: [...] }
ERRORS
400 MISSING_PACKAGE_CODE | INVALID_PACKAGE | PRICE_MISMATCH (only if you send price — don't)
400 INSUFFICIENT_BALANCE { currentBalance, requiredBalance, needToLoad }
404 PACKAGE_NOT_FOUND (enterprise ent_ code not yours / inactive)
409 DUPLICATE_REQUEST — a request with the same idempotency_key (or, without a key, the same
package) is still in flight. Wait and read the original result; do not fire again.
500 ORDER_PROCESSING_ERROR
STATUS CHECK (pending orders only)
GET https://esimfly.net/api/v1/business/esims/order?orderReference=<ref>
-> { success, order: { id, packageName, packageCode, status, amount, finalPrice, orderDate, orderReference,
paymentMethod, paymentStatus, esim: { iccid|null, status, imsi, msisdn, sim_status, esim_status,
profile_status, qrCodeUrl, directAppleInstallUrl, directAndroidInstallUrl, lpaString, isPending,
unlimited, totalVolume, totalDuration, expiredTime } } }
Ready when order.esim.isPending === false and order.esim.iccid is set.
INTEGRATION PATTERN
1. Create OUR order row first (status "creating", our_order_id). Use our_order_id as idempotency_key.
2. POST once. On network timeout / 5xx: retry ONCE with the SAME idempotency_key and a NEW
RT-RequestID. A replay returns the original order (duplicate: true) instead of charging twice.
If the first attempt returned a failure code, the same key may be reused for a corrected retry.
3. Persist the full response: orderReference, esimId, packageName, amount, final_price, profit,
currency, status, and one esims row per item (iccid, lpaString, directAppleInstallUrl,
directAndroidInstallUrl, status, imsi, msisdn, total_volume, total_duration, expired_time).
Update the local balance mirror from newBalance.
4. Store lpaString and render QR codes yourself; do not persist the base64 qrCodeUrl image.
5. If status === "pending_details": mark our order "pending", show "eSIM being prepared"; poll
GET /esims/order every 15–30 s, max ~10 min; when isPending is false store the eSIM fields and
deliver. If still pending after that, alert support (do not re-order).
6. Deliver to the customer: QR (from lpaString), directAppleInstallUrl (iOS one-tap),
directAndroidInstallUrl when present, and manual SM-DP+ / activation code split from lpaString
("LPA:1$<smdp>$<code>").
7. On INSUFFICIENT_BALANCE show needToLoad to the operator; do not retry automatically.
8. Do not call /balance before every order; do not call /esims or /orders after an order to "confirm".
DELIVERABLE: createOrder() in the shared client with idempotency + single retry, the order persistence
code, the pending-order handler (bounded polling), and error mapping for the codes above.
Authentication
This endpoint requires HMAC authentication. See Authentication for details.
Request Headers
| Header | Type | Required | Description |
|---|---|---|---|
| RT-AccessCode | String | Yes | Your API access code |
| RT-RequestID | String | Yes | Unique request ID (UUID v4) |
| RT-Timestamp | String | Yes | Request timestamp in milliseconds |
| RT-Signature | String | Yes | HMAC-SHA256 signature |
| Content-Type | String | Yes | Must be "application/json" |
Request Body
Simply provide the package code - everything else is handled automatically.
| Field | Type | Required | Description |
|---|---|---|---|
| packageCode | String | Yes | Package code from the packages endpoint |
| quantity | Integer | No | Number of eSIMs to order (default: 1, max: 10) |
| recurring | Boolean | No | For O2 and Vodafone packages that support auto-renewal. Set to true to enable subscription. Default: false (one-time). Check is_recurring in the packages list to see which packages support this. |
| idempotency_key | String | No (recommended) | Your own unique id for this purchase (max 200 characters). A retry with the same key returns the original order instead of charging again — see Idempotency. |
The API automatically handles:
- ✅ Package name lookup
- ✅ Price calculation
- ✅ Flag URL from database
- ✅ Duration detection
Request Examples
Single eSIM:
{
"packageCode": "TR1GB7D"
}
Multiple eSIMs:
{
"packageCode": "merhaba-15days-2gb",
"quantity": 3
}
O2/Vodafone package (one-time, default):
{
"packageCode": "OT-50000MB-9999T-9999V"
}
O2/Vodafone package (with auto-renewal subscription):
{
"packageCode": "OT-50000MB-9999T-9999V",
"recurring": true
}
Response
Success Response (200 OK)
USD User Example:
{
"success": true,
"message": "Order processed successfully",
"orderReference": "order_1692123456_ab7cd",
"esimId": 2503,
"packageName": "Turkey 1 GB 7 Days",
"newBalance": 47.28,
"currency": "USD",
"lpaString": "LPA:1$rsp-3104.idemia.io$DOAZJ-HYDO5-HGMLN-S9B8S",
"qrCodeUrl": "data:image/png;base64,iVBORw0KGgo...",
"directAppleInstallUrl": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$rsp-3104.idemia.io$DOAZJ-HYDO5-HGMLN-S9B8S",
"paymentMethod": "balance",
"status": "completed",
"amount": 2.72,
"profit": 0.20,
"final_price": 2.72,
"processing_time_ms": 3245,
"esims": [
{
"iccid": "8932042000010078801",
"lpaString": "LPA:1$rsp-3104.idemia.io$DOAZJ-HYDO5-HGMLN-S9B8S",
"qrCodeUrl": "data:image/png;base64,iVBORw0KGgo...",
"directAppleInstallUrl": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$rsp-3104.idemia.io$DOAZJ-HYDO5-HGMLN-S9B8S",
"status": "New",
"imsi": "260010185757766",
"msisdn": "48267753430",
"sim_status": "AFFECTED",
"esim_status": "Assigned (Not Installed)",
"profile_status": null,
"unlimited": false,
"total_volume": 1073741824,
"total_duration": 7,
"expired_time": "2026-08-24T17:00:31.884Z",
"isPending": false
}
]
}
IQD User Example:
{
"success": true,
"message": "Order processed successfully",
"orderReference": "order_1756393024924_wmdq2",
"esimId": 2604,
"packageName": "Turkey 1GB 7Days",
"newBalance": 203488,
"currency": "IQD",
"lpaString": "LPA:1$rsp-3104.idemia.io$DOAZJ-HYDO5-HGMLN-S9B8S",
"qrCodeUrl": "data:image/png;base64,iVBORw0KGgo...",
"directAppleInstallUrl": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$rsp-3104.idemia.io$DOAZJ-HYDO5-HGMLN-S9B8S",
"paymentMethod": "balance",
"status": "completed",
"amount": 969,
"profit": 91,
"final_price": 969,
"processing_time_ms": 3245,
"esims": [
{
"iccid": "8910300000037870123",
"lpaString": "LPA:1$rsp-3104.idemia.io$DOAZJ-HYDO5-HGMLN-S9B8S",
"qrCodeUrl": "data:image/png;base64,iVBORw0KGgo...",
"directAppleInstallUrl": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$rsp-3104.idemia.io$DOAZJ-HYDO5-HGMLN-S9B8S",
"status": "New",
"imsi": "260010185757766",
"msisdn": "48267753430",
"sim_status": "AFFECTED",
"esim_status": "Assigned (Not Installed)",
"profile_status": null,
"unlimited": false,
"total_volume": 1073741824,
"total_duration": 7,
"expired_time": "2026-08-24T17:00:31.884Z",
"isPending": false
}
]
}
Pending Response (200 OK)
A small number of packages are provisioned asynchronously and take a few minutes. For those you receive a pending response with an empty esims array:
{
"success": true,
"message": "Order created successfully. eSIM is being provisioned.",
"orderReference": "order_1776079539762_hvkrk",
"packageName": "Example 12 GB 4 Days",
"status": "pending_details",
"amount": 5.85,
"isPending": true,
"esims": []
}
How to get the eSIM when it is ready: call the order status endpoint every 15–30 seconds (for at most about 10 minutes, then hand the order to support — do not re-order):
Poll Response - Still Pending:
{
"success": true,
"order": {
"esim": {
"iccid": null,
"isPending": true
}
}
}
Poll Response - eSIM Ready:
{
"success": true,
"order": {
"packageName": "Example 12 GB 4 Days",
"status": "completed",
"esim": {
"iccid": "8981100000012345678",
"status": "New",
"imsi": "260010185757766",
"msisdn": "48267753430",
"sim_status": "AFFECTED",
"esim_status": "Assigned (Not Installed)",
"profile_status": null,
"qrCodeUrl": "data:image/png;base64,iVBORw0KGgo...",
"directAppleInstallUrl": "https://esimsetup.apple.com/...",
"directAndroidInstallUrl": "https://android.esim.me/...",
"lpaString": "LPA:1...",
"isPending": false,
"unlimited": false,
"totalVolume": 12884901888,
"totalDuration": 4,
"expiredTime": "2026-08-24T17:00:31.884Z"
}
}
}
When isPending changes to false and iccid is populated, the eSIM is ready to deliver to your customer.
Response Fields
| Field | Type | Description |
|---|---|---|
| success | Boolean | Request success status |
| message | String | Human-readable status message |
| orderReference | String | Unique order reference for tracking |
| esimId | Integer | Primary eSIM ID (for single eSIM orders) |
| packageName | String | Ordered package name |
| newBalance | Number | Your updated account balance in preferred currency |
| currency | String | Currency code based on the account: USD or IQD, or EUR for an enterprise account |
| lpaString | String | Raw LPA string (e.g., "LPA:1$smdp.address$activation-code") for QR code generation |
| qrCodeUrl | String | Base64-encoded QR code image (data:image/png;base64,...) |
| directAppleInstallUrl | String | Direct iPhone installation URL |
| paymentMethod | String | Always "balance" for business orders |
| status | String | Order status ("completed" or "pending_details") |
| amount | Number | Total order amount in user's preferred currency |
| profit | Number | Your profit amount in user's preferred currency |
| final_price | Number | Final price charged to your balance in preferred currency |
| processing_time_ms | Integer | Order processing time in milliseconds |
| esims | Array | Array of eSIM details |
| note | String | Additional information (for pending orders) |
eSIM Object Fields
| Field | Type | Description |
|---|---|---|
| iccid | String | eSIM ICCID number |
| lpaString | String | Raw LPA string for QR code generation (format: "LPA:1$smdp.address$activation-code") |
| qrCodeUrl | String | Base64-encoded QR code image |
| directAppleInstallUrl | String | Direct Apple installation URL |
| status | String | eSIM status ("New" or "PENDING") |
| imsi | String/null | IMSI of the eSIM profile |
| msisdn | String/null | The profile's home number (MSISDN). For eSIMfly-network eSIMs this is a roaming-hub number (Polish +48 prefix) regardless of the country the eSIM is used in — it is what SMS delivery uses, not a local number |
| sim_status | String/null | Provider SIM resource status (e.g. AFFECTED = assigned, FREE = released). Not an SM-DP+ status. (Previously named profileStatus.) |
| esim_status | String/null | Human-readable lifecycle label: New, Assigned (Not Installed), Installed, Active, Not Active |
| profile_status | String/null | eSIM profile (BPP) install status (e.g. Enable, Disable) |
| unlimited | Boolean | Whether the plan has unlimited data |
| total_volume | Number/null | Total data in bytes (e.g. 1073741824 = 1 GB) |
| total_duration | Number/null | Validity in days |
| expired_time | String/null | Expiry date (ISO 8601) |
| isPending | Boolean | Whether eSIM details are still being processed |
The field previously named profileStatus has been renamed to sim_status. If your integration reads profileStatus, update it to sim_status.
Using the LPA String:
- Extract SMDP address: Split by
$and get second part - Extract activation code: Split by
$and get third part - Generate your own QR code using the full LPA string
- Display to users for manual entry
Idempotency
Send an idempotency_key (for example your internal order id) with every order so a retry after a
network timeout can never charge you twice:
- Same key, order already completed → 200 with the original order and
"duplicate": true:
{
"success": true,
"message": "Order already processed for this idempotency key",
"duplicate": true,
"orderReference": "order_1692123456_ab7cd",
"esimId": 2503,
"packageName": "Turkey 1 GB 7 Days",
"status": "completed",
"esims": [
{
"iccid": "8932042000010078801",
"lpaString": "LPA:1$rsp-3104.idemia.io$DOAZJ-HYDO5-HGMLN-S9B8S",
"qrCodeUrl": "https://...",
"directAppleInstallUrl": "https://esimsetup.apple.com/...",
"directAndroidInstallUrl": "https://...",
"status": "New",
"isPending": false
}
]
}
- Same key while the first request is still being processed → 409
DUPLICATE_REQUEST. Wait and read the first result; do not send again. - If the first attempt failed, the same key can be reused for a corrected retry.
- Without a key, only concurrent requests for the same package are blocked (409); sequential orders for the same package are always accepted. Use
quantityfor multiple eSIMs of one package.
On a timeout or 5xx, retry once with the same idempotency_key and a new RT-RequestID. Never retry an order without a key.
Order Status Checking
You can check the status of any order using the order reference:
Endpoint: GET /api/v1/business/esims/order?orderReference=YOUR_ORDER_REF
Status Check Response
| Field | Type | Description |
|---|---|---|
| order.id | Integer | Order ID |
| order.packageName | String | Package name |
| order.packageCode | String | Package code |
| order.status | String | Order status |
| order.amount | Number | Order amount |
| order.orderReference | String | Order reference |
| order.paymentMethod | String | Payment method |
| order.paymentStatus | String | Payment status |
| order.esim.iccid | String | eSIM ICCID (null if still pending) |
| order.esim.status | String | eSIM status |
| order.esim.imsi | String/null | IMSI of the eSIM profile |
| order.esim.msisdn | String/null | The profile's home number (MSISDN) — a roaming-hub number (+48 prefix) for eSIMfly-network eSIMs, not a local number |
| order.esim.sim_status | String/null | Provider SIM resource status (AFFECTED/FREE) — not an SM-DP+ status |
| order.esim.esim_status | String/null | Human-readable lifecycle label (e.g. Assigned (Not Installed), Active) |
| order.esim.profile_status | String/null | eSIM profile (BPP) install status (Enable/Disable) |
| order.esim.qrCodeUrl | String | QR code URL |
| order.esim.directAppleInstallUrl | String | Apple direct install URL |
| order.esim.directAndroidInstallUrl | String | Android direct install URL |
| order.esim.lpaString | String | LPA activation string |
| order.esim.isPending | Boolean | true if eSIM is still being provisioned |
| order.esim.unlimited | Boolean | Whether the plan has unlimited data |
| order.esim.totalVolume | Number | Total data in bytes |
| order.esim.totalDuration | Number | Validity in days |
| order.esim.expiredTime | String | Expiry date (ISO 8601) |
When isPending is true, the eSIM is still being provisioned. Keep polling every 15-30 seconds until isPending becomes false.
Examples
Basic Order Creation
const crypto = require('crypto');
const { v4: uuidv4 } = require('uuid');
async function createOrder(packageCode, quantity = 1) {
const accessCode = 'esf_your_access_code';
const secretKey = 'sk_your_secret_key';
// Simple order data - just packageCode!
const orderData = {
packageCode: packageCode,
quantity: quantity
};
// Generate HMAC headers
const timestamp = Date.now().toString();
const requestId = uuidv4();
const requestBody = JSON.stringify(orderData);
const signData = timestamp + requestId + accessCode + requestBody;
const signature = crypto.createHmac('sha256', secretKey)
.update(signData)
.digest('hex')
.toUpperCase();
const response = await fetch('https://esimfly.net/api/v1/business/esims/order', {
method: 'POST',
headers: {
'RT-AccessCode': accessCode,
'RT-RequestID': requestId,
'RT-Timestamp': timestamp,
'RT-Signature': signature,
'Content-Type': 'application/json'
},
body: requestBody
});
const result = await response.json();
if (result.success) {
console.log(`Order created: ${result.orderReference}`);
console.log(`New balance: $${result.newBalance}`);
console.log(`Profit earned: $${result.profit}`);
// Save eSIM details for customer
result.esims.forEach((esim, index) => {
console.log(`eSIM ${index + 1}: ${esim.iccid}`);
console.log(`QR Code: ${esim.qrCodeUrl}`);
});
}
return result;
}
Multiple eSIM Order
const result = await createOrder("europe-5gb-30days", 5);
if (result.success) {
console.log(`Created ${result.esims.length} eSIMs`);
console.log(`Total cost: $${result.final_price}`);
console.log(`Total profit: $${result.profit}`);
}
Check Order Status
async function checkOrderStatus(orderReference) {
const response = await fetch(
`https://esimfly.net/api/v1/business/esims/order?orderReference=${orderReference}`,
{
headers: generateHMACHeaders() // Your HMAC function
}
);
const data = await response.json();
if (data.success) {
console.log(`Order Status: ${data.order.status}`);
console.log(`Payment Status: ${data.order.paymentStatus}`);
console.log(`eSIM Status: ${data.order.esim.status}`);
}
return data;
}
Python Example
import hashlib
import hmac
import json
import time
import uuid
import requests
def create_esim_order(package_code, quantity=1):
access_code = 'esf_your_access_code'
secret_key = 'sk_your_secret_key'
# Simple order data - just packageCode!
order_data = {
'packageCode': package_code,
'quantity': quantity
}
# Generate HMAC headers
timestamp = str(int(time.time() * 1000))
request_id = str(uuid.uuid4())
request_body = json.dumps(order_data)
sign_data = timestamp + request_id + access_code + request_body
signature = hmac.new(
secret_key.encode('utf-8'),
sign_data.encode('utf-8'),
hashlib.sha256
).hexdigest().upper()
headers = {
'RT-AccessCode': access_code,
'RT-RequestID': request_id,
'RT-Timestamp': timestamp,
'RT-Signature': signature,
'Content-Type': 'application/json'
}
response = requests.post(
'https://esimfly.net/api/v1/business/esims/order',
headers=headers,
json=order_data
)
data = response.json()
if data['success']:
print(f"Order created: {data['orderReference']}")
print(f"New balance: ${data['newBalance']}")
print(f"Profit: ${data['profit']}")
for i, esim in enumerate(data['esims']):
print(f"eSIM {i+1}: {esim['iccid']}")
return data
# Example usage
result = create_esim_order(
package_code='merhaba-7days-1gb',
quantity=1
)
Error Responses
400 Bad Request
Missing packageCode:
{
"success": false,
"message": "Missing required field: packageCode",
"code": "MISSING_PACKAGE_CODE"
}
Invalid package code:
{
"success": false,
"message": "Invalid package code. Package not found in database.",
"code": "INVALID_PACKAGE"
}
Price mismatch (security validation):
{
"success": false,
"message": "Invalid price submitted",
"code": "PRICE_MISMATCH"
}
Insufficient balance:
{
"success": false,
"message": "Insufficient balance",
"code": "INSUFFICIENT_BALANCE",
"currentBalance": 25.50,
"requiredBalance": 27.20,
"needToLoad": 1.70
}
409 Conflict
Duplicate request still in flight (same idempotency_key, or same package without a key):
{
"success": false,
"message": "A request with this idempotency key is already being processed",
"code": "DUPLICATE_REQUEST"
}
401 Unauthorized
Missing authentication:
{
"success": false,
"error": "Authentication required",
"message": "Please provide either Bearer token or complete HMAC signature authentication"
}
Invalid HMAC signature:
{
"success": false,
"error": "Invalid HMAC signature",
"code": "INVALID_SIGNATURE"
}
403 Forbidden
Not a business account:
{
"success": false,
"error": "Invalid user or not a business account",
"code": "INVALID_USER"
}
500 Internal Server Error
Order processing error:
{
"success": false,
"message": "Failed to process order",
"code": "ORDER_PROCESSING_ERROR"
}
Order Flow
- Get Packages: Use
/packagesendpoint to get available packages - Select Package: Choose a package and extract the required fields
- Create Order: Submit order with exact package details
- Balance Deduction: Your balance is automatically deducted
- eSIM Provisioning: eSIM is provisioned from the provider
- Response: Receive eSIM details or pending status
- Status Check: Monitor order status if initially pending
Security Features
Price Validation
- Server-side price validation prevents price manipulation
- Submitted price must match the current package price
- Prices are validated against live package data
Balance Safety
- Balance is checked before processing
- Atomic transaction ensures balance consistency
- No partial deductions on failed orders
Request Deduplication
- Each request requires a unique UUID v4 request ID
- Duplicate request IDs are rejected to prevent accidental reorders
- Request IDs are stored for 24 hours
Best Practices
1. Get Fresh Package Data
// Get available packages
const packages = await getPackages({ search: 'Turkey' });
const selectedPackage = packages.data.packages[0];
// Create order with just package code
const order = await createOrder(selectedPackage.package_code);
// Or with quantity
const order = await createOrder(selectedPackage.package_code, 3);
2. Handle Pending Orders
const result = await createOrder(orderData);
if (result.status === 'pending_details') {
console.log('Order created, waiting for eSIM details...');
// Check status after a delay
setTimeout(async () => {
const status = await checkOrderStatus(result.orderReference);
if (status.order.status === 'completed') {
console.log('eSIM details now available!');
}
}, 60000); // Check after 1 minute
}
3. Monitor Your Balance
const result = await createOrder(orderData);
if (result.success) {
console.log(`Order total: $${result.final_price}`);
console.log(`Profit earned: $${result.profit}`);
console.log(`Remaining balance: $${result.newBalance}`);
// Alert if balance is low
if (result.newBalance < 10) {
console.warn('Low balance! Consider topping up.');
}
}
4. Store eSIM Details Securely
const result = await createOrder(orderData);
if (result.success) {
// Store eSIM details in your database
for (const esim of result.esims) {
await storeEsimForCustomer({
customerEmail: customerEmail,
packageName: result.packageName,
iccid: esim.iccid,
qrCodeUrl: esim.qrCodeUrl,
installUrl: esim.directAppleInstallUrl,
orderReference: result.orderReference
});
}
}
Package Code Requirements
Important Notes
- Use the exact
package_codefrom the packages endpoint - Do not modify or prefix package codes
- Package codes are handled automatically by our system
- All package formats are supported
Quantity Limits
- Minimum: 1 eSIM per order
- Maximum: 10 eSIMs per order
- Multiple Orders: For larger quantities, create multiple orders
- Same ICCID: Each eSIM in an order has a unique ICCID
Rate Limiting
This endpoint is subject to:
- 1000 requests per hour per API key
- Rate limit headers included in responses
Troubleshooting
Common Issues
-
Invalid Package Code
- Verify package code exists in packages endpoint
- Use exact
package_codefrom packages response - Package availability may change
-
Insufficient Balance
- Check your balance with
/balanceendpoint - Top up your account before placing orders
- Check your balance with
-
Price Mismatch Error (Standard Mode only)
- Only occurs if you send
pricefield manually - Use simplified mode (packageCode only) to avoid this
- Only occurs if you send
Error Handling Example
try {
const result = await createOrder('merhaba-7days-1gb');
if (!result.success) {
switch (result.code) {
case 'INSUFFICIENT_BALANCE':
console.error(`Need to top up: $${result.needToLoad}`);
break;
case 'INVALID_PACKAGE':
console.error('Package no longer available');
break;
default:
console.error('Order failed:', result.message);
}
}
} catch (error) {
console.error('Network error:', error.message);
}
Legacy Integration Note
If you have an existing integration that sends additional fields like packageName, price, duration, or flagUrl, it will continue to work without any changes. The API is fully backward compatible.
For new integrations, we recommend using only packageCode (and optionally quantity) as shown in the examples above for simpler implementation.
Support
For technical support or questions about the order API:
- Email: support@esimfly.net
- Response Time: 24 hours
- Documentation: https://docs.esimfly.net