API v1

Sell eSIMs under your own brand

A catalogue covering 200+ countries and regions. All you send is a request to issue: the response immediately brings an eSIM page link for your customer and the price to sell it at. We take care of installation, support and top-ups. No carrier contracts and no billing stack of your own.

200+

destinations in the catalogue

1

request to issue — all we need from you

$0

is what the sandbox costs

1. How it works

  1. Your customer buys an eSIM from you — on your website, in your app or at your office — at the sale price we return in the catalogue (sale_price).

  2. Your software sends a single POST /orders request. We charge your price (price) to your wallet and immediately respond with a link.

  3. You give the link to your customer — by email, in a messenger, on screen. While the profile is being issued the page says it is being prepared, then it shows the QR code and instructions.

  4. Everything else is on our side: installation, support in the chat on the same page, data top-ups. For any question about their eSIM the customer writes to us.

The difference between sale_price and price is your income. We set both prices as agreed with you; there is nothing to calculate on your side.

2. Getting started

Send us a request and we will set up your console access. You issue keys there yourself, on the “Keys” page: start with the sandbox and issue a production key once your integration is ready. A key is shown only once — we store nothing but its hash; lost it? Revoke it and issue a new one. The console is also where you change your sign-in email and password and see your orders, profiles and account.

All amounts in the API are in US dollars (USD). Prices come already calculated: price is what we charge to your wallet, sale_price is what to sell to your customer for. The wallet is topped up in advance, on the “Account” page of the console — by bank card, Apple Pay or Google Pay; funds are credited immediately.

Start with the sandbox. It issues profiles and links in the real format but never contacts the supplier and never charges money, so you can experiment as much as you like.

Base URL: https://aloesim.am/api/v1

3. Authentication

Pass the key in the Authorization: Bearer header. Never put it in the query string — URLs end up in proxy logs and error reports.

curl https://aloesim.am/api/v1/ping \ -H "Authorization: Bearer alo_test_…"

The mode is visible in the key itself: alo_live_ is production, alo_test_ is the sandbox. This is not decoration: the most common integration accident is a production key left behind in a test environment. GET /ping shows which key you came with:

{ "data": { "status": "ok", "mode": "sandbox", "partner": { "id": 42, "company": "Travel Co" }, "key": { "name": "main server", "prefix": "alo_test_9f2c1ab4" }, "server_time": "2026-10-20T10:15:00+00:00" } }

4. What you can call

GET /ping Check the key and mode
GET /balance Account balance
GET /countries Destinations that have plans
GET /packages Catalogue with your price (price) and sale price (sale_price)
GET /packages/{slug} A single plan
POST /orders Issue an eSIM — the response carries the customer link
GET /orders Your orders
GET /orders/{id} Order status and the profile once it is ready
GET /esims Issued profiles
GET /esims/{iccid} A profile: link, installation details, usage
GET /esims/{iccid}/usage Fresh data usage
POST /esims/{iccid}/cancel Cancel a profile that has not been installed
GET /webhook Current notification URL
PUT /webhook Set the notification URL — the response carries the signing secret
POST /webhook/test Test notification
DELETE /webhook Turn notifications off

Lists (/packages, /orders, /esims) are paginated: page and per_page parameters (25 by default, 100 at most), page details in meta. A sandbox key sees only test orders and profiles, a production key only production ones.

{ "data": [ … ], "meta": { "page": 1, "per_page": 25, "total": 140, "last_page": 6 } }

5. Catalogue and prices

GET /countries returns destinations (iso2, name, flag). GET /packages returns plans; filters country (ISO2), region, type (local, regional, global). A plan is ordered by its slug.

curl "https://aloesim.am/api/v1/packages?country=GE&per_page=50" \ -H "Authorization: Bearer alo_test_…"
{ "data": [{ "slug": "esimaccess-ge-1-7", "title": "Georgia · 1 GB for 7 days", "type": "local", "country": { "iso2": "GE", "name": "Georgia" }, "data_bytes": 1073741824, "data_label": "1 GB", "is_unlimited": false, "is_daily": false, "validity_days": 7, "price": { "currency": "USD", "amount_cents": 673, "amount": 6.73, "per": "package" }, "sale_price": { "currency": "USD", "amount_cents": 990, "amount": 9.9, "per": "package" }, "network": { "speed": "4G/5G", "apn": "internet", "supports_topup": true, "operators": [ … ] } }], "meta": { "page": 1, "per_page": 50, "total": 12, "last_page": 1 } }

price is your price: what we charge to your wallet. sale_price is what to sell the eSIM to your customer for. Our storefront discounts are not included in these prices: they are ours, for our own customers.

For now the API offers only plans with a data limit — unlimited and daily plans will come later. Catalogue prices are per package (per: "package"), and the exact amounts also come back in the order response.

6. Issuing an eSIM

One request. The money is charged straight away and the response arrives in a fraction of a second — with the link for your customer; the profile itself is issued by the supplier within a few seconds.

curl -X POST https://aloesim.am/api/v1/orders \ -H "Authorization: Bearer alo_test_…" \ -H "Idempotency-Key: 7f3a9c02-order-1183" \ -H "Content-Type: application/json" \ -d '{"package":"esimaccess-ge-1-7","reference":"my-order-1183"}'
Field What it is
package Required. The plan slug from the catalogue.
days Daily plans only: how many days. There are none in the API right now — the field is for the future.
reference Optional. Your order number, up to 64 characters: it comes back in responses and notifications, handy for matching our orders with yours.
Idempotency-Key Required header, up to 191 characters. A retry with the same key returns the same order instead of issuing a second eSIM.

The Idempotency-Key header is not a formality: the request moves money, and connections drop on exactly this kind of request. Generate the key once per order on your side and retry with it as often as needed. The response is 201 for a new order and 200 for a retry:

{ "data": { "id": "01a0bbc2-1341-7298-a018-b1d28cfa1e0e", "status": "paid", "is_ready": false, "mode": "sandbox", "link": "https://aloesim.am/Ab3dE7…", "package": { "slug": "esimaccess-ge-1-7", "title": "Georgia · 1 GB for 7 days", "data_bytes": 1073741824, "validity_days": 7 }, "period_days": null, "quantity": 1, "price": { "currency": "USD", "amount_cents": 673, "amount": 6.73, "charged": false }, "sale_price": { "currency": "USD", "amount_cents": 990, "amount": 9.9 }, "reference": "my-order-1183", "esim": null, "failure_reason": null, "created_at": "2026-10-20T10:15:02+00:00", "completed_at": null } }

link is the eSIM page for your customer. It arrives immediately and never changes: the same URL will be in GET /orders/{id}, in the profile and in the esim.ready notification. price is what was charged to your wallet (charged: false in the sandbox), sale_price is what to sell the eSIM for; both prices are fixed at the time of the order.

Order status: paid, provisioning, awaiting_provider — being issued; completed — the profile is ready; failed — it could not be issued, the money is back in your wallet, the reason is in failure_reason. Polling is optional: the link works on its own, and you can get notifications about readiness and failures.

Once the profile is ready, is_ready becomes true and esim carries the installation details. You only need them if you want to show the QR code on your side too — usually the link is enough. Profile status: issued — issued, active — installed and working, depleted — out of data, expired — validity ended, cancelled, suspended, revoked.

"esim": { "iccid": "8944084435147973198", "status": "issued", "mode": "sandbox", "link": "https://aloesim.am/Ab3dE7…", "activation": { "lpa": "LPA:1$rsp.example.com$K2-9F3A-71BC", "smdp_address": "rsp.example.com", "matching_id": "K2-9F3A-71BC", "confirmation_code": null, "apn": "internet" }, "usage": { "total_bytes": 1073741824, "used_bytes": 0, "remaining_bytes": 1073741824, "synced_at": null }, "activated_at": null, "expires_at": "2026-10-27T10:15:20+00:00" }

7. The eSIM page behind the link

The link opens without signing in and without an app — the long random URL itself grants access. The page is hidden from search engines. What your customer sees there:

  • While the profile is being issued — “Your eSIM is being prepared”; the page refreshes by itself once it is ready.
  • A ready profile — the QR code, one-tap installation on iPhone, details for manual installation, video guides for iPhone and Android, remaining data and validity.
  • If issuing failed — a message that the problem is on our side, and a chat with our support.
  • Support chat — the customer writes to us right from the page: we help with installation, connectivity and data.
  • Top-ups — the customer adds data or days to the same eSIM and pays on the page; nothing needs to be reinstalled.
  • Your branding — your company logo, colours and fonts; we set it up at your request. Your contact details are not on the page: for anything about the eSIM the customer comes to us.
  • Language — English, Russian or Armenian; the customer switches it.

There are no prices on the page: neither yours nor the sale price. In the sandbox the page carries a “Test eSIM” notice and top-ups are disabled.

8. Profiles

GET /esims and GET /esims/{iccid} return issued profiles, addressed by ICCID. A profile carries the same link, installation details and usage.

GET /esims/{iccid}/usage returns fresh usage: we ask the supplier right now. If it does not answer, you get the last known figures with a synced_at timestamp.

POST /esims/{iccid}/cancel cancels a profile that has not yet been installed on a phone. An installed one cannot be cancelled (esim_activated): the supplier has already consumed it. Cancelled profiles are not refunded.

9. Notifications

Optional: the customer link works without notifications. Set a URL if you want to know right away that an issue failed (order.failed) — the money is back in your wallet, refund your customer — and that a profile is ready (esim.ready).

curl -X PUT https://aloesim.am/api/v1/webhook \ -H "Authorization: Bearer alo_live_…" \ -H "Content-Type: application/json" \ -d '{"url":"https://partner.example/hooks/alo"}'

The response carries a secret — it is shown only once; another PUT /webhook issues a new one. Every message from us is a JSON POST signed in the X-Alo-Signature header: HMAC-SHA256 of the request body in hex. The event name is repeated in X-Alo-Event. Always verify the signature, otherwise anyone could post to this URL:

// PHP $ok = hash_equals( hash_hmac('sha256', file_get_contents('php://input'), $secret), $_SERVER['HTTP_X_ALO_SIGNATURE'] ?? '' ); // Node.js (rawBody: the request body as received, before JSON parsing) const expected = Buffer.from(crypto.createHmac('sha256', secret).update(rawBody).digest('hex')); const got = Buffer.from(req.get('X-Alo-Signature') || ''); const ok = got.length === expected.length && crypto.timingSafeEqual(expected, got);

Events: esim.ready — the profile is issued and the link already shows the QR code; order.failed — issuing has definitively failed, the money is back in your wallet.

{ "event": "esim.ready", "sent_at": "2026-10-20T10:15:31+00:00", "data": { "order_id": "01a0bbc2-…", "reference": "my-order-1183", "mode": "live", "iccid": "8944084435147973198", "lpa": "LPA:1$rsp.example.com$K2-9F3A-71BC", "link": "https://aloesim.am/Ab3dE7…", "status": "issued" } }
{ "event": "order.failed", "sent_at": "2026-10-20T10:16:05+00:00", "data": { "order_id": "01a0bbc2-…", "reference": "my-order-1183", "mode": "live", "reason": "…" } }

POST /webhook/test sends a test ping event — use it to check delivery and the signature. GET /webhook shows the current URL, DELETE /webhook turns notifications off.

Reply with a 2xx code: anything else counts as a failed delivery and is retried five times with increasing delays. The URL must be https and point to the outside world — we do not call internal addresses.

10. Errors and limits

An error always looks the same: a machine code in error.code and a human explanation next to it. Parse the code, not the text — we may reword the text, and it comes in the language from the Accept-Language header.

{ "error": { "code": "insufficient_funds", "message": "Insufficient funds on the account." } }
Code HTTP When
missing_key / invalid_key / revoked_key 401 The key is missing, unknown or revoked
partner_blocked 403 Access closed — contact your manager
validation_failed 422 Request fields failed validation — see details
idempotency_key_required 400 No Idempotency-Key when creating an order
idempotency_key_conflict 409 The same Idempotency-Key with different data
idempotency_key_in_progress 409 An order with this key is still being created — retry in a second
insufficient_funds 402 Not enough money in the account
package_not_found 404 The plan does not exist or has been withdrawn
days_required 422 No days given for a daily plan
package_unavailable 409 The supplier does not have the plan right now
not_found 404 Order, profile or plan (/packages/{slug}) not found
esim_activated 409 The profile is already installed — it cannot be cancelled
cancel_refused 409 The supplier refused the cancellation
provider_error 502 The supplier did not respond — try again later
invalid_webhook_url 422 The notification URL is not https or points to an internal network
webhook_not_set 422 Test notification without a URL set
rate_limited 429 Request limit exceeded
server_error 5xx An error on our side — try again later

A validation error lists what is wrong with each field:

{ "error": { "code": "validation_failed", "message": "…", "details": { "package": ["…"] } } }

The limit is 120 requests per minute per key. Exceeding it returns 429 with a Retry-After header. Need more? Tell us and we will raise it.

11. Sandbox

An alo_test_ key works with the same catalogue and the same prices, but its orders never reach the supplier and never charge you. The profile comes back in the real format — with an ICCID and an LPA string — and the link opens a real eSIM page, so you can debug the whole flow, including what your customer will see.

Sandbox and production never mix: a test key cannot see production orders and a production key cannot see test ones. A test eSIM cannot be used for travel — its page says so.

Need access?

Send us a request — we will set you up as an API partner the same day. You issue keys yourself in the console: the sandbox first, then production.

ALO eSIM