Skip to main content

Process Topup Order

Process a top-up order for a specific eSIM with automatic profit calculation.

Endpoint​

POST /api/v1/business/topup/order

AI prompt — Process Topup Order

Open raw .txt

Paste into ChatGPT, Claude, Cursor, Copilot or any coding agent to generate this part of your integration. Built-in recommendation: One call per customer action with a per-ICCID lock; verify timeouts via the usage query instead of blind retries.

Show prompt text (51 lines)
# eSIMfly Business API — Process Topup Order (POST /topup/order) — prompt for AI coding assistants

TASK: implement adding data to an existing eSIM. Runs inside the customer request (after the customer
picked a package from GET /topup/packages). Update our local eSIM record and balance mirror from the response.

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/topup/order
  Body: { "iccid": "8943108170002570328", "packageCode": "<package_code from /topup/packages>", "quantity": 1 }
  Do NOT send price/packageName — the API looks them up and charges the current cost.

ELIGIBILITY: eSIM status ACTIVE, DEPLETED or USED_EXPIRED (case-insensitive); NEW is also allowed for
eSIMfly-provided eSIMs. Anything else -> 400 ESIM_NOT_TOPPABLE.

RESPONSE 200
  { success: true, message, orderReference: "topup_...", iccid, packageName, newBalance, currency,
    status: "completed", amount, profit, processing_time_ms,
    esimData: { newTotalVolumeGB, newRemainingVolumeGB, expiredTime } }

ERRORS
  400 MISSING_FIELDS | ESIM_NOT_FOUND | ESIM_NOT_TOPPABLE | INSUFFICIENT_BALANCE | INVALID_TOPUP_PACKAGE | TOPUP_NOT_SUPPORTED
  500 "Failed to process topup order"

INTEGRATION PATTERN
1. Flow: customer opens top-up screen -> GET /topup/packages?iccid (cached 10 min) -> customer picks ->
   POST /topup/order -> update our esims row (total/remaining GB, expiry) from esimData and our balance
   mirror from newBalance -> store orderReference on a topups table for support.
2. This endpoint has NO idempotency key. On a network timeout do NOT blindly retry: first call
   GET /esims/usage/query?iccid=<iccid> and compare data.total_mb with the value you stored before the
   top-up; if it increased, the top-up went through. Use a fresh RT-RequestID on any retry.
3. Wrap the customer action in a local lock per ICCID so a double-click cannot send two top-ups.
4. Map INSUFFICIENT_BALANCE / ESIM_NOT_TOPPABLE / INVALID_TOPUP_PACKAGE to clear UI messages.
5. Do not call /balance, /esims or /orders afterwards to "confirm" — the response is authoritative.

DELIVERABLE: topupEsim(iccid, packageCode) in the shared client, per-ICCID lock, timeout verification
via usage query, and the local record updates.

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-TypeStringYesapplication/json

Request Body​

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

FieldTypeRequiredDescription
iccidStringYeseSIM ICCID to top up
packageCodeStringYesPackage code from topup packages endpoint
quantityIntegerNoNumber of packages (default: 1)

The API automatically handles:

  • ✅ Package name lookup
  • ✅ Price calculation with your custom markup
  • ✅ Topup compatibility validation

eSIM Eligibility​

An eSIM can be topped up when its status is one of the following (status comparison is case-insensitive, so ACTIVE, Active, and active are all accepted):

StatusToppable
ACTIVE✅ All providers
DEPLETED✅ All providers
USED_EXPIRED✅ All providers
NEW (not yet activated)✅ eSIMfly-provided eSIMs only

If the eSIM is in any other status, the API returns ESIM_NOT_TOPPABLE (400).

Example Requests​

Single Topup:

{
"iccid": "8943108170002570328",
"packageCode": "turkey-7days-1gb-topup"
}

Multiple Topups:

{
"iccid": "8943108170002570328",
"packageCode": "TOPUP_PLGJ7UB3C",
"quantity": 2
}

Response​

Success Response (200 OK)​

USD User Example:

{
"success": true,
"message": "eSIM top-up processed successfully",
"orderReference": "topup_1755559183090_vyf9w",
"iccid": "8943108170002570328",
"packageName": "Turkey 1GB 7Days",
"newBalance": 550.68,
"currency": "USD",
"status": "completed",
"amount": 2.45,
"profit": 0.23,
"processing_time_ms": 3724,
"esimData": {
"newTotalVolumeGB": 8.0,
"newRemainingVolumeGB": 8.0,
"expiredTime": "February 13, 2026 at 11:27 PM"
}
}

IQD User Example:

{
"success": true,
"message": "eSIM top-up processed successfully",
"orderReference": "topup_1755559183090_vyf9w",
"iccid": "8943108170002570328",
"packageName": "Iraq 1GB 7Days",
"newBalance": 726895,
"currency": "IQD",
"status": "completed",
"amount": 4856,
"profit": 456,
"processing_time_ms": 3724,
"esimData": {
"newTotalVolumeGB": 8.0,
"newRemainingVolumeGB": 8.0,
"expiredTime": "February 13, 2026 at 11:27 PM"
}
}

Response Fields​

FieldTypeDescription
successBooleanOperation success status
messageStringSuccess message
orderReferenceStringUnique order reference for tracking
iccidStringeSIM ICCID that was topped up
packageNameStringName of the topup package applied
newBalanceNumberYour updated account balance in preferred currency
currencyStringCurrency code based on the account: USD or IQD, or EUR for an enterprise account
statusStringOrder status (always "completed" for successful orders)
amountNumberTotal amount charged in user's preferred currency
profitNumberYour profit from this transaction in user's preferred currency
processing_time_msIntegerProcessing time in milliseconds
esimDataObjectUpdated eSIM information

eSIM Data Object​

FieldTypeDescription
newTotalVolumeGBNumberNew total data capacity in GB after topup
newRemainingVolumeGBNumberNew remaining data in GB (total - used)
expiredTimeStringHuman-readable expiry date and time

Error Responses​

400 Bad Request​

Missing required fields:

{
"success": false,
"message": "Missing required fields: iccid, packageCode",
"code": "MISSING_FIELDS"
}

eSIM not found:

{
"success": false,
"error": "eSIM not found or access denied",
"code": "ESIM_NOT_FOUND"
}

eSIM cannot be topped up:

{
"success": false,
"message": "eSIM cannot be topped up. Current status: EXPIRED",
"code": "ESIM_NOT_TOPPABLE"
}

Only ACTIVE, DEPLETED, or USED_EXPIRED eSIMs can be topped up (case-insensitive). eSIMs in NEW status can also be topped up when the eSIM is provided by eSIMfly.

Insufficient balance:

{
"success": false,
"error": "Insufficient balance",
"message": "Your current balance is $2.50. Required: $3.68",
"code": "INSUFFICIENT_BALANCE"
}

Invalid topup package:

{
"success": false,
"message": "Invalid top-up package code. Package not found.",
"code": "INVALID_TOPUP_PACKAGE"
}

Topup not supported:

{
"success": false,
"error": "Top-ups not available for this eSIM",
"code": "TOPUP_NOT_SUPPORTED"
}

401 Unauthorized​

See Authentication documentation for authentication errors.

500 Internal Server Error​

{
"success": false,
"error": "Failed to process topup order"
}

Examples​

Basic Topup Order​

// Simple - just iccid and packageCode
const orderData = {
iccid: '8943108170002570328',
packageCode: 'TOPUP_PLGJ7UB3C'
};

const response = await fetch(
'https://esimfly.net/api/v1/business/topup/order',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
...generateHMACHeaders() // Your HMAC function
},
body: JSON.stringify(orderData)
}
);

const result = await response.json();
if (result.success) {
console.log(`Topup successful! Order: ${result.orderReference}`);
console.log(`New balance: $${result.newBalance}`);
console.log(`Profit earned: $${result.profit}`);
console.log(`eSIM now has ${result.esimData.newTotalVolumeGB}GB total`);
}

Error Handling​

try {
const response = await fetch(/* ... */);
const result = await response.json();

if (!result.success) {
switch (result.code) {
case 'INSUFFICIENT_BALANCE':
console.log('Need to add funds to account');
break;
case 'ESIM_NOT_TOPPABLE':
console.log('eSIM cannot be topped up in current state');
break;
case 'INVALID_TOPUP_PACKAGE':
console.log('Package not valid for this eSIM');
break;
default:
console.log('Topup failed:', result.error);
}
}
} catch (error) {
console.error('Request failed:', error);
}

Important Notes​

Automatic Processing​

  • Balance deduction happens automatically upon successful topup
  • Profit calculation is handled server-side using your account settings
  • eSIM data is updated in real-time

Provider Compatibility​

  • Cross-provider topups are supported (e.g., topping up any eSIM with any compatible package)
  • Provider detection is automatic based on package code format
  • All provider-specific logic is handled transparently

Security & Validation​

  • Package prices are validated server-side to prevent manipulation
  • Only eSIMs you own can be topped up
  • Balance checks are performed before processing
  • All transactions are logged and auditable

Best Practices​

  1. Get packages first: Always call Get Topup Packages to get available options
  2. Use exact codes: Use the exact package_code returned from the packages endpoint
  3. Handle errors gracefully: Always check for insufficient balance and eSIM status errors
  4. Store order reference: Keep orderReference for customer support and tracking
  5. Monitor profit: Track your earnings using the profit field in responses

Relationship to Other Endpoints​

Legacy Integration Note​

If you have an existing integration that sends additional fields like packageName or price, it will continue to work without any changes. The API is fully backward compatible.

For new integrations, we recommend using only iccid and packageCode (and optionally quantity) as shown in the examples above for simpler implementation.