Developer reference
NXTL B2B REST API
Issue eSIMs, manage pooled balances, change network tiers, reconcile usage, and receive signed webhooks — one API for travel platforms, enterprises, and device programs.
Base URL: https://nxtlsim.com/api/v1
API status
Live check against the public health endpoint (no authentication).
Loading…
Quick start
- After approval, create an API key (REST POST /api-keys with keys:write, or WP Admin → NXTL → Partners).
- Call GET /health (no auth) to verify reachability and plugin version.
- Call GET /partner with X-NXTL-Key to confirm the key resolves to your organization and scopes.
- Queue issuance with POST /esims + X-NXTL-Idempotency-Key; poll GET /esim-requests/{job_id} until the job completes.
- Subscribe webhooks with PUT /webhooks so you receive signed esim.* and balance.* events at your HTTPS endpoint.
curl -sS -H "X-NXTL-Key: nxtl_your_secret_here" \ "https://nxtlsim.com/api/v1/partner"
What travel & mobility teams use the API for
- Bind one eSIM per trip or traveller: issue with client_reference (PNR, employee id, device serial) and reconcile via GET /esims and webhooks.
- Fund a shared pool: POST /topup credits customer balance from prepaid partner budget; GET /balance/history feeds finance.
- Operational control: suspend stolen handsets, reactivate after travel, terminate eSIMs — PATCH /esims/{iccid}/status or POST suspend/resume aliases.
- Support & finance: per-ICCID usage (GET …/usage), ledger events (GET …/events), partner-wide summary (GET /usage/summary), and internal invoice rows (GET /invoices) for API-driven top-ups.
- Automation safety: idempotent POST /esims and POST /topup, rate-limit headers, and X-Request-Id on responses for ticket correlation.
Authentication & scopes
All routes except GET /health require the X-NXTL-Key header with your API secret. Secrets are stored hashed at rest; you only see the plaintext once when the key is created.
curl -H "X-NXTL-Key: nxtl_your_secret_here" "https://nxtlsim.com/api/v1/esims"
Scopes: esims:read, esims:write, balance:read, balance:write, keys:read, keys:write, webhooks:read, webhooks:write, usage:read, thresholds:read, thresholds:write, audit:read. A key may use "*" for full access.
All monetary fields render in your selected display currency when you send ?currency=EUR or Accept-Currency: EUR. The canonical USD value is always available alongside (e.g. threshold_value_usd on threshold rules, total_usd on invoices) so you can store the source value for accounting.
Idempotency
For POST /esims and POST /topup, send X-NXTL-Idempotency-Key. Retries with the same key return the original outcome — safe for network retries and queue workers.
curl -X POST \
-H "X-NXTL-Key: ..." \
-H "X-NXTL-Idempotency-Key: partner-job-2026-04-22-001" \
-H "Content-Type: application/json" \
-d '{"nickname":"Vehicle gateway 12","client_reference":"PNR-8F2Q"}' \
"https://nxtlsim.com/api/v1/esims"
B2B issuance fee & balance
Each successful API-issued eSIM is charged a small USD fee from the partner owner’s pooled balance (default 1.99 USD, option nxtl_v2_b2b_issue_fee_usd; set to 0 to disable). POST /esims returns 402 if the wallet total is too low, or if the unallocated pool cannot cover the fee and partner budget cannot top it up automatically.
GET /balance includes customer_total_usd and available_usd (same value): your full “wallet” for capacity planning, plus unallocated_usd (pool not yet on an eSIM) and b2b_issue_fee_usd for the current per-eSIM charge.
Rate limits & tracing
Successful responses include rate-limit headers and a request id for support correlation:
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-ResetX-Request-Id— quote this in support tickets
Tune defaults with the developer filter nxtl_v2_rest_rate_limit_per_key (your NXTL operator can adjust this server-side).
Dashboard vs API
The signed-in customer portal (balance top-up, eSIM list, suspend/resume, exchange/reissue where configured) and the REST API operate on the same ledger and inventory. Actions you take in the UI are reflected in API responses and vice versa.
Currencies
Retail storefronts can display and settle purchases in multiple supported currencies. Partner REST fields for budgets, top-ups, and ledger entries use a normalized accounting unit (USD) so pools, invoices, and automation stay consistent regardless of display currency.
Endpoints
Paths are relative to the base URL above. {iccid} is 18–22 digits. The "Idempotent" column flags routes that honour X-NXTL-Idempotency-Key — safe to retry from a queue worker without double-charging your pool.
| Method | Path | Description | Scope | Idempotent |
|---|---|---|---|---|
GET |
/health |
Public health check. No auth. | — |
— |
GET |
/partner |
Current partner profile (sanitized). | balance:read |
— |
GET |
/quotas |
Single-shot snapshot: balance, lifetime quota, rate limit, issuance fee, subscriptions, key count. | balance:read |
— |
GET |
/usage/summary |
Aggregated usage for partner eSIMs. ?days=7 | esims:read |
— |
GET |
/usage/esims |
Per-eSIM usage rollups. Legacy alias: /usage/lines. | esims:read |
— |
GET |
/usage/cdrs |
Itemised usage events from the ledger (CDR-equivalent). ?iccid=...&page=1 | esims:read |
— |
GET |
/usage/timeseries |
Daily usage + top-up totals. ?days=30 | esims:read |
— |
GET |
/invoices |
Completed internal orders linked to API balance top-ups. | balance:read |
— |
GET |
/esims |
List eSIMs for the partner. | esims:read |
— |
GET |
/lines |
Legacy alias — same response as GET /esims. | esims:read |
— |
POST |
/esims |
Queue new eSIM issuance (per-eSIM fee from pool; 402 if insufficient). Returns job_id. | esims:write |
✓ |
POST |
/esims/estimate |
Pre-flight cost + balance-after for a planned issuance batch. | esims:read |
— |
POST |
/esims/bulk-issue |
Async batch issuance (up to ~1000). Returns 202 Accepted + job_id. | esims:write |
✓ |
GET |
/esims/{iccid} |
Fetch one eSIM by ICCID. | esims:read |
— |
GET |
/esim-requests/{job_id} |
Poll legacy async job status. | esims:read |
— |
GET |
/jobs |
List async jobs (issuance, bulk-tier) with status counts. | esims:read |
— |
GET |
/jobs/{id} |
Fetch one async job: status, summary, per-row outcomes. | esims:read |
— |
PATCH |
/esims/{iccid}/nickname |
Body: {"nickname":"..."} | esims:write |
— |
PATCH |
/esims/{iccid}/tier |
Body: {"tier":"economy|comfort|full"} | esims:write |
— |
PATCH |
/esims/{iccid}/status |
Body: {"action":"suspend|reactivate|terminate"} | esims:write |
— |
POST |
/esims/{iccid}/suspend |
Alias: suspend. Safe to retry — repeated calls converge. | esims:write |
— |
POST |
/esims/{iccid}/resume |
Alias: reactivate. Safe to retry — repeated calls converge. | esims:write |
— |
GET |
/esims/{iccid}/wallet |
Live wallet snapshot for an eSIM. | esims:read |
— |
GET |
/esims/{iccid}/usage |
Usage rows. ?page=1 | esims:read |
— |
GET |
/esims/{iccid}/events |
Ledger events for an eSIM. | esims:read |
— |
POST |
/esims/bulk-status |
Body: {"iccids":["..."]} — up to 100 ICCIDs. | esims:read |
— |
POST |
/esims/bulk-tier |
Body: {"iccids":[...],"tier":"..."} | esims:write |
— |
GET |
/balance |
Partner balances + per-eSIM wallet breakdown. | balance:read |
— |
GET |
/balance/history |
Balance ledger. ?page=1 | balance:read |
— |
POST |
/topup |
Body: {"amount_usd":10} — credit pool from prepaid budget. | balance:write |
✓ |
GET |
/api-keys |
List keys (public ids only). | keys:read |
— |
POST |
/api-keys |
Body: {"label":"..."} — returns secret once. | keys:write |
— |
DELETE |
/api-keys/{public_id} |
Revoke a key. | keys:write |
— |
GET |
/webhooks |
Webhook URL + subscribed event names. | webhooks:read |
— |
PUT |
/webhooks |
Body: {"webhook_url":"https://...","events":["esim.provisioned",...]} | webhooks:write |
— |
GET |
/webhooks/events |
Catalog of every event emitted + your subscription state. | webhooks:read |
— |
GET |
/webhooks/secret |
Reveal active HMAC signing secret + signed-payload spec. | webhooks:read |
— |
GET |
/webhooks/deliveries |
Filterable delivery log (status, event, since, until). | webhooks:read |
— |
POST |
/webhooks/test |
Queue test.ping delivery. | webhooks:write |
— |
POST |
/webhooks/deliveries/{id}/replay |
Replay a stored delivery by id. | webhooks:write |
— |
POST |
/webhooks/{id}/replay |
Legacy alias for delivery replay. | webhooks:write |
— |
GET |
/thresholds |
List alert rules. | thresholds:read |
— |
POST |
/thresholds |
Create a balance/usage alert rule (optionally auto-suspend). | thresholds:write |
— |
PATCH |
/thresholds/{id} |
Update an existing alert rule. | thresholds:write |
— |
DELETE |
/thresholds/{id} |
Remove a rule and its dedupe state. | thresholds:write |
— |
GET |
/audit |
Partner-scoped audit log. ?action=esim.&since=YYYY-MM-DD | audit:read |
— |
Recipes
Three end-to-end flows our most common partner shapes use. Each is copy-pastable curl plus the webhook events you should listen for.
Recipe 1 — Issue an eSIM bound to a booking
Travel platform: one eSIM per PNR, you want the provisioning to happen on booking confirmation and the QR delivered to the traveller before they reach the airport.
# 1. Pre-flight: confirm we can pay for it
curl -X POST -H "X-NXTL-Key: $NXTL_KEY" -H "Content-Type: application/json" \
-d '{"quantity":1,"tier":"international"}' \
"https://nxtlsim.com/api/v1/esims/estimate"
# 2. Issue with a stable Idempotency-Key so a retry returns the same job
curl -X POST -H "X-NXTL-Key: $NXTL_KEY" \
-H "X-NXTL-Idempotency-Key: pnr-8F2Q-issue" \
-H "Content-Type: application/json" \
-d '{
"tier": "international",
"client_reference": "PNR-8F2Q",
"nickname": "Trip 2026-05-12 Lima"
}' \
"https://nxtlsim.com/api/v1/esims"
# 3. Subscribe to esim.provisioned + esim.provision_failed webhooks
# so your booking record can attach the QR or surface an error.
Recipe 2 — Fleet refresh: rotate tiers for 200 devices
Mobility / IoT: at the start of each month you want every car in the EU service area on the comfort tier and everything elsewhere on economy.
# 1. Pull every active eSIM (paginate with ?page=)
curl -H "X-NXTL-Key: $NXTL_KEY" \
"https://nxtlsim.com/api/v1/esims?page=1"
# 2. Bulk-tier the EU subset (server-side cap: 100 ICCIDs per call)
curl -X POST -H "X-NXTL-Key: $NXTL_KEY" \
-H "X-NXTL-Idempotency-Key: tier-rotate-2026-05" \
-H "Content-Type: application/json" \
-d '{
"iccids": ["89...","89..."],
"tier": "comfort"
}' \
"https://nxtlsim.com/api/v1/esims/bulk-tier"
# 3. Listen for esim.tier_changed; reconcile against /audit?action=esim.tier
# if anything looks off in the 24h window after the rotation.
Recipe 3 — Catch a runaway eSIM before it eats the pool
Customer success: a lost or compromised device is racking up usage. Set a threshold rule that auto-suspends when daily usage exceeds the limit, and follow up with an exchange call once the user reports the loss.
# 1. Define an auto-suspend rule (one-time setup per fleet)
curl -X POST -H "X-NXTL-Key: $NXTL_KEY" -H "Content-Type: application/json" \
-d '{
"scope": "esim",
"metric": "line_usage_usd_today",
"operator": ">=",
"threshold_value": 25.00,
"period": "daily",
"action": "suspend",
"enabled": true
}' \
"https://nxtlsim.com/api/v1/thresholds"
# 2. When the rule fires you receive esim.usage_threshold + esim.suspended.
# 3. Replace the SIM and return wallet to the pool in a single call:
curl -X POST -H "X-NXTL-Key: $NXTL_KEY" -H "Content-Type: application/json" \
-d '{"action":"terminate"}' \
"https://nxtlsim.com/api/v1/esims/89.../status"
Webhooks
When a partner webhook URL is configured, NXTL POSTs a JSON envelope to it. Each delivery includes X-NXTL-Signature = HMAC-SHA256(raw_body, signing_secret). The signing secret is configured on the NXTL platform (server-side secret store; the first active secret is used for new signatures) and is shared with your integration team out-of-band; it is not returned by the REST API.
The NXTL POST uses Content-Type: application/json and a raw JSON body (the same bytes you must verify).
Envelope
{
"event": "esim.provisioned",
"timestamp": 1714410000,
"data": {
"iccid": "89359012345678901234",
"esim_id": 9012,
"request_job_id": 4401,
"client_reference": "PNR-8F2Q"
}
}
timestamp is Unix seconds (UTC). data is event-specific. NXTL does not send query parameters; everything is in the JSON body.
Verify signature (receiver pseudo-code)
// $raw = file_get_contents('php://input'); // exact bytes NXTL signed
// $sig = $_SERVER['HTTP_X_NXTL_SIGNATURE'] ?? '';
// $secret = getenv('NXTL_WEBHOOK_SECRET'); // issued by NXTL ops
// $expected = hash_hmac('sha256', $raw, $secret);
// if (!hash_equals($expected, $sig)) { http_response_code(401); exit; }
HTTP response & retries
Return 2xx within a few seconds so we mark the delivery successful. NXTL stores the HTTP status and up to 2000 characters of your response body for debugging — the body is not parsed as an API contract. Non-2xx or network errors trigger exponential backoff retries (up to 5 attempts), then the delivery is dead-lettered.
Event quick reference
PUT /webhooks persists an events array for your records and future filtering; today deliveries are not filtered by that list — any event below may be POSTed whenever a webhook URL is set.
| Event | When | data fields |
|---|---|---|
esim.provisioned |
Async issuance completed successfully. | iccid, esim_id, request_job_id, client_reference |
esim.provision_failed |
Issuance job failed after retries. | request_job_id, client_reference, message |
esim.tier_changed |
Network tier updated via API or dashboard. | iccid, tier |
esim.suspended |
eSIM suspended (API, dashboard, or automation). | iccid, esim_id, user_id |
esim.reactivated |
eSIM reactivated from suspended state. | iccid, esim_id, user_id |
esim.terminated |
eSIM terminated; wallet return may be included. | iccid, line_id, user_id, wallet_returned |
balance.topup |
Partner pool credited from prepaid budget via API. | amount_usd |
test.ping |
Queued by POST /webhooks/test. | message, timestamp (inside data, in addition to envelope timestamp) |
Full examples: request body & recommended response
Below, “Request body” is the exact JSON POSTed by NXTL (pretty-printed). “Recommended response” is what your server should return to acknowledge receipt; you may use 204 No Content or any small JSON body — NXTL only checks the HTTP status for success.
esim.provisioned
Async issuance completed successfully.
Request body (POST from NXTL)
{
"event": "esim.provisioned",
"timestamp": 1714410000,
"data": {
"iccid": "89359012345678901234",
"esim_id": 9012,
"request_job_id": 4401,
"client_reference": "PNR-8F2Q"
}
}
Recommended HTTP response
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{"received":true}
esim.provision_failed
Issuance job failed after retries.
Request body (POST from NXTL)
{
"event": "esim.provision_failed",
"timestamp": 1714410090,
"data": {
"request_job_id": 4401,
"client_reference": "PNR-8F2Q",
"message": "Upstream carrier timeout"
}
}
Recommended HTTP response
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{"received":true}
esim.tier_changed
Network tier updated via API or dashboard.
Request body (POST from NXTL)
{
"event": "esim.tier_changed",
"timestamp": 1714410180,
"data": {
"iccid": "89359012345678901234",
"tier": "comfort"
}
}
Recommended HTTP response
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{"received":true}
esim.suspended
eSIM suspended (API, dashboard, or automation).
Request body (POST from NXTL)
{
"event": "esim.suspended",
"timestamp": 1714410270,
"data": {
"iccid": "89359012345678901234",
"esim_id": 9012,
"user_id": 501
}
}
Recommended HTTP response
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{"received":true}
esim.reactivated
eSIM reactivated from suspended state.
Request body (POST from NXTL)
{
"event": "esim.reactivated",
"timestamp": 1714410360,
"data": {
"iccid": "89359012345678901234",
"esim_id": 9012,
"user_id": 501
}
}
Recommended HTTP response
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{"received":true}
esim.terminated
eSIM terminated; wallet_returned is the amount credited back to the partner pool (USD).
Request body (POST from NXTL)
{
"event": "esim.terminated",
"timestamp": 1714410450,
"data": {
"iccid": "89359012345678901234",
"esim_id": 9012,
"user_id": 501,
"wallet_returned": 12.75
}
}
Recommended HTTP response
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{"received":true}
balance.topup
Partner pool credited from prepaid budget via POST /topup.
Request body (POST from NXTL)
{
"event": "balance.topup",
"timestamp": 1714410540,
"data": {
"amount_usd": 250
}
}
Recommended HTTP response
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{"received":true}
test.ping
Queued by POST /webhooks/test (smoke-test your endpoint).
Request body (POST from NXTL)
{
"event": "test.ping",
"timestamp": 1714410630,
"data": {
"message": "This is a test webhook delivery.",
"timestamp": 1714410480
}
}
Recommended HTTP response
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{"received":true}
Partner lifecycle emails (application received, approved, rejected) are sent separately via email — they are not duplicated on this webhook channel unless your project adds them.
Roadmap & common requests
The table above reflects what the public partner API exposes today. Travel platforms often also ask for:
- Per-destination catalog & dynamic pricing feeds — today surfaced through your retail integration and NXTL operations; ask us if you need a machine-readable catalog API.
- Push usage alerts (threshold webhooks) — combine GET /usage/summary and GET /esims/{iccid}/usage on a schedule until native threshold events exist.
- Subscriber carrier diagnostics — available to support through NXTL; not all fields are exposed on the public REST surface yet.
- Bulk CSV export — use partner dashboard exports where offered, or page through REST list endpoints to build your own extracts.
Bulk eSIM operations (recommended for fleets)
Use the bulk endpoints whenever you need to issue or change more than ~5 eSIMs at once. Each one returns a job id you can poll, and emits the job.completed webhook with per-row outcomes.
POST /esims/bulk-issue
curl -X POST \
-H "X-NXTL-Key: $NXTL_KEY" \
-H "Idempotency-Key: fleet-q2-launch" \
-H "Content-Type: application/json" \
-d '{
"quantity": 50,
"tier": "international",
"client_reference": "fleet-q2",
"metadata": { "department": "logistics" }
}' \
"https://nxtlsim.com/api/v1/esims/bulk-issue"
Response: 202 Accepted with job_id + estimated cost. Pre-flight checks: insufficient pooled balance returns 402 with type=INSUFFICIENT_BALANCE.
POST /esims/estimate
curl -X POST \
-H "X-NXTL-Key: $NXTL_KEY" -H "Content-Type: application/json" \
-d '{ "quantity": 50, "tier": "international" }' \
"https://nxtlsim.com/api/v1/esims/estimate"
Returns total_usd + balance_after_usd + affordable boolean — gate your UX with this before calling bulk-issue.
GET /jobs/{id}
curl -H "X-NXTL-Key: $NXTL_KEY" \ "https://nxtlsim.com/api/v1/jobs/12345"
Status: pending|running|complete|failed. summary { total, succeeded, failed }; result.rows lists per-eSIM outcomes.
Usage analytics
| Endpoint | Returns |
|---|---|
GET /usage/summary | Partner-wide totals (today, MTD, last 7d). |
GET /usage/esims?since=YYYY-MM-DD&iccid=… | Per-eSIM aggregates over a window: usage_usd, usage_events, first/last event. Legacy alias: /usage/lines. |
GET /usage/cdrs?iccid=…&page=1 | Itemised usage events from the ledger (CDR-equivalent), paginated. |
GET /usage/timeseries?days=30 | Daily usage and top-up totals — drive your charts directly off this. |
Webhook management & verification
| Endpoint | Use |
|---|---|
GET /webhooks | Read current URL + subscribed events. |
PUT /webhooks | Update URL and event subscriptions. Unknown events return 400 with rejected[]. |
GET /webhooks/events | Catalog of every event we emit + your subscription state. |
GET /webhooks/secret | Reveal the active HMAC signing secret + signed-payload spec. |
GET /webhooks/deliveries | Filterable delivery log: status, event, since, until. |
POST /webhooks/deliveries/{id}/replay | Replay a single delivery (e.g. after fixing your endpoint). |
POST /webhooks/test | Send a test.ping event so you can verify HMAC end-to-end. |
Verifying signatures (recommended pattern)
NXTL ships two signature headers on every delivery. Always verify using v1 (timestamped, replay-resistant). Reject events older than 5 minutes.
// Node.js (Express)
const crypto = require('crypto');
function verify(req, secret) {
const hdr = req.header('X-NXTL-Signature-V1') || '';
const m = hdr.match(/t=(\d+),v1=([0-9a-f]+)/);
if (!m) return false;
const t = parseInt(m[1], 10);
if (Math.abs(Date.now()/1000 - t) > 300) return false; // replay window
const expected = crypto.createHmac('sha256', secret)
.update(t + '.' + req.rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(m[2], 'hex'));
}
# Python (Flask)
import hmac, hashlib, time, re
def verify(headers, raw_body, secret):
h = headers.get("X-NXTL-Signature-V1", "")
m = re.match(r"t=(\d+),v1=([0-9a-f]+)", h)
if not m: return False
t, sig = int(m.group(1)), m.group(2)
if abs(time.time() - t) > 300: return False
expected = hmac.new(secret.encode(), f"{t}.{raw_body.decode()}".encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, sig)
// PHP
function nxtl_verify($header_v1, $raw_body, $secret) {
if (!preg_match('/t=(\d+),v1=([0-9a-f]+)/', $header_v1, $m)) return false;
if (abs(time() - (int) $m[1]) > 300) return false;
$expected = hash_hmac('sha256', $m[1] . '.' . $raw_body, $secret);
return hash_equals($expected, $m[2]);
}
Alert thresholds
Define rules that fire balance.low / esim.usage_threshold webhooks (and optionally auto-suspend an eSIM) when a metric crosses the configured threshold.
| Endpoint | Use |
|---|---|
GET /thresholds | List all rules. |
POST /thresholds | Create a rule (see schema below). |
PATCH /thresholds/{id} | Update an existing rule. |
DELETE /thresholds/{id} | Remove the rule and its dedupe state. |
Rule schema
{
"scope": "balance" | "esim",
"metric": "balance_below" | "line_usage_pct" | "line_usage_usd_today",
"operator": "<=" | "<" | ">=" | ">" | "=",
"threshold_value": 25.00, // value in `threshold_currency` (default: USD)
"threshold_currency": "EUR", // optional ISO 4217 — server converts to USD on save
"period": "instant" | "daily" | "weekly" | "monthly",
"action": "notify" | "suspend",
"enabled": true,
"notes": {
"cap_per_line_usd": 50, // required for line_usage_pct
"cap_per_line_currency": "EUR", // optional — server converts cap to USD on save
"min_renotify_seconds": 86400 // dedupe window per (rule, key)
}
}
Storage is always USD because the ledger is USD-aligned with the carrier. Send threshold_currency to record the value you typed in another currency — the response (and webhook payload) always echoes the canonical threshold_value_usd alongside.
List response
{
"rules": [
{
"id": 7,
"metric": "balance_below",
"operator": "<=",
"threshold_value": 25.00, // canonical USD (legacy field)
"threshold_value_usd": 25.00, // explicit USD (recommended)
"threshold_value_in": 22.84, // converted to your selected currency
"threshold_value_display": "€22.84", // pre-formatted, ready for UI
"threshold_currency": "EUR", // selected via ?currency=EUR or Accept-Currency: EUR header
"cap_per_line_usd": 50.00,
"cap_per_line_in": 45.68,
"cap_per_line_display": "€45.68",
"action": "notify",
"enabled": 1
}
]
}
Audit log & quotas
| Endpoint | Use |
|---|---|
GET /audit?action=esim&since=YYYY-MM-DD | Partner-scoped audit log (every mutating call). Filter by action prefix, target_type, target_id, time window. |
GET /quotas | Single-shot snapshot: pooled balance, lifetime quota, rate limit, issuance fee, subscribed events, active key count. |
GET /jobs?per_page=50 | List your async jobs (issuance, bulk-tier) with status counts. |
Errors
Every error is returned with a uniform JSON envelope so you can switch on type/code instead of HTTP status alone.
{
"error": true,
"type": "INSUFFICIENT_BALANCE",
"code": "insufficient_balance",
"message": "Pooled balance is below the estimated cost of this batch.",
"doc_url": "https://nxtlsim.com/business-api/errors/insufficient_balance",
"request_id": "5f8b2e0d-…",
"details": { "total_usd": 100.0, "balance_usd": 42.30 }
}
| HTTP | type | Meaning |
|---|---|---|
| 400 | BAD_REQUEST | Invalid body or parameters. |
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 402 | INSUFFICIENT_BALANCE | Pre-flight or live balance check failed. |
| 403 | ACCESS_DENIED | Key is missing the required scope. |
| 404 | NOT_FOUND | Unknown ICCID, job, delivery, key. |
| 409 | QUOTA_EXCEEDED | Lifetime issuance quota exhausted. |
| 429 | RATE_LIMIT_EXCEEDED | Back off using RateLimit-* + Retry-After. |
| 502 | UPSTREAM_ERROR | Upstream carrier or provisioning service error. |