Accept payments
Invoices API
An invoice is a request to pay a fixed amount in USD. It knows the order, the customer, the network the customer picked, the deposit address and every transfer we saw on chain. Five endpoints cover the whole flow.
Basics
| Base URL | https://pay.axiomis.app/api/v1 |
| Auth | Authorization: Bearer axp_live_… or axp_test_…. The key decides the mode: test keys see only test invoices and test networks. |
| Format | JSON in, JSON out, snake_case fields. Times are ISO 8601 in UTC. |
| Money | USD amounts are integer cents. On-chain amounts (*_units) are strings of base units: USDT on BSC has 18 decimals and does not fit in a JavaScript number. |
| IDs | inv_ plus 24 random characters. The ID is unguessable: the checkout page uses it as the only secret. |
Create an invoice
| Field | Type | Description |
|---|---|---|
amount_usd_cents | integer, required | From 100 to 10000000. 1 USDT is treated as 1 USD. |
external_order_id | string, required | Your order ID, 1 to 128 printable ASCII characters. Several invoices can share it (network change creates a new invoice for the same order). |
customer_ref | string, required | Your customer ID, same format. A customer can have at most 3 open invoices by default (TOO_MANY_OPEN_INVOICES). |
description | string | Shown to the customer, up to 200 characters. |
locale | en | ru | Language of the checkout page. |
success_url | URL | Where the "Back to store" button leads after payment on the hosted page. HTTPS in live mode. |
cancel_url | URL | Where the customer goes to start over (expired or cancelled invoice). HTTPS in live mode. |
metadata | object | Anything you want back in webhooks, up to 4 KB of JSON. Not shown to the customer. |
Idempotency
Send an Idempotency-Key header (up to 255 printable ASCII characters) to make retries safe. The same key within 30 minutes returns the same invoice with status 200 instead of 201. The same key with a different amount, order or customer returns 409 IDEMPOTENCY_KEY_REUSED. A good key is your order ID plus an attempt number.
curl https://pay.axiomis.app/api/v1/invoices \
-H "Authorization: Bearer $AXIOMISPAY_KEY" \
-H "Idempotency-Key: order-1042-1" \
-H "Content-Type: application/json" \
-d '{"amount_usd_cents":2900,"external_order_id":"1042","customer_ref":"user-77"}'Lifetime
A new invoice has no network yet and does not hold a deposit address. The customer picks a network and accepts the terms on the checkout page; only then is an address assigned and the 60-minute timer restarts from that moment. An invoice nobody opened expires 60 minutes after creation.
The invoice object
{
"id": "inv_7Qx2mK9aLp3RtV8sNc4WbY1z",
"object": "invoice",
"mode": "test",
"status": "paid",
"amount_usd_cents": 2900,
"received_usd_cents": 2900,
"external_order_id": "1042",
"customer_ref": "user-77",
"description": "Pro plan, 1 month",
"locale": "en",
"metadata": { "plan": "pro" },
"network": "tron-nile",
"asset": "tron-nile:usdt",
"address": "TJ4fQm1x6cE8hB9zZy3rVpW2nK5sLtGd7A",
"memo": null,
"decimals": 6,
"expected_units": "29000000",
"received_units": "29000000",
"quote": null,
"hosted_url": "https://pay.axiomis.app/i/inv_7Qx2mK9aLp3RtV8sNc4WbY1z",
"embed_url": "https://pay.axiomis.app/embed/i/inv_7Qx2mK9aLp3RtV8sNc4WbY1z",
"created_at": "2026-10-12T09:14:03.000Z",
"expires_at": "2026-10-12T10:16:40.000Z",
"paid_at": "2026-10-12T09:19:52.000Z",
"closed_at": null,
"cancel_reason": null,
"replaced_by": null,
"replaces": null,
"resolution": "auto",
"transfers": [
{
"network": "tron-nile",
"asset": "tron-nile:usdt",
"tx_hash": "9f1c…e42a",
"log_index": 0,
"amount_units": "29000000",
"status": "confirmed",
"confirmations": 19,
"confirmations_required": 19,
"block_number": "61523874",
"counted": true,
"explorer_url": "https://nile.tronscan.org/#/transaction/9f1c…e42a"
}
]
}| Field | Description |
|---|---|
status | One of eight statuses, see Statuses. |
amount_usd_cents | The amount you asked for. |
received_usd_cents | What actually arrived, by confirmed transfers. Use it for overpayments and partial payments. |
network, asset | null until the customer picks. Asset IDs look like tron:usdt, bsc:usdt, base:usdc, ton:usdt, tron-nile:usdt. |
address | The deposit address from your pool, assigned for the lifetime of this invoice. On TON: your own TON wallet address. |
memo | TON only: the transfer comment (AX-…) that identifies this invoice. The customer must include it; null on other networks. |
expected_units, received_units | Amounts in base units of the asset; divide by 10^decimals. |
quote | For native coins (TRX, BNB, ETH, POL, TON): the USD rate and when it expires. null for USDT and USDC. |
replaced_by, replaces | Network change closes the invoice and creates a new one; these fields link them. |
resolution | auto, or manual when our operator confirmed the payment by hand. |
transfers | Every transfer to the address. counted: false means it is linked but not counted (for example a different token). |
Retrieve an invoice
Returns the invoice object. Use it to double-check a status before you ship something expensive, or to poll if webhooks are not set up yet (poll no more often than every 10 seconds).
List invoices of an order
Returns { "data": [Invoice, …] }. An order has more than one invoice when the customer changed the network: the old one is cancelled with cancel_reason: "network_changed" and points to the new one in replaced_by.
Cancel an invoice
Closes an open invoice (for example the customer changed the plan). Works only for pending invoices with no transfer seen yet; otherwise you get 409 PAYMENT_IN_PROGRESS or 409 INVOICE_NOT_CANCELLABLE. Returns the invoice with status: "cancelled" and sends invoice.cancelled.
List networks and assets
{
"data": [
{ "id": "tron-nile:usdt", "network": "tron-nile", "code": "usdt", "kind": "token", "decimals": 6, "testnet": true },
{ "id": "bsc-testnet:usdt", "network": "bsc-testnet", "code": "usdt", "kind": "token", "decimals": 6, "testnet": true }
]
}Networks enabled for your key's mode. The customer chooses among them on the checkout page; you do not pass a network when creating an invoice.
Errors
Errors return a non-2xx status and { "code": "…", "message": "…" }. Build your logic on code: it is stable. message is for logs and may change (during the pilot some messages are in Russian).
| Status | Code | Meaning |
|---|---|---|
| 400 | VALIDATION | A field is missing or out of range; the message names it. |
| 401 | INVALID_API_KEY | Missing, wrong or revoked key. |
| 404 | NOT_FOUND | No such invoice for this key (test keys do not see live invoices and vice versa). |
| 409 | IDEMPOTENCY_KEY_REUSED | The Idempotency-Key was used for a different request. |
| 409 | TOO_MANY_OPEN_INVOICES | This customer already has 3 open invoices. Cancel the old ones or reuse one. |
| 409 | INVOICE_NOT_CANCELLABLE | The invoice is not pending any more. |
| 409 | PAYMENT_IN_PROGRESS | A transfer to the address was already seen; wait for the final status. |
| 429 | RATE_LIMITED | Too many requests. Back off and retry. |
| 5xx | Our side. Retry with the same Idempotency-Key. |
Network timeouts happen. If a create request times out, retry it with the same Idempotency-Key: you will not get a duplicate invoice.