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:
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 isHMAC-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 any2xx. 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).
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..webp?fit=max&auto=format&n=O6SgNi-K863CxPv7&q=85&s=5a022604a7f294c9858b55256ba8a593)
