eSIM Global
For Developers

Partner Reseller API

Browse the eSIM catalog, place orders on behalf of your own customers, and receive delivery confirmation from your own backend β€” no browser session required.

Base URLhttps://triptel.co/api/v1/partner
Getting set up is an admin-only, manual step: a Super Admin issues your API key once your merchant application is approved. There is no self-service signup for API access β€” see Β§6 below.

1. Authentication

All endpoints except the token endpoint itself require a Bearer access token in the Authorization header. The long-lived API key you were issued (esim_live_...) is never sent on these calls directly β€” it is only ever used once, to obtain a token.

1.1 Exchange your API key for an access token

POST/auth/token
Request
POST /api/v1/partner/auth/token
Content-Type: application/json

{
  "api_key": "esim_live_1a2b3c4d5e6f7g8h..."
}
Response 200
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer",
  "expires_in": 3600
}
  • Tokens are valid for 1 hour. There is no refresh token β€” when a token expires, call this endpoint again with your API key to get a new one (standard OAuth2 Client Credentials style flow).
  • 401 if the key is invalid, malformed, or has been revoked.
  • Rate limited to 10 requests/minute per IP.

1.2 Use the token on every other call

GET /api/v1/partner/me
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
  • 401 if the token is missing, malformed, expired, or was issued for a different purpose β€” a regular customer login token cannot be used here, and this token cannot be used against the regular customer-facing API either.
  • If your key is revoked by an admin, tokens already issued for it stop working on their very next use β€” you don't have to wait out the full hour.

Full flow example

bash
TOKEN=$(curl -s -X POST https://triptel.co/api/v1/partner/auth/token \
  -H "Content-Type: application/json" \
  -d '{"api_key": "esim_live_..."}' | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")

curl -s https://triptel.co/api/v1/partner/me \
  -H "Authorization: Bearer $TOKEN"

2. Endpoints

All request/response bodies are JSON. Every endpoint below requires the Bearer token from Β§1.2.

GET/countries

Lists all active destination countries. Optional query param search filters by name or ISO2 code.

Response 200
[
  {
    "id": 1,
    "iso2": "US",
    "iso3": "USA",
    "name_en": "United States",
    "name_bn": null,
    "flag_url": "https://...",
    "region": null,
    "is_popular": true,
    "is_active": true
  }
]

Rate limit: 120/minute.

GET/countries/{iso2}/products

Lists active eSIM data products for a country. iso2 is case-insensitive.

Response 200
[
  {
    "id": "a1b2c3d4-...",
    "country_id": 1,
    "title_en": "1GB / 7 Days",
    "title_bn": null,
    "data_amount_gb": 1.0,
    "validity_days": 7,
    "retail_price": 4.50,
    "currency": "USD",
    "is_unlimited": false,
    "is_active": true,
    "coverage_networks": null
  }
]

404 if the country ISO2 doesn't exist. Rate limit: 120/minute.

GET/me

Your merchant profile, effective commission rate, and current wallet balance.

Response 200
{
  "business_name": "Acme Travel Co",
  "commission_rate_pct": 5.00,
  "wallet_balance": 142.5000,
  "wallet_currency": "USD",
  "webhook_configured": true
}

Reflects any active volume-based commission tier, falling back to your flat rate. Rate limit: 120/minute.

POST/orders

Places an order and provisions an eSIM asynchronously. Your merchant wallet is debited the product's retail_price immediately; the order returns in PROCESSING status right away β€” poll GET /orders/{id} or wait for the webhook (Β§3) to find out whether it completed.

Request
{
  "product_id": "a1b2c3d4-...",
  "customer_reference": "your-internal-order-id-123"
}

customer_reference is optional β€” echoed back in every response and webhook so you can reconcile without storing our order_id first.

Response 200
{
  "order_id": "e5f6a7b8-...",
  "order_no": "PTR-20260904-A1B2C3",
  "status": "PROCESSING"
}

What happens after PROCESSING:

  • Success β€” order becomes COMPLETED, iccid/lpa_string/qr_code_data are populated, and commission is credited to your account.
  • Failure β€” order becomes FAILED, no eSIM is issued, and the full amount is refunded to your wallet automatically. No commission is paid on a failed order.

400 insufficient wallet balance (no order created) Β· 404 product not found/inactive Β· rate limit 30/minute.

GET/orders/{order_id}

Polls the current state of an order β€” the fallback if you're not using webhooks.

Response 200
{
  "order_id": "e5f6a7b8-...",
  "order_no": "PTR-20260904-A1B2C3",
  "customer_reference": "your-internal-order-id-123",
  "status": "PROCESSING",
  "iccid": null,
  "lpa_string": null,
  "qr_code_data": null,
  "is_mock_fallback": null,
  "created_at": "2026-09-04T12:00:00Z"
}

status is one of PROCESSING, COMPLETED, FAILED. 404 if the order doesn't exist or isn't yours. Rate limit: 120/minute.

PUT/webhook-config

Sets, rotates, or clears your webhook endpoint. Self-service β€” no admin involvement needed.

Request (set/rotate)
{ "webhook_url": "https://yourapp.example.com/webhooks/esim" }
Response 200
{
  "webhook_url": "https://yourapp.example.com/webhooks/esim",
  "webhook_secret": "whsec_...",
  "warning": "Store this secret securely - it will not be shown again in full."
}

A new webhook_secret is generated every time you set or change the URL β€” save it immediately, it verifies webhook signatures (Β§3) and is never shown again.

Send {"webhook_url": null} to clear your webhook entirely (you'll only be able to poll after that):

Response (clear)
{ "webhook_url": null, "webhook_secret": null }

Rate limit: 10/minute.

3. Webhooks

If you have a webhook_url configured, we POST to it once your order reaches a terminal state (COMPLETED or FAILED).

Delivery attempts: one immediately when the order finishes processing; if that fails (non-2xx response, timeout, connection error), a retry sweep runs every 5 minutes and tries again, up to 3 attempts total. There's no dead-letter queue after that β€” poll GET /orders/{id} if you suspect a delivery was missed.

Payload

{
  "order_id": "e5f6a7b8-...",
  "customer_reference": "your-internal-order-id-123",
  "status": "COMPLETED",
  "iccid": "8988212345678901234",
  "lpa_string": "LPA:1$rsp.truphone.com$MATCHID123",
  "qr_code_data": "LPA:1$rsp.truphone.com$MATCHID123",
  "is_mock_fallback": false
}

On a FAILED order, the eSIM fields are all null β€” check status first.

Signature verification

Every delivery includes an X-Webhook-Signature header: the HMAC-SHA256 of the exact raw request body, hex-encoded, using your webhook_secret.

python
import hmac, hashlib

def verify_webhook(raw_body: bytes, signature_header: str, webhook_secret: str) -> bool:
    expected = hmac.new(webhook_secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature_header)

Always verify against the raw bytes of the request body, before any JSON parsing β€” re-serializing and re-hashing a parsed-then-reserialized payload is not guaranteed to match byte-for-byte.

4. Rate Limits

EndpointLimit
POST /auth/token10/minute (per IP)
POST /orders30/minute (per API key)
PUT /webhook-config10/minute (per API key)
Everything else120/minute (per API key)

A 429 response means you've hit the limit β€” back off and retry after a short delay. Limits are keyed to your specific API key (via the token), not your IP, except for the token endpoint itself (which has no partner identity yet).

5. Errors

Standard HTTP status codes; error detail is always in the JSON body's detail field:

{ "detail": "Insufficient wallet balance. Please top up." }
StatusMeaning
400Bad request β€” invalid input or a business rule violation
401Missing/invalid/expired token, or invalid/revoked API key at the token endpoint
404Resource not found (deliberately indistinguishable from "not yours")
429Rate limit exceeded
502Upstream provisioning failure (rare from your side β€” provisioning happens after POST /orders already returned)

6. Getting API Access

Key issuance and revocation are Super-Admin-only actions performed from the internal admin console β€” there is no self-service signup. If you're a merchant looking to integrate programmatically, contact your account admin once your partner application is approved.

If you suspect your key has leaked, ask for immediate revocation β€” any access tokens already issued from it stop working on their next use, not just after they naturally expire.

Not a partner yet? Apply to the B2B Reseller Program