Developers

API documentation

One REST API for the whole eSIM business: browse the wholesale catalog, order from your prepaid wallet, deliver eSIMs and stay in sync with signed webhooks. Free on every plan — create an account and your first key in two minutes.

How Simtasy works in 4 steps

You buy eSIMs at wholesale from your prepaid wallet and deliver them to your customers — through your own app (API) or through a hosted storefront.

  1. 01

    Fund your wallet

    Top up under Wallet. Orders are only accepted when the balance covers the cost — never a negative balance.

  2. 02

    Create an API key

    One key per integration. Send it as a Bearer token with every request.

  3. 03

    Pick a product

    GET /products lists every plan with your B2B price (your plan's discount already applied) and a suggested retail price.

  4. 04

    Order & deliver

    POST /orders charges the wallet and returns the eSIM (ICCID, activation code, QR). Give it to your customer or point them to the hosted eSIM page.

Authentication

Base URL https://simtasy.com/api/v1. All requests need Authorization: Bearer sk_live_…. Keys are scoped to your organisation and can be revoked at any time.

List products for a country
curl https://simtasy.com/api/v1/products?country=JP \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

Wallet

Check your balance before large batches. Top-ups happen in the dashboard or via POST /wallet, which returns a Stripe checkout link.

Balance & recent transactions
curl https://simtasy.com/api/v1/wallet \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

Create an order

The response already contains the eSIM — most orders complete in a few seconds. Pass an Idempotency-Key header (or idempotency_key in the body) so a network retry never buys the same eSIM twice. The optional email triggers a branded delivery e-mail to your customer.

Request
curl -X POST https://simtasy.com/api/v1/orders \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-2026-0001" \
  -d '{
    "product_id": "8c2f3e6e-…",
    "email": "traveller@example.com",
    "name": "Ada Lovelace"
  }'
Response 200
{
  "data": {
    "order_id": "3d7ef0fc-…",
    "status": "completed",
    "cost": 3.84,
    "esim": {
      "iccid": "8944500000000012345",
      "lpa_string": "LPA:1$rsp.example.com$ABC-123",
      "qr_code_url": "https://…/qr.png",
      "status": "ready",
      "access_token": "f1a2…"
    }
  }
}

Order lifecycle

Poll GET /orders/{id} or subscribe to webhooks to follow an order.

  • pendingCreated, not paid yet (storefront orders waiting for Stripe).
  • provisioningPaid; the eSIM is being purchased at the supplier (seconds).
  • completedeSIM delivered — ICCID and activation code are available.
  • failedNot fulfilled. Wallet was refunded if it had been charged. See failure_reason.
  • cancelledCustomer abandoned or payment failed before provisioning.
  • refundedRefunded after completion (manual).

Errors

Errors are JSON with error (human readable) and, where useful, a stable code. The most important one: an unfunded wallet returns 402 and nothing is ordered.

HTTPcodeMeaning
401unauthorizedMissing or revoked API key.
400invalid_bodyValidation failed — see details.
402insufficient_fundsWallet balance too low. Response includes required, available, currency.
404not_foundProduct / order / eSIM does not exist or belongs to another account.
400order_failedSupplier rejected the order (e.g. price changed). Nothing was charged.
429rate_limitedToo many requests — back off and retry with jitter.

Webhooks

Instead of polling, let us call you. Add an HTTPS endpoint under Developers → Webhooks; each delivery is signed with your endpoint secret (HMAC-SHA256 over the raw body in X-Simtasy-Signature). Respond 2xx within 10 seconds; failed deliveries can be retried from the dashboard. Handle duplicates using the event id.

EventSent when
order.completedAn order was provisioned and the eSIM is ready to install.
esim.statusThe supplier reported a lifecycle change (installed, in_use, exhausted, expired…).
esim.usageA new data-usage snapshot was received for an eSIM.
Event envelope
{
  "id": "5f1c9b2e7a3d4c10",
  "event": "order.completed",
  "created_at": "2026-09-14T00:12:31.000Z",
  "data": {
    "orderId": "3d7ef0fc-…",
    "esimId": "…",
    "iccid": "8944500000000012345",
    "status": "completed"
  }
}
Verify & handle (Node.js)
import { createHmac, timingSafeEqual } from "node:crypto";

export async function POST(req: Request) {
  const raw = await req.text();                       // use the RAW body
  const signature = req.headers.get("x-simtasy-signature") ?? "";
  const expected = createHmac("sha256", process.env.SIMTASY_WEBHOOK_SECRET!)
    .update(raw)
    .digest("hex");

  const ok =
    signature.length === expected.length &&
    timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
  if (!ok) return new Response("invalid signature", { status: 400 });

  const event = JSON.parse(raw);                     // { id, event, created_at, data }
  switch (event.event) {
    case "order.completed":
      // mark the order as delivered in your system
      break;
    case "esim.usage":
      // update the remaining data shown to your customer
      break;
  }
  return new Response("ok");                          // respond 2xx within 10 s
}

eSIM usage & lifecycle

Show your customers how much data is left and whether the eSIM is installed.

Live usage for an ICCID
curl https://simtasy.com/api/v1/esims/8944500000000012345/usage \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

GET /esims/{iccid} returns the stored eSIM (status, activation code, QR, expiry). GET /esims/{iccid}/usage fetches fresh usage from the network and stores a snapshot. Top-ups (where the product supports them) are available under /esims/{iccid}/topups.

Every eSIM also has a hosted customer page (the access_token in the order response): https://simtasy.com/esim/<token>, or on your storefront domain for storefront orders — QR code, installation steps and usage, no login required.

Good to know

Prices

wholesale_price in the product list is what you pay — your plan's discount is already applied. retail_price is the suggested end-customer price from your pricing rules — you may charge whatever you like.

Rate limits & retries

Be reasonable (a few requests per second). On 429 or 5xx, retry with exponential backoff. Because POST /orders is idempotent with a key, retries are safe.

Reference

Full endpoint reference (OpenAPI): simtasy.com/docs · raw spec at /api/v1/openapi.

Endpoint reference

Interactive reference generated from our OpenAPI specification. Raw spec: /api/v1/openapi.