Fulfil a customer order
Attach an eSIM to a flight, stay, tour, relocation, membership or premium-service order. Use your booking or customer reference to reconcile the result.
Issue and manage NXTL data eSIMs, fund a pooled business balance, reconcile usage and receive signed events through one server-to-server API.
Designed for travel platforms, company mobility, customer benefits and compatible-device operations across NXTL-supported destinations.
API access is available to approved NXTL Business accounts.
GET /health
GET /partner
POST /esims/estimate
POST /esims
esim.provisioned or GET /jobs/{id}
The NXTL API removes the hand-off to a separate purchasing process. Issue at the moment connectivity is needed, keep your own reference attached, and bring status, usage and billing information back into your operation.
Attach an eSIM to a flight, stay, tour, relocation, membership or premium-service order. Use your booking or customer reference to reconcile the result.
Issue individually or in bulk, label every line, change network policy and suspend, resume or terminate service from your own system.
Read usage and balance data, follow a consistent ledger, receive signed events and retain request IDs for support investigation.
The portal and API work with the same business account, balance and eSIM inventory—start manually and automate without moving to another platform.
This reference documents the current NXTL B2B API. Use the endpoint groups below for human-readable integration guidance and the published OpenAPI document for machine-readable tooling.
The NXTL B2B REST API provides server-to-server endpoints for programmatic provisioning and management. Before integrating, ensure your environment meets these operational requirements:
API keys belong to an approved NXTL Business account and inherit its available scopes, quota and commercial configuration.
Issuance and API-funded actions use the business account's available balance. Check the current issuance cost and capacity before creating a batch.
Keep API and webhook secrets on a trusted server. Never place them in browser JavaScript, a public repository or a distributed mobile application.
Use an HTTPS endpoint, preserve the raw request body for signature verification and return a 2xx response after safely accepting the event.
This sequence confirms the key, estimates the account impact and queues one eSIM without exposing a secret to the client.
GET /health is the only public operation. Use it to confirm that the API is reachable; it does not prove that your key, balance or quota is ready.
curl -sS "https://nxtlsim.com/api/v1/health"
The response identifies the business account and the permissions available to this key. Store the key as a server-side secret.
curl -sS \
-H "X-NXTL-Key: $NXTL_KEY" \
"https://nxtlsim.com/api/v1/partner"
Read the returned estimate and affordability result before starting a larger issuance request. Current fees and account capacity come from the API; do not duplicate them as permanent constants in your application.
curl -sS -X POST \
-H "X-NXTL-Key: $NXTL_KEY" \
-H "Content-Type: application/json" \
-d '{"quantity":1,"tier":"standard"}' \
"https://nxtlsim.com/api/v1/esims/estimate"
A successful request returns HTTP 202 with a job ID. Issuance continues asynchronously. Poll GET /jobs/{id} or receive the esim.provisioned or esim.provision_failed event.
curl -sS -X POST \
-H "X-NXTL-Key: $NXTL_KEY" \
-H "Idempotency-Key: order-123-esim" \
-H "Content-Type: application/json" \
-d '{
"nickname":"Traveller 123",
"tier":"standard",
"client_reference":"ORDER-123"
}' \
"https://nxtlsim.com/api/v1/esims"
Authenticate requests by passing your API key in the X-NXTL-Key header on every operation except GET /health. A newly created key returns its secret once; subsequent key listings expose only public identifiers. Store secrets in a managed server-side secret store and rotate or revoke them independently.
X-NXTL-Key: $NXTL_KEY
| Scope Identifier | Granted Operations & Access Level |
|---|---|
balance:read | Read partner profile, balance totals, ledger history, top-up invoices, and quota snapshots. |
balance:write | Execute programmatic account top-ups via saved payment method or invoicing. |
esims:read | Query fleet inventory, inspect single ICCID status/wallet/events, estimate costs, poll jobs, and view usage CDRs. |
esims:write | Issue single/bulk eSIMs, edit nicknames, switch network tiers, suspend, resume, or terminate profiles. |
keys:read | Inspect registered public key identifiers, status, and associated metadata. |
keys:write | Create new scoped API access keys or instantly revoke compromised keys. |
webhooks:read | Inspect configured webhook target URL, subscribed event list, HMAC signing secrets, and delivery logs. |
webhooks:write | Configure webhook subscriptions, dispatch synthetic test pings, and replay previous deliveries. |
thresholds:read | Query configured spend, line usage, and balance threshold alert rules. |
thresholds:write | Create, update, enable/disable, or remove automated usage thresholds and auto-suspend policies. |
audit:read | Access immutable audit logs for administrative actions, security revisions, and key lifecycles. |
usage:read | Granular usage telemetry access (accepted alongside esims:read or balance:read). |
Send Idempotency-Key on POST /esims, POST /esims/bulk-issue and POST /topup. Retrying the same operation with the same key returns the original outcome and adds Idempotent-Replayed: true. Generate a stable key from your own business operation—not a new random value for every retry.
X-NXTL-Idempotency-Key remains supported as a legacy alias. New integrations should use Idempotency-Key.
Read X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset on responses. Account configuration may change the limit, so do not hard-code one global request rate. Treat HTTP 429 as retryable after the indicated reset.
Every response includes X-Request-Id. Record it with failed operations and include it when asking NXTL support to investigate.
A successful issuance request means NXTL accepted the work—not that a profile is already ready. Single and bulk issuance return a job ID. Use GET /jobs/{id} for status and per-row results, or subscribe to the relevant webhook events.
POST /esims queues one eSIM and accepts nickname, tier and client_reference.POST /esims/bulk-issue accepts quantity from 1 to 1,000, plus tier, client_reference and metadata.POST /esims/estimate checks the proposed issuance cost and balance impact without creating eSIMs.GET /jobs lists asynchronous jobs.GET /jobs/{id} returns the current state, summary and per-row outcomes where available./esim-requests/{job_id} is a legacy alias; use /jobs/{id} in new work.esim.provisioned event.
Three end-to-end patterns reflecting standard operational architectures across travel, mobility, and device fleet deployments.
client_reference.POST /esims/estimate.POST /esims with a stable Idempotency-Key.job_id against the booking.esim.provisioned and attach the activation result to the customer journey.esim.provision_failed to support or fulfilment review.POST /esims/estimate and confirm affordable: true.POST /esims/bulk-issue with a stable batch reference and Idempotency-Key.GET /jobs/{id} or listen for job.completed and job.failed.GET /balance and usage summaries.POST /thresholds.notify or suspend as the configured action.balance.low, balance.exhausted and esim.usage_threshold as required.* Qualification: Roaming usage may be reported after activity. Thresholds are operational safeguards, not guaranteed instantaneous hard spending caps.
Repeated suspend and resume requests converge on the requested state. Termination is permanent and should require deliberate confirmation in your own workflow.
PATCH /esims/{iccid}/nickname
PATCH /esims/{iccid}/tier
POST /esims/{iccid}/suspend
POST /esims/{iccid}/resume
PATCH /esims/{iccid}/status
PATCH /esims/{iccid}/tier and POST /esims/bulk-tier take cheapest or best_coverage. A fleet call sends iccids with that tier. A new eSIM uses cheapest unless the issue request sets a tier.
The business account uses a pooled balance. Current issuance fees, available balance, quota and account configuration must be read from the API instead of copied into application code.
Read current balance context, lifetime quota, rate-limit state, issuance fee and key/subscription summary.
Read pool totals and per-eSIM wallet mirrors.
Read the balance ledger.
Credit the pool from available prepaid partner budget; amount_usd is the current request field.
Read completed orders associated with API-driven balance top-ups.
Usage and pooled-balance records can contain USD or EUR depending on the eSIM profile generation and transaction. Read currency and amount as the native pair. Usage/CDR events also expose amount_usd and amount_eur where available; one converted field may be zero when it is not the native accounting amount, so zero must not be interpreted as free usage.
Display conversion can be requested with ?currency=EUR or Accept-Currency: EUR where supported, while canonical accounting fields remain available alongside. Preserve the native currency and amount in your ledger.
| Endpoint | Use |
|---|---|
GET /usage/summary | Partner-wide totals for a selected period |
GET /usage/esims | Per-eSIM rollups and filters |
GET /usage/cdrs | Itemised, paginated usage events |
GET /usage/timeseries | Daily usage and top-up totals for reporting |
GET /esims/{iccid}/usage | Usage rows for one eSIM |
GET /esims/{iccid}/events | Ledger/lifecycle events for one eSIM |
GET /usage/lines is retained as an alias. New integrations should use GET /usage/esims.
balance, esim or legacy-compatible linebalance_below, line_usage_pct, line_usage_usd_todayinstant, daily, weekly, monthlynotify or suspend
Create, update and remove rules through /thresholds. Keep the operational caveat visible: network usage records are not guaranteed to arrive in real time.
NXTL delivers asynchronous lifecycle and billing events to your registered HTTPS webhook endpoint. Every inbound request includes the X-NXTL-Signature-V1 header containing the delivery timestamp and an HMAC-SHA256 signature calculated over {timestamp}.{raw_json_body} using your active webhook secret.
Header Specification: X-NXTL-Signature-V1: t=<unix_timestamp>,v1=<hmac_sha256_hex>. Always compute HMAC over the raw UTF-8 request payload before JSON parsing.
const crypto = require('crypto');
function verifyNxtlWebhook(rawBody, signatureHeader, secret) {
if (!signatureHeader) return false;
// Format: t=<unix_timestamp>,v1=<hex_hmac_sha256>
const parts = signatureHeader.split(',');
const timestamp = parts.find(p => p.startsWith('t='))?.slice(2);
const v1 = parts.find(p => p.startsWith('v1='))?.slice(3);
if (!timestamp || !v1) return false;
// Validate v1 is exactly 64 hexadecimal characters
if (v1.length !== 64 || !/^[0-9a-f]{64}$/i.test(v1)) {
return false;
}
// Reconstruct signed payload
const signedPayload = `${timestamp}.${rawBody}`;
const computedHex = crypto
.createHmac('sha256', secret)
.update(signedPayload, 'utf8')
.digest('hex');
const expectedBuf = Buffer.from(computedHex, 'hex');
const suppliedBuf = Buffer.from(v1, 'hex');
if (expectedBuf.length !== suppliedBuf.length) return false;
return crypto.timingSafeEqual(expectedBuf, suppliedBuf);
}
import hmac, hashlib
def verify_nxtl_webhook(raw_body: bytes, signature_header: str, secret: str) -> bool:
if not signature_header:
return False
parts = dict(part.split('=', 1) for part in signature_header.split(','))
timestamp, v1 = parts.get('t'), parts.get('v1')
if not timestamp or not v1 or len(v1) != 64:
return False
signed_payload = f"{timestamp}.".encode('utf-8') + raw_body
computed_hex = hmac.new(secret.encode('utf-8'), signed_payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(computed_hex, v1)
function verify_nxtl_webhook(string $raw_body, string $signature_header, string $secret): bool {
parse_str(str_replace(',', '&', $signature_header), $parts);
$timestamp = $parts['t'] ?? '';
$v1 = $parts['v1'] ?? '';
if (empty($timestamp) || empty($v1) || strlen($v1) !== 64) {
return false;
}
$signed_payload = $timestamp . '.' . $raw_body;
$computed_hex = hash_hmac('sha256', $signed_payload, $secret);
return hash_equals($computed_hex, $v1);
}
| Event Name | Category | Trigger Condition & Payload Meaning |
|---|---|---|
esim.provisioned |
eSIM Lifecycle | eSIM successfully provisioned (one ICCID ready for profile download). |
esim.provision_failed |
eSIM Lifecycle | Asynchronous issuance failed for an order item. |
esim.tier_changed |
eSIM Lifecycle | Network tier changed (single or bulk). |
esim.suspended |
eSIM Lifecycle | eSIM suspended (data paused). |
esim.reactivated |
eSIM Lifecycle | eSIM resumed after suspend. |
esim.terminated |
eSIM Lifecycle | eSIM permanently terminated. |
esim.exchanged |
eSIM Lifecycle | Old eSIM swapped for a fresh one (device replacement). |
esim.usage_threshold |
eSIM Lifecycle | Per-eSIM usage threshold (e.g. 80% of cap) crossed. |
balance.topup |
Pooled Balance | API top-up credited the pooled balance. |
balance.low |
Pooled Balance | Pooled balance fell below configured threshold. |
balance.exhausted |
Pooled Balance | Pooled balance reached zero — auto-suspend may have fired. |
usage.recorded |
Usage Telemetry | Billable data usage recorded for an eSIM (amount, currency, volume, network). |
job.completed |
Batch Processing | Async bulk job (issuance, etc.) finished. |
job.failed |
Batch Processing | Async bulk job failed at least one row. |
test.ping |
Diagnostics | Manually-triggered test event from the partner portal. |
NXTL API responses use a structured error object with error, type, code, message, doc_url, optional details and a request ID. Some earlier lifecycle and balance operations can return a compact { "error": "code" } object. Treat the HTTP status as authoritative, handle optional fields defensively and retain the X-Request-Id response header when contacting support.
{
"error": true,
"type": "INSUFFICIENT_BALANCE",
"code": "insufficient_balance",
"message": "Pooled balance cannot cover the requested operation.",
"doc_url": "https://nxtlsim.com/business-api/errors/insufficient_balance",
"request_id": "req_8f1b2c3d4e5f",
"details": {
"required_usd": 11.99,
"available_usd": 4.50
}
}
| Status Code | Error Type | Integration Meaning & Handling Guidance |
|---|---|---|
400 BAD_REQUEST | bad_request | Invalid request body or parameters. Inspect details field for validation errors. Some earlier endpoints may return compact {"error":"code"}. |
401 UNAUTHORIZED | unauthorized | Missing, invalid or revoked API secret in the X-NXTL-Key header. |
402 INSUFFICIENT_BALANCE | insufficient_balance | Pooled balance cannot cover the requested operation. Earlier paths may return compact insufficient_budget or insufficient_balance. Top up balance to resume. |
403 ACCESS_DENIED | access_denied | Valid key without the required access scope for the requested endpoint. |
404 NOT_FOUND | not_found | Requested resource (ICCID, Job ID, Delivery ID, Key ID) was not found. |
409 QUOTA_EXCEEDED | quota_exhausted | Partner lifetime issuance quota would be exceeded on bulk issuance. |
429 RATE_LIMIT_EXCEEDED | rate_limit_exceeded | Request rate limit exceeded (or single issuance quota exceeded with {"error":"quota_exceeded"}). Check Retry-After header. |
502 UPSTREAM_ERROR | upstream_error | Upstream carrier provisioning or service dependency failed. Retry with exponential backoff. |
All paths below are relative to https://nxtlsim.com/api/v1. This reference documents the current NXTL B2B API. Use the endpoint groups below for human-readable integration guidance and the published OpenAPI document for machine-readable tooling.
The public API exposes the operations documented above. Some adjacent capabilities still require NXTL operations or a commercial integration review.
If a required operation is not documented, treat it as unavailable until NXTL confirms it—do not build against undocumented endpoints, unpublished routes, or inferred upstream carrier capabilities.
Tell us who or what you need to connect, the markets involved, expected monthly volume and the system that should trigger fulfilment. We will assess account, device, commercial and API fit.
API approval is not automatic. Begin with a contained workflow and expand after the operational path is proven.