Tizondocs

Quickstart

From sign-up to an installable eSIM, on test credentials and without real money.

The goal of this page: an eSIM you could install on a handset, bought with a test key, in a few minutes. No KYC, no funding, nothing real.

It buys with payment_source: lightning rather than from a wallet balance, because that is the path that needs no float — the quote mints an invoice for its own price, and paying it settles the purchase. It is also how you would sell to a real customer at a real checkout. See selling without a float.

Test keys are issued as soon as you sign up. Live keys unlock after KYC — see going live at the end.

Get a test key

Sign up in the dashboard and copy the test key. It looks like tz_test_…, and it goes in the Authorization header on every request:

export TIZON_KEY="tz_test_..."
curl https://api.tizon.mobile/v1/account \
  -H "Authorization: Bearer $TIZON_KEY"

The prefix is what selects the mode, so there is no separate sandbox host to remember.

Find an offer

GET /v1/offers is the catalog. Filter it by category and country:

curl -G https://api.tizon.mobile/v1/offers \
  -H "Authorization: Bearer $TIZON_KEY" \
  -d category=esim \
  -d country=JP \
  -d limit=5
{
  "object": "list",
  "data": [
    {
      "id": "off_jp_5gb_30d",
      "object": "offer",
      "category": "esim",
      "name": "Japan 5GB 30 days",
      "coverage": { "type": "country", "countries": ["JP"] },
      "data": { "amount": 5, "unit": "GB", "unlimited": false },
      "validity_days": 30,
      "price": { "amount": 1450, "currency": "USD" },
      "requires_destination": false,
      "status": "active"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

price is the binding amount a quote will debit — $14.50 here, since amounts are integer cents. Pass display_currency=NGN to get an extra indicative display amount for presentation; it is never used for settlement.

Buy it with a Lightning invoice

The shortest path to a real purchase is to let the quote mint its own invoice, so there is nothing to fund first:

curl https://api.tizon.mobile/v1/quotes \
  -H "Authorization: Bearer $TIZON_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "offer_id": "off_jp_5gb_30d",
    "payment_source": "lightning"
  }'

The quote comes back awaiting_funds, and payment.lightning.bolt11 is the whole instruction — an invoice for exactly this quote's price, which expires and cannot be reused:

{
  "id": "qt_01J9Z8M9S346Q3D25VT4F5V37E",
  "object": "quote",
  "status": "awaiting_funds",
  "offer_id": "off_jp_5gb_30d",
  "payment_source": "lightning",
  "price": { "amount": 1450, "currency": "USD" },
  "payment": {
    "rail": "lightning",
    "amount": { "amount": 1450, "currency": "USD" },
    "asset": null,
    "asset_amount": null,
    "destinations": [],
    "lightning": {
      "bolt11": "lnbc144u1p...",
      "payment_hash": "9f2a...",
      "amount_sats": 22656
    }
  },
  "expires_at": "2026-10-02T12:15:00Z",
  "order_id": null
}

The payer sends satoshis; the wallet is credited the USD amount whatever the rate does in between. amount_sats is indicative only.

Every POST that moves money needs an Idempotency-Key. Retrying with the same key returns the original response instead of buying twice. See idempotency.

Pay it, without paying it

A test key cannot receive a real Lightning payment, so ask the sandbox to pretend one arrived. The invoice id is in the quote's payment.lightning:

curl -X POST \
  https://api.tizon.mobile/v1/test_helpers/lightning_invoices/{invoice_id}/pay \
  -H "Authorization: Bearer $TIZON_KEY"

This runs the same ingestion path a real payment would: the wallet is credited, the quote settles, and an order is created. Test helpers refuse a live key with 403 test_mode_only, so this can never touch real money.

Collect the eSIM

Fulfillment happens in the background. Poll the order, or — better — wait for the order.fulfilled webhook:

curl https://api.tizon.mobile/v1/orders/{order_id} \
  -H "Authorization: Bearer $TIZON_KEY"
{
  "id": "ord_01J9Z83S3E28JT97KB6CQ643DZ",
  "object": "order",
  "quote_id": "qt_01J9Z8M9S346Q3D25VT4F5V37E",
  "offer_id": "off_jp_5gb_30d",
  "status": "fulfilled",
  "price": { "amount": 1450, "currency": "USD" },
  "deliverable": {
    "type": "esim_profile",
    "iccid": "8910300000012345678",
    "lpa": "LPA:1$smdp.example.com$MBGNW-2IMOY-XAKHK-CPNCJ",
    "activation_code": "MBGNW-2IMOY-XAKHK-CPNCJ",
    "smdp_address": "smdp.example.com"
  },
  "failure_reason": null,
  "resolved_at": "2026-10-02T11:47:12Z"
}

Render lpa as a QR code and the handset installs from it. activation_code and smdp_address are the same thing for a customer typing it in by hand.

If fulfillment fails the order is failed, failure_reason says why, and the price is already back in the wallet. You are never charged for an eSIM you did not get.

What to build next

Going live

Submit the KYB form in the dashboard. Tizon then emails a KYC form, and the live key unlocks on approval — the dashboard shows the state as test only, KYC pending, then live. Post-pay and overdraft are a separate approval on top of live; see overdraft.

Switching to live is a change of key, not of code: same base URL, same endpoints.

On this page