curl --request POST \
--url https://sandbox-api.sakawave.example/v1/esims/{profileId}/top-ups \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"packageSlug": "united-states-5gb-30d-pkgus5gb",
"debitAccountId": "01K2Z3AB4D5E6F7G8H9JKMNPQR",
"externalReference": "esim_topup_20260829_001"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
packageSlug: 'united-states-5gb-30d-pkgus5gb',
debitAccountId: '01K2Z3AB4D5E6F7G8H9JKMNPQR',
externalReference: 'esim_topup_20260829_001'
})
};
fetch('https://sandbox-api.sakawave.example/v1/esims/{profileId}/top-ups', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://sandbox-api.sakawave.example/v1/esims/{profileId}/top-ups"
payload = {
"packageSlug": "united-states-5gb-30d-pkgus5gb",
"debitAccountId": "01K2Z3AB4D5E6F7G8H9JKMNPQR",
"externalReference": "esim_topup_20260829_001"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"id": "01K2Z3AB4S5T6V7W8XYZABCDEF",
"kind": "topup",
"status": "completed",
"package": {
"id": "01K2Z3AB4C5D6E7F8GHJKMNPQR",
"code": "PKG-US-5GB",
"name": "United States 5GB"
},
"quantity": 1,
"customerId": null,
"price": {
"amount": "999",
"currency": "USD",
"display": "9.99"
},
"paid": {
"amount": "1568430",
"currency": "NGN",
"display": "15,684.30"
},
"fee": {
"amount": "0",
"currency": "NGN",
"display": "0.00"
},
"refunded": {
"amount": "0",
"currency": "NGN",
"display": "0.00"
},
"rate": "1570.000000000000",
"externalReference": "esim_topup_20260829_001",
"failureReason": null,
"profileIds": [
"01K2Z3AB4R5S6T7V8WXYZABCDE"
],
"completedAt": "2026-08-29T10:30:12.000Z",
"createdAt": "2026-08-29T10:30:00.000Z",
"updatedAt": "2026-08-29T10:30:12.000Z"
}{
"statusCode": 400,
"message": "Must be a valid identifier.",
"code": "validation_failed",
"errors": [
{
"message": "Must be a valid identifier.",
"field": "accountId",
"code": "custom",
"path": [
"accountId"
]
}
]
}{
"statusCode": 401,
"code": "INVALID_API_KEY",
"message": "Invalid or inactive API credentials."
}{
"statusCode": 404,
"code": "resource_not_found",
"message": "Resource not found."
}{
"statusCode": 409,
"code": "resource_conflict",
"message": "The request conflicts with the current resource state."
}{
"statusCode": 429,
"message": "Too many API requests. Try again later.",
"code": "api_key_rate_limit_exceeded",
"retryAfterSeconds": 17
}{
"statusCode": 500,
"code": "internal_server_error",
"message": "An unexpected error occurred."
}{
"statusCode": 503,
"code": "provider_unavailable",
"message": "We could not issue an account number just now. Try again shortly."
}Add data to an eSIM
Extends an eSIM the traveller has already installed. Nothing is reinstalled.
curl --request POST \
--url https://sandbox-api.sakawave.example/v1/esims/{profileId}/top-ups \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"packageSlug": "united-states-5gb-30d-pkgus5gb",
"debitAccountId": "01K2Z3AB4D5E6F7G8H9JKMNPQR",
"externalReference": "esim_topup_20260829_001"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
packageSlug: 'united-states-5gb-30d-pkgus5gb',
debitAccountId: '01K2Z3AB4D5E6F7G8H9JKMNPQR',
externalReference: 'esim_topup_20260829_001'
})
};
fetch('https://sandbox-api.sakawave.example/v1/esims/{profileId}/top-ups', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://sandbox-api.sakawave.example/v1/esims/{profileId}/top-ups"
payload = {
"packageSlug": "united-states-5gb-30d-pkgus5gb",
"debitAccountId": "01K2Z3AB4D5E6F7G8H9JKMNPQR",
"externalReference": "esim_topup_20260829_001"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"id": "01K2Z3AB4S5T6V7W8XYZABCDEF",
"kind": "topup",
"status": "completed",
"package": {
"id": "01K2Z3AB4C5D6E7F8GHJKMNPQR",
"code": "PKG-US-5GB",
"name": "United States 5GB"
},
"quantity": 1,
"customerId": null,
"price": {
"amount": "999",
"currency": "USD",
"display": "9.99"
},
"paid": {
"amount": "1568430",
"currency": "NGN",
"display": "15,684.30"
},
"fee": {
"amount": "0",
"currency": "NGN",
"display": "0.00"
},
"refunded": {
"amount": "0",
"currency": "NGN",
"display": "0.00"
},
"rate": "1570.000000000000",
"externalReference": "esim_topup_20260829_001",
"failureReason": null,
"profileIds": [
"01K2Z3AB4R5S6T7V8WXYZABCDE"
],
"completedAt": "2026-08-29T10:30:12.000Z",
"createdAt": "2026-08-29T10:30:00.000Z",
"updatedAt": "2026-08-29T10:30:12.000Z"
}{
"statusCode": 400,
"message": "Must be a valid identifier.",
"code": "validation_failed",
"errors": [
{
"message": "Must be a valid identifier.",
"field": "accountId",
"code": "custom",
"path": [
"accountId"
]
}
]
}{
"statusCode": 401,
"code": "INVALID_API_KEY",
"message": "Invalid or inactive API credentials."
}{
"statusCode": 404,
"code": "resource_not_found",
"message": "Resource not found."
}{
"statusCode": 409,
"code": "resource_conflict",
"message": "The request conflicts with the current resource state."
}{
"statusCode": 429,
"message": "Too many API requests. Try again later.",
"code": "api_key_rate_limit_exceeded",
"retryAfterSeconds": 17
}{
"statusCode": 500,
"code": "internal_server_error",
"message": "An unexpected error occurred."
}{
"statusCode": 503,
"code": "provider_unavailable",
"message": "We could not issue an account number just now. Try again shortly."
}Error codes
| Status | code | Cause |
|---|---|---|
409 | esim_profile_not_topupable | The eSIM is expired, revoked, or cancelled. |
409 | esim_topup_region_mismatch | The plan covers a different destination. |
409 | esim_fx_quote_required | The account is not USD and no usable quote was supplied. |
401 | INVALID_API_KEY | The key is missing, invalid, inactive, expired, from the other environment, or blocked by its IP allowlist. |
429 | api_key_rate_limit_exceeded | The per-minute limit for this key was exceeded. |
503 | esim_feature_disabled | eSIM is not enabled on this deployment. |
503 | esim_catalog_unavailable | The catalog is empty and could not be refreshed from the provider. |
503 | esim_provider_unavailable | The eSIM provider could not be reached. Nothing was charged. |
Authorizations
A secret API key, sent as Authorization: Bearer sk_test_… in sandbox or Bearer sk_live_… in live.
Path Parameters
eSIM ULID. A 26-character ULID.
26^[0-7][0-9A-HJKMNP-TV-Z]{25}$"01K2Z3AB4C5D6E7F8G9HJKMNPQ"
Body
A 26-character ULID.
26^[0-7][0-9A-HJKMNP-TV-Z]{25}$"01K2Z3AB4C5D6E7F8G9HJKMNPQ"
1 - 120200A 26-character ULID.
26^[0-7][0-9A-HJKMNP-TV-Z]{25}$"01K2Z3AB4C5D6E7F8G9HJKMNPQ"
Required when the account is not USD.
26^[0-7][0-9A-HJKMNP-TV-Z]{25}$"01K2Z3AB4C5D6E7F8G9HJKMNPQ"
Response
Successful response.
A 26-character ULID.
26^[0-7][0-9A-HJKMNP-TV-Z]{25}$"01K2Z3AB4C5D6E7F8G9HJKMNPQ"
purchase, topup completed is the success state. A failure refunds itself.
provisioning, completed, failed, refunded, partially_refunded Show child attributes
Show child attributes
x >= 1A 26-character ULID.
26^[0-7][0-9A-HJKMNP-TV-Z]{25}$"01K2Z3AB4C5D6E7F8G9HJKMNPQ"
The USD list price.
Show child attributes
Show child attributes
What was debited, in the paying account’s own currency.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
The eSIMs this order allocated.
A 26-character ULID.
26^[0-7][0-9A-HJKMNP-TV-Z]{25}$"2026-08-14T09:00:00.000Z"
"2026-08-14T09:00:00.000Z"
"2026-08-14T09:00:00.000Z"