Top-ups
Add more data or more days to an eSIM that the end-user is already using.
How it works
A top-up is an additional purchase against an existing, already-issued eSIM. It does not replace the eSIM, the ICCID, or the QR code — the user keeps the same profile installed on their device. The plan's data allowance and/or validity are simply extended.
- The eSIM must exist and be in
issuedoractivestatus. - Top-up packages are plan-specific: each plan exposes its own compatible list. Always call
GET /v1/esims/{id}/topup-packagesfor the eSIM you want to top up — never reuse a list from another plan. - Not every plan supports top-ups. The
planobject returned byGET /v1/catalogandGET /v1/orders/{id}includessupports_topup: true|false. Iffalse, the top-up endpoints will returntopup_not_supported. - Top-up orders are billed against the same wallet / credit line as new eSIM orders, at your partner tier price.
- A successful top-up emits
order.topup.completed. A failed top-up emitsorder.topup.failed. No customer email is sent by eSimerge for top-ups.
End-to-end flow
1. POST /v1/orders → issue the eSIM (kind: "new")
2. … customer installs and uses the eSIM …
3. GET /v1/esims/{id}/topup-packages → list compatible top-ups for THIS eSIM
4. POST /v1/orders/topup → charge partner & apply top-up
5. Webhook: order.topup.completed → confirm to your system
6. GET /v1/esims/{id}/usage → new totals reflect the top-upGET /v1/esims/{id}/topup-packages
Returns the list of top-up packages compatible with the given eSIM. Prices are already priced in USD and include your tier markup.
curl https://portal.esimerge.com/api/public/v1/esims/esm_esim_AbCdEf/topup-packages \
-H 'Authorization: Bearer esm_sk_test_...'Example response
{
"object": "list",
"esim_id": "esm_esim_AbCdEf...",
"count": 3,
"data": [
{ "id": "topup_test_3gb_7d", "object": "topup_package", "name": "Top-up 3GB / 7d", "type": "fixed", "data_mb": 3072, "is_unlimited": false, "validity_days": 7, "price_sar": 22.50 },
{ "id": "topup_test_5gb_15d", "object": "topup_package", "name": "Top-up 5GB / 15d", "type": "fixed", "data_mb": 5120, "is_unlimited": false, "validity_days": 15, "price_sar": 37.50 },
{ "id": "topup_test_unlim_7d", "object": "topup_package", "name": "Top-up Unlimited / 7d", "type": "unlimited", "data_mb": null, "is_unlimited": true, "validity_days": 7, "price_sar": 45.00 }
]
}Top-up package fields
| Field | Type | Description |
|---|---|---|
id | string | Top-up package identifier. Pass this to POST /v1/orders/topup. |
object | string | Always "topup_package". |
name | string | Display name (e.g. "Top-up 5GB / 15d"). |
type | string | fixed | unlimited |
data_mb | number? | Extra data added to the eSIM. null when type=unlimited. |
is_unlimited | boolean | true for unlimited data top-ups. |
validity_days | number | Extra validity days added to the eSIM from the moment the top-up is applied. |
price_sar | number | Partner price in USD (already includes your tier markup). |
POST /v1/orders/topup
Apply a top-up package to an eSIM. The partner wallet (or credit line) is charged immediately at the listed price_sar. Always send a unique Idempotency-Key so retries don't double-charge.
Request body
esim_id(required) — the target eSIM id (esm_esim_…).topup_package_id(required) — id fromGET /v1/esims/{id}/topup-packages.customer_reference(optional, ≤120 chars) — your internal reference, echoed back.
curl -X POST https://portal.esimerge.com/api/public/v1/orders/topup \
-H 'Authorization: Bearer esm_sk_test_...' \
-H 'Idempotency-Key: 8f0b1f9e-1f70-4a3e-9b3a-2c2bdcf7a1d2' \
-H 'Content-Type: application/json' \
-d '{"esim_id":"esm_esim_AbCdEf","topup_package_id":"topup_test_5gb_15d","customer_reference":"cust_42"}'Example response (201)
{
"id": "esm_order_GhIjKl...",
"object": "order",
"kind": "topup",
"status": "issued",
"parent_order_id": "esm_order_AbCdEf...",
"esim_id": "esm_esim_AbCdEf...",
"topup_package_id": "topup_test_5gb_15d",
"topup_package": {
"id": "topup_test_5gb_15d",
"name": "Top-up 5GB / 15d",
"data_mb": 5120,
"validity_days": 15
},
"amount_sar": 37.50,
"customer_reference": "cust_42",
"created_at": "2026-06-21T08:30:00Z"
}Error codes
| Code | HTTP | Meaning |
|---|---|---|
esim_not_found | 404 | No eSIM with that id belongs to your account. |
topup_not_supported | 409 | The eSIM's plan does not allow top-ups. |
topup_package_unavailable | 404 | The chosen package id is not in the eSIM's compatible list (always refresh from GET /v1/esims/{id}/topup-packages). |
esim_not_topupable | 409 | The eSIM is expired, cancelled, or not yet issued. |
insufficient_funds | 402 | Wallet balance or credit line cannot cover price_sar. |
Webhook events
| Event | When |
|---|---|
order.topup.completed | Top-up applied successfully. Payload includes order_id, parent_order_id, esim_id, iccid, topup_package_id, amount_sar. |
order.topup.failed | Top-up could not be applied. Payload includes esim_id, topup_package_id, code, reason. |