Skip to main content
When an invoice reaches a terminal state, we send one POST to its callback_url. This webhook is the source of truth for fulfillment — act on it, not on the customer returning to your success_url.

What you need to do

1

Expose an HTTPS endpoint

Set it as callback_url on the invoice, or as your business default. It must accept POST with a JSON body.
2

Verify the signature

Confirm the request is from us before trusting it (see Verifying).
3

Respond 2xx quickly

Any 2xx marks the delivery as received. Do heavy work asynchronously — acknowledge first, process after.
4

Process idempotently

The same event may arrive more than once. Deduplicate on X-Webhook-Id (or invoice_uuid), and treat a repeat as a no-op.

Events

One terminal webhook per invoice.

Payload

invoice.paid:
invoice.canceled:
Our order id. Your order id. success for paid; expired / canceled / fail for canceled. For invoice.paid, the actual paid fiat. For invoice.canceled, the original order amount. USDT credited to you. Present on invoice.paid only. Whether the paid amount was corrected. The originally ordered fiat. Present only when adjusted is true.

Adjustments

On invoice.paid, always check adjusted. When it is true, the amount is what the customer actually paid — not what you ordered (original_amount), and credited_usdt is what actually landed. Fulfill against credited_usdt, not the original order value, or your books will drift. A paid invoice does not guarantee full payment.

Headers

Every delivery carries:

Verifying the signature

The signature is HMAC-SHA256 over the string {timestamp}.{raw_body}, keyed with your webhook secret (from the dashboard). Compute it over the raw request body and compare against X-Webhook-Signature with a constant-time check.
Reject the request if the signature doesn’t match, or if X-Webhook-Timestamp is older than a few minutes. The timestamp is inside the signed string, so an attacker can’t replay an old body with a fresh timestamp.

Retries

A delivery succeeds on any 2xx. Anything else — a non-2xx status, a timeout (15s), or a connection error — is retried on a backoff schedule. By default there are 7 attempts: the first immediately, then after 1, 5, 30, 120, 360, and 720 minutes. The last attempt lands roughly 20.6 hours after the event. If all attempts fail, the delivery is marked failed — resend it manually once your endpoint is healthy.

Manual resend

POST /external/invoices/{ident}/resend-webhook Sends the latest delivery for the invoice now — expedites it if still queued, or starts a fresh attempt if it already failed or delivered. {ident} is the order_id or your external_id.

Delivery history

GET /external/invoices/{ident}/webhooks Returns the invoice’s deliveries, each with its full attempt history — useful to debug failures (status codes, errors, timings).
Each delivery includes: Delivery id (matches X-Webhook-Id). invoice.paid or invoice.canceled. pending, delivering, delivered, or failed. Attempts made so far, out of max_attempts. HTTP status of the most recent attempt. When the next retry is scheduled (if any). When a 2xx was received. Per-attempt log: status_code, success, error, duration_ms, triggered_by, created_at.