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
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.
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 | application/json |
Request Body
Simply provide the ICCID and package code - everything else is handled automatically.
| Field | Type | Required | Description |
|---|---|---|---|
| iccid | String | Yes | eSIM ICCID to top up |
| packageCode | String | Yes | Package code from topup packages endpoint |
| quantity | Integer | No | Number 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):
| Status | Toppable |
|---|---|
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
| Field | Type | Description |
|---|---|---|
| success | Boolean | Operation success status |
| message | String | Success message |
| orderReference | String | Unique order reference for tracking |
| iccid | String | eSIM ICCID that was topped up |
| packageName | String | Name of the topup package applied |
| 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 |
| status | String | Order status (always "completed" for successful orders) |
| amount | Number | Total amount charged in user's preferred currency |
| profit | Number | Your profit from this transaction in user's preferred currency |
| processing_time_ms | Integer | Processing time in milliseconds |
| esimData | Object | Updated eSIM information |
eSIM Data Object
| Field | Type | Description |
|---|---|---|
| newTotalVolumeGB | Number | New total data capacity in GB after topup |
| newRemainingVolumeGB | Number | New remaining data in GB (total - used) |
| expiredTime | String | Human-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
- Get packages first: Always call Get Topup Packages to get available options
- Use exact codes: Use the exact
package_codereturned from the packages endpoint - Handle errors gracefully: Always check for insufficient balance and eSIM status errors
- Store order reference: Keep
orderReferencefor customer support and tracking - Monitor profit: Track your earnings using the
profitfield in responses
Relationship to Other Endpoints
- Prerequisites: Get eSIMs to get ICCID, then Get Topup Packages
- Follow-up: Use Get Orders to track order history
- Account: Check Get Balance for current balance after topup
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.