NexaEsim Partner API
Build eSIM catalog, ordering, delivery, and account management into your website or application.
https://nexaesim.com/api/partner/v1All requests and responses use UTF-8 JSON over HTTPS. All catalog prices, order amounts, top-ups, and wallet balances exposed by the Partner API use USD.
Authentication
NexaEsim separates account access from commerce access. Use a bearer token to manage the account, and use an API key with an approved source IP for catalog and order operations.
Authorization: Bearer <token>
Tokens are issued by /auth/login.
X-API-Key: nexa_...
The source IP must match an active whitelist rule.
Register an account
/auth/register{
"companyName": "Example Travel",
"contactName": "Jane Doe",
"email": "api@example.com",
"phone": "+1 555 000 0000",
"password": "minimum-10-characters"
}
Sign in
/auth/login{
"email": "api@example.com",
"password": "minimum-10-characters"
}
Quick start
Before placing an order, create an API key and webhook signing secret in the portal, then add your server's public IP to the whitelist.
curl -X POST "https://nexaesim.com/api/partner/v1/package/load" \
-H "Content-Type: application/json" \
-H "X-API-Key: nexa_your_key" \
-d "{}"Countries and regions
Location endpoints return simple English names and stable identifiers. Country records use ISO 3166-1 alpha-2 and alpha-3 codes. Regional and global records use their NexaEsim coverage ID and catalog code because ISO country codes do not apply to multi-country areas.
List countries
/location/countries{}{
"name": "United States",
"countryCode": "US",
"iso2": "US",
"iso3": "USA",
"coverageIds": [216, 310]
}A country can have several coverage IDs when different carrier groups or network products are available.
List continents
/location/continents{}Returns the basic NexaEsim continent names and every active coverage ID mapped to each continent.
List regions
/location/regions{}Each item includes coverageId, a basic name, catalog code, locationType (continent, region, or global), and the included country codes.
Catalog and coverage
Load coverage
/package/coverageReturns active countries and regions with coverage codes, ISO2/ISO3 country identifiers, included countries, operators, carriers, APN details, and available network technologies.
X-API-Key: nexa_your_key
Content-Type: application/json
{}
Load packages
/package/loadReturns active packages grouped by coverage. wholesalePrice is the amount charged to your wallet; retailPrice is the current public catalog reference.
{
"errorCode": 0,
"message": "Load packages successful",
"data": {
"listPackages": [{
"product": { "id": 1001, "code": "US", "name": "United States" },
"packages": [{
"id": 13726,
"code": "US-1GB-7D",
"name": "United States 1 GB / 7 Days",
"wholesalePrice": 4.80,
"retailPrice": 5.77,
"currency": "USD",
"dataAmount": 1,
"dataUnit": "GB",
"duration": 7
}]
}]
}
}
Networks and speeds
Load coverage networks
/coverage/networks{
"coverageId": 216,
"countryCode": "US",
"page": 1,
"pageSize": 100
}coverageId and countryCode are optional filters. The response includes country ISO2/ISO3 codes, APN, remarks, carrier names, the original networkTechnologies, normalized networkSpeeds, and maximumNetworkSpeed. Network data is returned from the current NexaEsim catalog.
{
"networkId": 10001,
"coverageId": 216,
"coverageName": "United States",
"countryName": "United States",
"iso2": "US",
"iso3": "USA",
"apnValue": "globaldata",
"carriers": [{ "name": "T-Mobile" }],
"networkTechnologies": [{ "name": "5G" }, { "name": "4G" }],
"networkSpeeds": ["5G", "4G"],
"maximumNetworkSpeed": "5G"
}Recommended database structure
This is a suggested partner-side model for storing NexaEsim API responses. The names below are examples; partners do not need to reproduce NexaEsim's internal database. Use Unicode text columns and store monetary values as DECIMAL(19,6) in USD.
iso2 for countries, coverageId for coverages, networkId for network rows, packageId for packages, and orderCode for orders. Names, prices, and network availability can change and must not be used as keys.Catalog tables
| Table | Primary key | Main columns | API source |
|---|---|---|---|
countries | iso2 CHAR(2) | iso3, Unicode name, is_active | /location/countries |
coverages | coverage_id BIGINT | code, name, coverage_type, location_type, country_count | /package/coverage |
coverage_countries | (coverage_id, iso2) | Foreign keys to coverages and countries | /package/coverage |
coverage_networks | network_id BIGINT | coverage_id, iso2, APN, remarks, SIM type, maximum speed | /coverage/networks |
network_carriers | (network_id, carrier_name) | Carrier name | /coverage/networks |
network_technologies | (network_id, technology_name) | 5G, 4G/LTE, 3G or other returned technology | /coverage/networks |
packages | package_id BIGINT | coverage_id, code, name, prices, data, duration, policy, status | /package/load |
Order and delivery tables
| Table | Primary/unique key | Required data |
|---|---|---|
partner_orders | order_code; unique idempotency_key | Status, currency, amount snapshots, callback URL, created/updated timestamps |
partner_order_items | Local item ID | order_code, package_id, quantity, package name and unit-price snapshots |
partner_esims | iccid | order_code, package, QR data, installation URL, SM-DP+, status and expiry |
webhook_receipts | Unique body_sha256 | Order code, signature, raw body, received/processed time and result |
Relationships
countries (iso2) 1 -- * coverage_countries * -- 1 coverages (coverage_id)
coverages 1 -- * coverage_networks 1 -- * network_carriers
1 -- * network_technologies
coverages 1 -- * packages
partner_orders 1 -- * partner_order_items * -- 1 packages
partner_orders 1 -- * partner_esims
Minimal SQL example
CREATE TABLE countries (
iso2 CHAR(2) PRIMARY KEY,
iso3 CHAR(3) NOT NULL UNIQUE,
name NVARCHAR(200) NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE
);
CREATE TABLE coverages (
coverage_id BIGINT PRIMARY KEY,
code NVARCHAR(255),
name NVARCHAR(500) NOT NULL,
coverage_type SMALLINT,
location_type VARCHAR(20) NOT NULL,
country_count INT NOT NULL DEFAULT 0,
is_active BOOLEAN NOT NULL DEFAULT TRUE
);
CREATE TABLE coverage_countries (
coverage_id BIGINT NOT NULL REFERENCES coverages(coverage_id),
iso2 CHAR(2) NOT NULL REFERENCES countries(iso2),
PRIMARY KEY (coverage_id, iso2)
);
CREATE TABLE packages (
package_id BIGINT PRIMARY KEY,
coverage_id BIGINT NOT NULL REFERENCES coverages(coverage_id),
code NVARCHAR(255), name NVARCHAR(500) NOT NULL,
wholesale_price DECIMAL(19,6) NOT NULL,
retail_price DECIMAL(19,6), currency CHAR(3) NOT NULL DEFAULT 'USD',
data_amount DECIMAL(19,6), data_unit VARCHAR(16), duration_days INT,
data_type SMALLINT, unlimited_policy NVARCHAR(1000),
unlimited_bandwidth NVARCHAR(100), is_active BOOLEAN NOT NULL DEFAULT TRUE,
updated_at_utc TIMESTAMP NOT NULL
);
The sample uses portable names. Adapt BOOLEAN, TIMESTAMP, identity columns, and Unicode types for SQL Server, PostgreSQL, MySQL, or your ORM.
Safe synchronization
- Upsert countries by
iso2, then coverages bycoverageId. - Replace each coverage's country links inside one transaction.
- Read every page of
/coverage/networks; upsert bynetworkIdand replace its carrier and technology children. - Upsert packages by
packageId. Refresh current prices while preserving price snapshots in historical order items. - Only mark unseen rows inactive after all endpoints and pages succeed. Never deactivate catalog data after a partial sync.
Order and callback storage
- Persist one unique idempotency key before calling
/order/create. Reuse the same key and payload after a timeout. - Save
orderCodeimmediately and query that order before attempting another purchase after an uncertain response. - Verify the HMAC against the exact raw UTF-8 callback body before parsing it.
- Insert the callback body hash with a unique constraint, then update the order and eSIM rows in one transaction before returning HTTP 2xx.
- Treat QR codes and installation URLs as secrets; never write them to logs or analytics.
Orders
Create an order
/order/createThe idempotency key is mandatory and unique to your account. Repeating the same key with the same items and callback URL returns the original order. Reusing it with a different payload returns HTTP 409.
X-API-Key: nexa_your_key
X-Idempotency-Key: YOUR-UNIQUE-ORDER-001
Content-Type: application/json
{
"orderItems": [
{ "packageId": 13726, "quantity": 1 }
],
"callbackUrl": "https://partner.example.com/api/esim/callback"
}
Query an order
/order/query{ "orderCode": "PO20260912..." }The order code is issued by NexaEsim and remains the stable reference for queries and callbacks.
Signed callbacks
NexaEsim sends the completed eSIM details to your HTTPS callback URL. The Signature header is the uppercase hexadecimal HMAC-SHA256 digest of the exact UTF-8 request body, generated with your webhook secret.
Signature: UPPERCASE_HEX_HMAC_SHA256
Content-Type: application/json
{
"orderCode": "PO20260912...",
"orderStatus": 6,
"simInfos": [{
"qrCode": "LPA:...",
"qrUrl": "https://...",
"iccid": "...",
"installationUrl": "https://esimsetup.apple.com/...",
"expiredTime": "2026-12-31T00:00:00Z",
"smDp": "...",
"status": 0,
"packageId": 13726
}]
}eSIM information
Load customer eSIMs
/sim-info/load{ "email": "customer@example.com", "orderCode": "PO20260912..." }Query eSIM usage
/sim-info/query-info{ "iccid": "..." }Attach customer information
/hook/order/inform{
"orderCode": "PO20260912...",
"email": "customer@example.com",
"fullName": "Customer Name",
"phone": "+1 555 000 0000",
"lastPaymentMoney": 9.62
}Queries return only orders and ICCIDs owned by the authenticated partner account.
Account API
These endpoints use a bearer token and support portal functions programmatically.
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /account | Account profile and wallet balance |
| GET | /packages | Searchable, paginated catalog |
| GET | /pricing | Price definitions and currency |
| GET / POST | /purchases | List or create purchases |
| GET | /wallet | Balance and wallet transactions |
| GET / POST | /wallet/topups | List or request a positive USD top-up; NexaEsim applies no top-up limit |
| GET / POST / DELETE | /api-keys | Manage API credentials |
| GET / POST / DELETE | /ip-whitelist | Manage trusted IP rules |
| GET | /policies | Current partner policies |
Responses and order status
Commerce endpoints use a consistent envelope.
{ "errorCode": 0, "message": "Operation successful", "data": {} }errorCode = 0 means success. errorCode = -1 means the request was rejected or could not be completed.
| Status | Meaning |
|---|---|
| 1 | Created |
| 2 | Awaiting payment |
| 3 | Provisioning |
| 4 | Payment failed |
| 5 | Cancelled |
| 6 | Completed |
| 11 | Provisioning cancelled |
| 12 | Provisioning failed |
Errors and safe retries
| HTTP | Meaning | Partner action |
|---|---|---|
| 400 | Invalid package, quantity, callback URL, or request body. | Correct the request before retrying. |
| 401 | Invalid credential, inactive account, or source IP is not whitelisted. | Fix authentication or the whitelist. |
| 409 | The idempotency key belongs to a different payload. | Keep the original key for the original order; use a new key only for a deliberate new order. |
| 429 | Rate limit reached. | Wait for Retry-After, then retry with the same idempotency key. |
| 202 | The request may have reached provisioning, but the immediate result is unknown. | Query the returned order code. Do not create a new order. |
| 502 / 5xx | A downstream or temporary server error occurred. | First query the order; if retrying create, reuse the exact same idempotency key and payload. |
Production checklist
Store API keys and webhook secrets in a server-side secret manager. Never expose them in browser or mobile code.
Add only the public IP addresses used by your production servers.
Compute the HMAC from the exact raw body and compare signatures using a constant-time method.
Assign one stable idempotency key to each order attempt and reuse it for safe retries.
Need integration support? Email support@nexaesim.com.