Roam eSIMs API

Roam eSIMs provides a REST API that allows AI assistants and other authorised clients to find, purchase and provision travel eSIMs.

API base URL: https://api.roamesims.com (kept separate from the website domain). Machine-readable OpenAPI specification: https://api.roamesims.com/openapi.json

Endpoints

All https://api.roamesims.com/v1/* requests require Authorization: Bearer <connector-key>.

Example: get a quote

POST https://api.roamesims.com/v1/quotes

{
  "destinations": ["JP"],
  "start_date": "2026-10-08",
  "end_date": "2026-10-18",
  "travellers": 3,
  "usage": "normal"
}

Response — 1 to 3 plans, returned in Light, Standard, Heavy order where available:

{
  "status": "available",
  "quote_id": "qt_f3016m0cj928vfesgkzp",
  "trip": { "destinations": ["JP"], "duration_days": 11, "travellers": 3, "usage": "normal" },
  "currency": "USD",
  "expires_at": "2026-10-01T03:31:42.401Z",
  "plans": [
    { "offer_id": "of_light", "tier": "light", "name": "Light", "data_gb": 3, "validity_days": 30, "price": { "per_traveller": 8.99, "total": 26.97, "currency": "USD" }, "recommended": false },
    { "offer_id": "of_standard", "tier": "standard", "name": "Standard", "data_gb": 10, "validity_days": 30, "price": { "per_traveller": 15.99, "total": 47.97, "currency": "USD" }, "recommended": true },
    { "offer_id": "of_heavy", "tier": "heavy", "name": "Heavy", "data_gb": 20, "validity_days": 30, "price": { "per_traveller": 25.99, "total": 77.97, "currency": "USD" }, "recommended": false }
  ]
}

total is per_traveller × the traveller count from the request.

When no plan covers every requested destination, status is unavailable, plans is empty, and reason is NO_COMPLETE_COVERAGE or NO_ELIGIBLE_PACKAGES. A quote_id is still returned for logging/support purposes, even though it can never be checked out.

Example: create a checkout

Call this only after the traveller has explicitly selected or approved a plan — not simply because travel was mentioned.

POST https://api.roamesims.com/v1/checkouts

{
  "quote_id": "qt_f3016m0cj928vfesgkzp",
  "offer_id": "of_standard"
}

Response — hand checkout_url to the traveller directly (do not fetch or parse it); use order_id (never a token) for any later status checks:

{
  "order_id": "ord_9f2a1c7e4b8d3f6a2c10",
  "checkout_url": "https://checkout.stripe.com/c/pay/cs_test_..."
}

This call is safe to retry (e.g. after a network timeout) even without any special header — the same quote_id/offer_id always returns the original order rather than creating a second one. An optional Idempotency-Key header gives extra protection on top of that.

Example: check order status

GET https://api.roamesims.com/v1/orders/ord_9f2a1c7e4b8d3f6a2c10

{
  "order_id": "ord_9f2a1c7e4b8d3f6a2c10",
  "state": "READY",
  "travellers": 1,
  "esims_expected": 1,
  "esims_ready": 1,
  "next_action": "OPEN_INSTALL_PAGE",
  "message": "The eSIM is ready. The traveller can use the private installation page received after checkout.",
  "plan": { "name": "Standard", "data_gb": 10, "validity_days": 30, "coverage": ["JP"] },
  "price": { "total": 18.99, "currency": "USD" },
  "created_at": "2026-10-01T03:21:10.000Z"
}

One eSIM is provisioned per traveller. For a group order, state only becomes READY once esims_ready equals esims_expected — it never means “at least one is ready”:

{
  "order_id": "ord_3g7ouppartialabc123",
  "state": "PROVISIONING",
  "travellers": 3,
  "esims_expected": 3,
  "esims_ready": 2,
  "next_action": "WAIT_FOR_PROVISIONING",
  "message": "2 of 3 eSIMs are ready. The remaining eSIM is still being prepared.",
  "plan": { "name": "Standard", "data_gb": 10, "validity_days": 30, "coverage": ["JP"] },
  "price": { "total": 47.97, "currency": "USD" },
  "created_at": "2026-10-01T03:21:10.000Z"
}

next_action tells the caller what to do next (COMPLETE_PAYMENT, WAIT_FOR_PROVISIONING, OPEN_INSTALL_PAGE, or NONE) without having to infer behaviour from state alone.

Example: check installation status

GET https://api.roamesims.com/v1/orders/ord_9f2a1c7e4b8d3f6a2c10/installation

{
  "state": "ready",
  "activation_message": "You can install this now. Your plan begins when the eSIM first connects to a supported network at your destination."
}

This endpoint never returns the QR code, LPA, or activation code — those only ever appear on the traveller's own private install page, so they never end up in connector logs or conversation transcripts. Once ready, tell the traveller to use the private installation page they were redirected to after payment or received by email.

Errors

Non-2xx responses always return the same shape:

{
  "error": "OFFER_CHANGED",
  "message": "optional human-readable detail"
}

Common error codes: