API and SDKs

API and SDKs

Build Atli into your own app or website, on your own screens.

Atli eSIM API

v1.0.0

Buy and manage travel eSIMs: browse the catalogue, quote and buy a plan, install and track an eSIM, share one as a gift, and run a group account.

Paths are unversioned and the version of this document is in info.version, as on heyatli.com's published API. A breaking change will arrive under a new base URL rather than by changing what a path here means.

Base URL
https://api.heyatli.com
OpenAPI file
https://api.heyatli.com/openapi.yaml

Main journeys

The API’s main journeysA user is created, gets a quote, purchases and pays, gets the eSIM, installs it and tracks usage. Optional branches: a real account, saving a card, a group card, topping up, discounts, and buying to share, then sharing a link that someone claims.
Create a userPOST /login
Get a quotePOST /plan/quote
Purchase and payPOST /plan/purchase
Get the eSIMGET /fulfillment/…
InstallGET /esims
Track usagePOST /esims/{id}/sync
Real accountEmail, Telegram, WhatsApp
Save a cardStripe SetupIntent
Group cardgroup_id
Top uptarget_esim_id
DiscountsVouchers, credits, referral
Buy to shareis_gift, max_claims
Share the linkGET /public/gifts/{code}
ClaimMay need a real account
Main journeyOptional

Pick a flow on the map to see its steps.

SDKs

Generate a ready-to-use client for your language from our OpenAPI file.

docker run --rm -v "$PWD:/out" \
  openapitools/openapi-generator-cli generate \
  -i https://api.heyatli.com/openapi.yaml \
  -g typescript-fetch -o /out/atli-sdk

Authentication

Most routes need a bearer token, issued by signing in through /login, /register or one of the /oauth/* flows, and refreshed at /refresh. Each operation states whether a token is required, read when sent, or ignored. Tokens identify a person, not an application; there is no application API key yet.

Enums

A field with a listed set of values may gain new ones: that ships as a minor version, never a new base URL. Treat a value you do not recognise as one you do not handle yet, not as invalid, and never let it fail parsing.

Errors

Every failed request answers with the same body: a stable code, an English message, a trace_id to quote when reporting a problem, and sometimes params. Branch on code and never on message.

See common error codes
400

The request is invalid

401

Sign-in is missing or has expired

402

The payment failed

403

This user isn’t allowed to do this

404

It doesn’t exist, or isn’t theirs

429

Too many requests, so wait out Retry-After

500

Something went wrong on our side

Rate limits

Sign-in, registration, token refresh, email codes, voucher redemption, quotes, reviews and eSIM sync are throttled per caller. A refused request answers 429 with Retry-After in whole seconds, alongside X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Wait out Retry-After rather than retrying in a loop.

Language and currency

Send X-Locale (or Accept-Language, or ?lang=) to choose the language; the language answered in comes back as Content-Language, falling back to English. Prices follow the signed-in person's currency, or the currency a quote asks for.

Buying a plan

Quote first at /plan/quote, then pass the quote_id to /plan/purchase so the price you showed is the price charged. Send an idempotency_key on every purchase: the same key never buys twice.