Skip to Content
DocumentationCheckouts

Checkouts

The Checkout API lets you create payment sessions, check their status, and list past checkouts.

Base URL: https://api.inzilink.com/api/v1

All requests require an API key in the Authorization header. See Authentication.


Create checkout

Creates a new payment session and returns a hosted checkout page URL.

POST /v1/checkouts

Request body

FieldTypeRequiredDescription
amountstringYesPayment amount. String to avoid float precision issues.
currencystringYesISO 4217 code. Currently supported: USD.
descriptionstringNoShown on checkout page. Max 256 chars.
metadataobjectNoArbitrary JSON (max 4KB). Returned in webhooks untouched.
customer_emailstringNoFor future receipt functionality.
redirect_urlstringNoWhere to redirect user after payment.
webhook_urlstringNoOverride merchant-level default webhook URL.
expires_inintegerNoSeconds until expiration. Default: 1800. Min: 300, Max: 86400.
customer_refstringNoYour id for the buyer. Max 128 chars. See References.
product_refstringNoYour id for what is being bought. Max 128 chars.
order_refstringNoYour id for this order. Makes creation idempotent.

References

Three optional strings that let us tell your purchases apart. They are opaque to us — never validated, displayed, or resolved against anything. We only ever compare them to each other.

They are worth sending, and here is precisely what each one buys.

order_ref makes this endpoint idempotent. Call it twice with the same order_ref, same amount and same currency, and you get the same checkout back rather than a second one. A different amount always creates a new checkout — so if you re-price an order and retry, the buyer is never shown a figure that differs from what they will be charged.

customer_ref and product_ref rescue a payment we would otherwise have to leave unattributed. Outside Base and Polygon, a checkout is identified by its exact amount: $7.38 becomes $7.381, and the fractional part is the invoice number. Wallets and exchange withdrawal forms routinely round it away.

If a customer has one live checkout and pays the round $7.38, we credit it. If they have several at the same price — usually because they pressed pay more than once — the round amount fits all of them, and we cannot tell which. That payment goes uncredited until someone intervenes.

With both references present we can: checkouts naming the same customer and the same product at the same price are interchangeable, so crediting any one of them delivers what that person paid for. We require both. Two products can cost the same and be entirely different things, so a customer alone is not enough — and getting this wrong means shipping someone the wrong goods.

Make product_ref distinguish anything a buyer would notice. If one code covers several variants, put the variant in the string: PLAN-A:7d and PLAN-A:30d rather than PLAN-A for both.

{ "amount": "7.38", "currency": "USD", "customer_ref": "user_6b1f2c", "product_ref": "PLAN-A:7d", "order_ref": "ord_9d41e0" }

If you cannot add top-level fields, we also read them from metadata — see Reading references from metadata below.

curl -X POST https://api.inzilink.com/api/v1/checkouts \ -H "Authorization: Bearer sk_live_xxxxx" \ -H "Content-Type: application/json" \ -d '{ "amount": "25.00", "currency": "USD", "description": "Order #12345", "metadata": { "order_id": "uuid-xxx", "user_id": "uuid-yyy" }, "redirect_url": "https://yoursite.com/thank-you", "webhook_url": "https://yoursite.com/webhooks/inzi", "expires_in": 1800 }'

Response 201 Created

{ "id": "chk_live_abc123def456", "status": "pending", "amount": "25.00", "currency": "USD", "description": "Order #12345", "metadata": { "order_id": "uuid-xxx", "user_id": "uuid-yyy" }, "checkout_url": "https://pay.inzilink.com/c/abc123def456", "available_chains": [ { "chain": "solana", "name": "Solana", "tokens": ["USDC"], "estimatedConfirmSec": 1, "gasTier": "low", "priority": 0, "badge": "Fastest & cheapest" }, { "chain": "polygon", "name": "Polygon", "tokens": ["USDC", "USDT"], "estimatedConfirmSec": 24, "gasTier": "low", "priority": 1, "badge": null } ], "expires_at": "2026-04-04T12:30:00Z", "created_at": "2026-04-04T12:00:00Z" }

Redirect the user to checkout_url — a full page navigation, not an iframe.

The checkout page cannot be embedded. It sets frame-ancestors to the Telegram domains only, so an <iframe> or a modal built on one renders blank and the browser reports it only in the console. The page shows the amount and the payment address, which is what clickjacking targets — Telegram Mini Apps are the single exception, because they have no other way to open a page.

They’ll see a hosted payment page with chain/token selector, QR code, and copy-paste address.

available_chains is sorted by priority (0 = recommended). Use badge, gasTier, and estimatedConfirmSec to build a custom network selector in your own checkout UI. You can also call GET /v1/chains (public, no auth) to get chain metadata independently.

Amount tolerance: Inzi accepts payments within max(1%, $0.05) of the requested amount to account for network rounding.


Checking what we recorded

Both the create response and GET /v1/checkouts/{id} echo back what we stored, so you can confirm the protection is on without asking us:

"references": { "customer_ref": "user_6b1f2c", "product_ref": "PLAN-A:7d", "order_ref": "ord_9d41e0", "ambiguity_resolution_armed": true }

ambiguity_resolution_armed is true when both customer and product are present — the condition under which several matching checkouts can be collapsed and credited. order_ref is observable anyway, since idempotency shows itself; the other two do nothing visible until a payment goes wrong, which is why they are reported here.

Reading references from metadata

If adding top-level fields is awkward, put the same values in metadata and we will find them. This exists for a real case: some integrations redirect the buyer to our hosted checkout page, so the call that starts the payment is made by us, not by you — creation is the only moment you are on the wire.

We look for these keys, in order, and accept either snake_case or camelCase:

ReferenceKeys read from metadata
customer_refcustomer_ref, user_id
product_refproduct_ref, package_code
order_reforder_ref, reference_id, order_id

Top-level fields win if both are present. Numbers are accepted and read as strings, since user ids are often integers.

{ "amount": "7.38", "currency": "USD", "metadata": { "user_id": "user_6b1f2c", "package_code": "PLAN-A:7d", "reference_id": "ord_9d41e0" } }

Unknown keys in metadata are ignored, not rejected. Adding these cannot break an existing integration.

Get checkout

Retrieve the current state of a checkout. Use as fallback when a webhook doesn’t arrive.

GET /v1/checkouts/{checkout_id}

Response 200 OK

{ "id": "chk_live_abc123def456", "status": "completed", "amount": "25.00", "currency": "USD", "description": "Order #12345", "metadata": { "order_id": "uuid-xxx", "user_id": "uuid-yyy" }, "checkout_url": "https://pay.inzilink.com/c/abc123def456", "payment": { "amount_crypto": "25.000000", "crypto_currency": "USDT", "network": "ton", "tx_hash": "abc123...", "sender_address": "UQ...", "confirmed_at": "2026-04-04T12:05:00Z" }, "expires_at": "2026-04-04T12:30:00Z", "created_at": "2026-04-04T12:00:00Z" }

The payment object is null when status is pending or expired.

Checkout statuses

StatusDescription
pendingAwaiting payment
processingPayment detected, awaiting confirmations
completedPayment confirmed on-chain
expiredNot paid before expires_at
underpaidReceived less than required amount (customer can send more to the same address)

List checkouts

Paginated list of your checkouts. Useful for dashboards and reconciliation.

GET /v1/checkouts?status=completed&limit=20&offset=0

Query parameters

ParamTypeDescription
statusstringFilter by status. Optional.
limitintegerMax results. Default: 20, Max: 100.
offsetintegerPagination offset.
created_afterdatetimeFilter: created after this ISO 8601 timestamp.
created_beforedatetimeFilter: created before this ISO 8601 timestamp.

Response 200 OK

{ "data": [ { "id": "chk_live_abc123", "status": "completed", "amount": "25.00", "..." : "..." }, { "id": "chk_live_def456", "status": "pending", "amount": "10.00", "..." : "..." } ], "total": 142, "limit": 20, "offset": 0 }