NexaEsim Partner Platform
Developer documentation

NexaEsim Partner API

Build eSIM catalog, ordering, delivery, and account management into your website or application.

Version 1
Production Base URL
https://nexaesim.com/api/partner/v1
All 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.

Account API

Authorization: Bearer <token>
Tokens are issued by /auth/login.

Commerce API

X-API-Key: nexa_...
The source IP must match an active whitelist rule.

Register an account

POST/auth/register
{
  "companyName": "Example Travel",
  "contactName": "Jane Doe",
  "email": "api@example.com",
  "phone": "+1 555 000 0000",
  "password": "minimum-10-characters"
}

Sign in

POST/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

POST/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

POST/location/continents
{}

Returns the basic NexaEsim continent names and every active coverage ID mapped to each continent.

List regions

POST/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

POST/package/coverage

Returns 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

POST/package/load

Returns 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

POST/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.

Stable keys: use 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

TablePrimary keyMain columnsAPI source
countriesiso2 CHAR(2)iso3, Unicode name, is_active/location/countries
coveragescoverage_id BIGINTcode, name, coverage_type, location_type, country_count/package/coverage
coverage_countries(coverage_id, iso2)Foreign keys to coverages and countries/package/coverage
coverage_networksnetwork_id BIGINTcoverage_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
packagespackage_id BIGINTcoverage_id, code, name, prices, data, duration, policy, status/package/load

Order and delivery tables

TablePrimary/unique keyRequired data
partner_ordersorder_code; unique idempotency_keyStatus, currency, amount snapshots, callback URL, created/updated timestamps
partner_order_itemsLocal item IDorder_code, package_id, quantity, package name and unit-price snapshots
partner_esimsiccidorder_code, package, QR data, installation URL, SM-DP+, status and expiry
webhook_receiptsUnique body_sha256Order 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

  1. Upsert countries by iso2, then coverages by coverageId.
  2. Replace each coverage's country links inside one transaction.
  3. Read every page of /coverage/networks; upsert by networkId and replace its carrier and technology children.
  4. Upsert packages by packageId. Refresh current prices while preserving price snapshots in historical order items.
  5. 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 orderCode immediately 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

POST/order/create

The 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

POST/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
  }]
}
Delivery behavior: callbacks are retried up to 12 times with increasing intervals. Store the payload idempotently and return an HTTP 2xx response only after it has been saved successfully.

eSIM information

Load customer eSIMs

POST/sim-info/load
{ "email": "customer@example.com", "orderCode": "PO20260912..." }

Query eSIM usage

POST/sim-info/query-info
{ "iccid": "..." }

Attach customer information

POST/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.

MethodEndpointPurpose
GET/accountAccount profile and wallet balance
GET/packagesSearchable, paginated catalog
GET/pricingPrice definitions and currency
GET / POST/purchasesList or create purchases
GET/walletBalance and wallet transactions
GET / POST/wallet/topupsList or request a positive USD top-up; NexaEsim applies no top-up limit
GET / POST / DELETE/api-keysManage API credentials
GET / POST / DELETE/ip-whitelistManage trusted IP rules
GET/policiesCurrent 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.

StatusMeaning
1Created
2Awaiting payment
3Provisioning
4Payment failed
5Cancelled
6Completed
11Provisioning cancelled
12Provisioning failed

Errors and safe retries

HTTPMeaningPartner action
400Invalid package, quantity, callback URL, or request body.Correct the request before retrying.
401Invalid credential, inactive account, or source IP is not whitelisted.Fix authentication or the whitelist.
409The 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.
429Rate limit reached.Wait for Retry-After, then retry with the same idempotency key.
202The request may have reached provisioning, but the immediate result is unknown.Query the returned order code. Do not create a new order.
502 / 5xxA downstream or temporary server error occurred.First query the order; if retrying create, reuse the exact same idempotency key and payload.

Production checklist

Protect credentials

Store API keys and webhook secrets in a server-side secret manager. Never expose them in browser or mobile code.

Restrict source IPs

Add only the public IP addresses used by your production servers.

Verify every callback

Compute the HMAC from the exact raw body and compare signatures using a constant-time method.

Use idempotency

Assign one stable idempotency key to each order attempt and reuse it for safe retries.

Need integration support? Email support@nexaesim.com.