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
Register a webhook
Stop polling: take order.fulfilled as it happens, and verify the signature.
Fund the wallet
Fund once, then every purchase is a single call that cannot wait.
Errors
One error shape, and the codes worth handling by name.
Test mode
Every sandbox tool, including deposits and forced failures.
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.