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
POST https://api.roamesims.com/v1/quotes— find suitable eSIM plans for an itinerary.POST https://api.roamesims.com/v1/checkouts— create a checkout for a selected quoted offer.GET https://api.roamesims.com/v1/orders/:id— check fulfilment status.GET https://api.roamesims.com/v1/orders/:id/installation— check installation status once provisioned.GET https://api.roamesims.com/health— unauthenticated liveness probe (infra/monitoring only, not a connector action).
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:
UNAUTHORIZED(401) — missing or invalid connector key.INVALID_REQUEST(400) — request failed validation; seemessage.QUOTE_EXPIRED/QUOTE_NOT_FOUND(400) — get a new quote.OFFER_NOT_FOUND(400) — unknown offer id.OFFER_UNAVAILABLE(400) — rare: the offer was consumed but has no recoverable order; get a new quote. A repeatedquote_id/offer_idthat already has an order does not hit this — it returns 201 with the existing order.OFFER_CHANGED(409) — the plan changed since the quote was issued; no charge was made, get a new quote.SUPPLIER_UNAVAILABLE(503) — live inventory could not be checked; retry shortly.ORDER_NOT_FOUND(404) — unknown order id.