Skip to main content

Create Order

Create a new eSIM order using your account balance.

Endpoint​

POST /api/v1/business/esims/order

AI prompt — Create Order

Open raw .txt

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.

Building the whole integration? Use the complete prompt for all endpoints instead of combining the per-endpoint ones.

Authentication​

This endpoint requires HMAC authentication. See Authentication for details.

Request Headers​

HeaderTypeRequiredDescription
RT-AccessCodeStringYesYour API access code
RT-RequestIDStringYesUnique request ID (UUID v4)
RT-TimestampStringYesRequest timestamp in milliseconds
RT-SignatureStringYesHMAC-SHA256 signature
Content-TypeStringYesMust be "application/json"

Request Body​

Simply provide the package code - everything else is handled automatically.

FieldTypeRequiredDescription
packageCodeStringYesPackage code from the packages endpoint
quantityIntegerNoNumber of eSIMs to order (default: 1, max: 10)
recurringBooleanNoFor 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_keyStringNo (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​

FieldTypeDescription
successBooleanRequest success status
messageStringHuman-readable status message
orderReferenceStringUnique order reference for tracking
esimIdIntegerPrimary eSIM ID (for single eSIM orders)
packageNameStringOrdered package name
newBalanceNumberYour updated account balance in preferred currency
currencyStringCurrency code based on the account: USD or IQD, or EUR for an enterprise account
lpaStringStringRaw LPA string (e.g., "LPA:1$smdp.address$activation-code") for QR code generation
qrCodeUrlStringBase64-encoded QR code image (data:image/png;base64,...)
directAppleInstallUrlStringDirect iPhone installation URL
paymentMethodStringAlways "balance" for business orders
statusStringOrder status ("completed" or "pending_details")
amountNumberTotal order amount in user's preferred currency
profitNumberYour profit amount in user's preferred currency
final_priceNumberFinal price charged to your balance in preferred currency
processing_time_msIntegerOrder processing time in milliseconds
esimsArrayArray of eSIM details
noteStringAdditional information (for pending orders)

eSIM Object Fields​

FieldTypeDescription
iccidStringeSIM ICCID number
lpaStringStringRaw LPA string for QR code generation (format: "LPA:1$smdp.address$activation-code")
qrCodeUrlStringBase64-encoded QR code image
directAppleInstallUrlStringDirect Apple installation URL
statusStringeSIM status ("New" or "PENDING")
imsiString/nullIMSI of the eSIM profile
msisdnString/nullThe 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_statusString/nullProvider SIM resource status (e.g. AFFECTED = assigned, FREE = released). Not an SM-DP+ status. (Previously named profileStatus.)
esim_statusString/nullHuman-readable lifecycle label: New, Assigned (Not Installed), Installed, Active, Not Active
profile_statusString/nulleSIM profile (BPP) install status (e.g. Enable, Disable)
unlimitedBooleanWhether the plan has unlimited data
total_volumeNumber/nullTotal data in bytes (e.g. 1073741824 = 1 GB)
total_durationNumber/nullValidity in days
expired_timeString/nullExpiry date (ISO 8601)
isPendingBooleanWhether eSIM details are still being processed
Breaking change

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 quantity for multiple eSIMs of one package.
Recommended retry rule

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​

FieldTypeDescription
order.idIntegerOrder ID
order.packageNameStringPackage name
order.packageCodeStringPackage code
order.statusStringOrder status
order.amountNumberOrder amount
order.orderReferenceStringOrder reference
order.paymentMethodStringPayment method
order.paymentStatusStringPayment status
order.esim.iccidStringeSIM ICCID (null if still pending)
order.esim.statusStringeSIM status
order.esim.imsiString/nullIMSI of the eSIM profile
order.esim.msisdnString/nullThe profile's home number (MSISDN) — a roaming-hub number (+48 prefix) for eSIMfly-network eSIMs, not a local number
order.esim.sim_statusString/nullProvider SIM resource status (AFFECTED/FREE) — not an SM-DP+ status
order.esim.esim_statusString/nullHuman-readable lifecycle label (e.g. Assigned (Not Installed), Active)
order.esim.profile_statusString/nulleSIM profile (BPP) install status (Enable/Disable)
order.esim.qrCodeUrlStringQR code URL
order.esim.directAppleInstallUrlStringApple direct install URL
order.esim.directAndroidInstallUrlStringAndroid direct install URL
order.esim.lpaStringStringLPA activation string
order.esim.isPendingBooleantrue if eSIM is still being provisioned
order.esim.unlimitedBooleanWhether the plan has unlimited data
order.esim.totalVolumeNumberTotal data in bytes
order.esim.totalDurationNumberValidity in days
order.esim.expiredTimeStringExpiry 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​

  1. Get Packages: Use /packages endpoint to get available packages
  2. Select Package: Choose a package and extract the required fields
  3. Create Order: Submit order with exact package details
  4. Balance Deduction: Your balance is automatically deducted
  5. eSIM Provisioning: eSIM is provisioned from the provider
  6. Response: Receive eSIM details or pending status
  7. 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_code from 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​

  1. Invalid Package Code

    • Verify package code exists in packages endpoint
    • Use exact package_code from packages response
    • Package availability may change
  2. Insufficient Balance

    • Check your balance with /balance endpoint
    • Top up your account before placing orders
  3. Price Mismatch Error (Standard Mode only)

    • Only occurs if you send price field manually
    • Use simplified mode (packageCode only) to avoid this

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: