Travixor GlobalDevelopers

Order an eSIM

An order takes three steps: a quote, the order itself, and reading the result. The Idempotency-Key makes the second step safe to repeat.

The whole flow

This script does all three steps. Replace PACKAGE_ID with the id of a package from the catalogue.

cURL
# 1. Ask for a quote. The package_id comes from the catalogue.
curl https://api.travixorglobal.com/v1/quotes \
  -H "Authorization: Bearer $TRAVIXOR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"package_id": "PACKAGE_ID", "quantity": 1}'

# 2. Place the order with the quote id from step 1.
#    Make up the Idempotency-Key yourself and keep it with your booking.
curl https://api.travixorglobal.com/v1/orders \
  -H "Authorization: Bearer $TRAVIXOR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6b0f2c1e-8d4a-4c7e-9b53-1f2a7d90e4c8" \
  -d '{"quote_id": "QUOTE_ID", "client_reference": "booking-4821"}'

# 3. Read the order until its status is completed or failed.
curl https://api.travixorglobal.com/v1/orders/ORDER_ID \
  -H "Authorization: Bearer $TRAVIXOR_API_KEY"
TypeScript
import { randomUUID } from 'node:crypto'

const API = 'https://api.travixorglobal.com'

async function api(path: string, init: { method?: string; body?: unknown; headers?: Record<string, string> } = {}) {
  const res = await fetch(API + path, {
    method: init.method ?? 'GET',
    headers: {
      authorization: `Bearer ${process.env.TRAVIXOR_API_KEY}`,
      ...(init.body === undefined ? {} : { 'content-type': 'application/json' }),
      ...init.headers,
    },
    body: init.body === undefined ? undefined : JSON.stringify(init.body),
  })
  const json = (await res.json()) as any
  if (!res.ok) throw new Error(`${res.status} ${json.error.code}: ${json.error.message}`)
  return json
}

// 1. A quote fixes the price for 5 minutes.
const { quote } = await api('/v1/quotes', { method: 'POST', body: { package_id: 'PACKAGE_ID', quantity: 1 } })

// 2. Save this key with your booking before you send the order.
//    If the request times out, send the same order again with the same key.
const idempotencyKey = randomUUID()
let { order } = await api('/v1/orders', {
  method: 'POST',
  headers: { 'idempotency-key': idempotencyKey },
  body: { quote_id: quote.id, client_reference: 'booking-4821' },
})

// 3. Read the order until it is no longer processing.
while (order.status === 'processing') {
  await new Promise((resolve) => setTimeout(resolve, 3000))
  ;({ order } = await api(`/v1/orders/${order.id}`))
}

console.log(order.status, order.esims)
Python
import os
import time
import uuid

import requests

API = "https://api.travixorglobal.com"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['TRAVIXOR_API_KEY']}"


def api(method, path, **kwargs):
    res = session.request(method, API + path, timeout=30, **kwargs)
    body = res.json()
    if not res.ok:
        raise RuntimeError(f"{res.status_code} {body['error']['code']}: {body['error']['message']}")
    return body


# 1. A quote fixes the price for 5 minutes.
quote = api("POST", "/v1/quotes", json={"package_id": "PACKAGE_ID", "quantity": 1})["quote"]

# 2. Save this key with your booking before you send the order.
#    If the request times out, send the same order again with the same key.
idempotency_key = str(uuid.uuid4())
order = api(
    "POST",
    "/v1/orders",
    headers={"Idempotency-Key": idempotency_key},
    json={"quote_id": quote["id"], "client_reference": "booking-4821"},
)["order"]

# 3. Read the order until it is no longer processing.
while order["status"] == "processing":
    time.sleep(3)
    order = api("GET", f"/v1/orders/{order['id']}")["order"]

print(order["status"], order["esims"])

1. Get a quote

POST /v1/quotes needs the orders:write scope. Send package_id and quantity (1 to 10). The price is worked out on our side and cannot be sent. A package sold per day also needs days.

Example answer. The values are made up.
{
  "quote": {
    "id": "5d1c7e92-3b48-4f06-8a2d-c7e1b09f4a63",
    "package_id": "0b7e2a54-6f1c-4d58-9a3e-2c91f0d7b6a4",
    "package_name": "Turkey 1GB 7 Days",
    "days": null,
    "quantity": 2,
    "unit_price_cents": 1023,
    "total_price_cents": 2046,
    "currency": "USD",
    "created_at": "2026-11-02T09:30:00.000Z",
    "expires_at": "2026-11-02T09:35:00.000Z"
  }
}

A quote holds its price for 5 minutes (expires_at) and can be used for one order. It does not set any money aside.

2. Place the order

POST /v1/orders takes the quote_id and, if you like, a client_reference such as your own booking number. It returns the order with its client_reference so you can match them up later.

Every order needs an Idempotency-Key header: a new UUID for each order. Create it before you send the request and store it with your booking.

Example answer. The values are made up.
{
  "order": {
    "id": "e3a94d17-72c0-4b85-9f1e-6d08a2c5b7f1",
    "status": "processing",
    "package_id": "0b7e2a54-6f1c-4d58-9a3e-2c91f0d7b6a4",
    "package_name": "Turkey 1GB 7 Days",
    "days": null,
    "quantity": 1,
    "unit_price_cents": 1023,
    "total_price_cents": 1023,
    "currency": "USD",
    "client_reference": "booking-4821",
    "failure": null,
    "created_at": "2026-11-02T09:30:02.000Z",
    "completed_at": null,
    "esims": []
  }
}

The price is taken from your wallet when the order is placed and shown as held until the order is finished. A failed order releases it in full.

3. Read the result

An order is in one of three states.

StatusMeaning
processingThe order is being supplied. Read it again in a few seconds.
completedThe eSIMs are ready and listed in esims. The money has been spent.
failedNothing was charged. failure holds a code and a message.

Poll GET /v1/orders/{id} every few seconds, or leave the order and read it later: it does not expire. Orders are never split, so you either get all the eSIMs of an order or none. See Deliver the eSIM for what to do with them.

Example answer for a completed order. The values are made up.
{
  "order": {
    "id": "e3a94d17-72c0-4b85-9f1e-6d08a2c5b7f1",
    "status": "completed",
    "package_id": "0b7e2a54-6f1c-4d58-9a3e-2c91f0d7b6a4",
    "package_name": "Turkey 1GB 7 Days",
    "days": null,
    "quantity": 1,
    "unit_price_cents": 1023,
    "total_price_cents": 1023,
    "currency": "USD",
    "client_reference": "booking-4821",
    "failure": null,
    "created_at": "2026-11-02T09:30:02.000Z",
    "completed_at": "2026-11-02T09:30:07.000Z",
    "esims": [
      {
        "id": "9c26b0e4-1a7d-4e38-b5f2-d4803c7a19e6",
        "order_id": "e3a94d17-72c0-4b85-9f1e-6d08a2c5b7f1",
        "iccid": "8901234567890123456",
        "status": "ready",
        "activation_code": "LPA:1$smdp.example.invalid$AAAA-BBBB-CCCC-DDDD",
        "ios_install_link": "https://example.invalid/install/ios/AAAA-BBBB",
        "android_install_link": "https://example.invalid/install/android/AAAA-BBBB",
        "data_total_mb": null,
        "data_used_mb": null,
        "data_left_mb": null,
        "activated_at": null,
        "expires_at": null,
        "updated_at": "2026-11-02T09:30:07.000Z"
      }
    ]
  }
}

Ordering can be paused for a short time. Quotes and orders then answer 503 with ordering_paused. Nothing is charged; try again later.