Invoices
A request for an exact amount, its statuses, how long it stays open, and what each status means for you.
An invoice is a fiat price ("30.00 USD") turned into one or more payment options:
an exact amount of an asset on a network, and the address to send it to. You create it on your server
with POST /v1/invoices, and read it back with GET /v1/invoices/{id} or from your webhooks.
Statuses
The status is derived from what has actually arrived on the chain, every time something changes. It is never set by hand, and never from anything the payer tells the checkout.
| Status | Meaning | What you do |
|---|---|---|
awaiting_payment | Nothing seen yet. | Wait. |
detected | A payment was seen on the chain, nothing is final yet. | Show "payment received, confirming". Do not ship. |
confirming | The same, with confirmations arriving. The webhook stays invoice.detected: confirmations alone send nothing. | Wait. |
partially_paid | Final funds arrived, but less than the amount due (minus your tolerance). The payer can top up. | Wait, or refund if it expires. |
paid | The amount due arrived and is final. | Fulfil the order. |
overpaid | Paid, with an excess of at least 0.01 of the stablecoin (any excess, on a BTC-only invoice). | Fulfil; refund the excess if you want to. |
paid_late | Paid, but the payment that completed it arrived after the detection window, or (for a volatile asset) after the price lock. | Fulfil. |
expired | The window closed without full payment. Anything that did arrive is refundable. | Cancel the order; refund partial payments. |
under_review | A payment is held for a person to look at (see reviews). Nothing more is credited and the clock stops. | Accept or reject it. |
cancelled | You cancelled it before anything arrived, or rejected a held payment. | Nothing, or refund what arrived. |
paid, overpaid and paid_late all mean "paid": treat the three alike when you fulfil. Each status
change sends invoice.<status> (for example invoice.paid) to your webhook, except
confirming. A paid invoice never expires afterwards, but a chain reorganisation can undo a credit: you
then get invoice.payment_reversed and the status steps back. That is why finality matters
(finality per chain).
How long an invoice stays open
expires_at is the end of the detection window. It is set when the invoice is created and cannot be
changed per invoice:
| Invoice offers | Window | Price lock |
|---|---|---|
| Stablecoins only | 24 hours | none: a stablecoin is priced 1:1 |
| Any volatile asset (BTC), even alongside stablecoins | 60 minutes | 15 minutes (price_locked_until) |
A payment that arrives in the 10 minutes after expires_at still counts, as paid_late. A payment that
was already seen inside the window but is still confirming gets up to 6 more hours to become final. After
that the invoice is expired, and later arrivals are late payments.
Creating one
{
"amount": "30.00",
"currency": "USD",
"assets": ["USDT@tron:nile", "USDT@eip155:97"],
"description": "Order #1001",
"customer_ref": "cus_8812",
"metadata": { "order_id": "1001" }
}amountandcurrency: the price, as a decimal string in the currency's major unit.assets: which payment options to offer. Leave it out to offer your account'sdefault_assets.description: shown to the payer, at most 500 characters.customer_ref(at most 200 characters) andmetadata(at most 20 pairs, keys up to 40 characters, values up to 500): yours only, never shown to the payer.
Unknown fields are refused, so a typo is an error rather than a silently ignored option.
Idempotency
Every POST takes an Idempotency-Key header (1 to 255 visible ASCII characters). Use one key per
logical operation, such as your order id:
- Same key, same request (method, path and body, byte for byte): you get the stored response again,
marked
Idempotent-Replayed: true. Nothing is created twice. - Same key, different request:
409withidempotency_key_reused. - Same key while the first request is still running:
409withidempotency_key_in_use. Retry shortly. - A
5xxresponse is not stored, so retrying it runs the request again.
Keys are remembered for 24 hours.
Cancelling
POST /v1/invoices/{id}/cancel works only while nothing at all has been seen for the invoice: after
that it is 409 invoice_not_cancellable, because money may already be on its way. Cancelling twice is
harmless. The address goes into its cooldown rather than straight back into use, so a payment that still
arrives is attributed to this invoice and becomes a refundable late payment.