Events & money movement
Webhooks.
Axle calls your server when something happens, with a signed JSON event. Verify the signature, respond quickly with a 2xx, and de-duplicate on the event ID.
Registering an endpoint
Add endpoints on the merchant's Webhooks tab. Each endpoint has a URL and the list of event types it wants.
- The URL must be HTTPS. Axle re-checks it at delivery time and refuses to call private or internal addresses.
- Subscribe to event types by their exact name. There's no wildcard: an endpoint only receives the types it lists.
- A signing secret is shown once, when the endpoint is created. Store it as a secret in your own environment; it can't be shown again.
Event types
| Type | Fires when |
|---|---|
payment.succeeded | A payment has been verified on-chain and is final. This is your fulfilment signal. |
payment.failed | The on-chain transaction failed or didn't match. |
payment.canceled | The merchant canceled a pending payment. |
refund.processed | A refund transaction was verified on-chain. |
dispute.opened | A dispute was opened (disputes sandbox). |
dispute.resolved | A dispute was resolved (disputes sandbox). |
Payload
Every delivery is an HTTP POST with a JSON body and these headers:
data.object is the payment record at the moment of the event. It doesn't include the nested crypto object, so call GET /v1/payments/:id when you need the transaction hash or confirmation count. created is a Unix timestamp in seconds.
Verifying signatures
Anyone can POST to your URL, so verify every request. The signature covers the timestamp and the exact raw body:
- Parse the header
Axle-Signatureintot(Unix seconds) andv1(hex). - Compute
HMAC-SHA256(secret, t + "." + rawBody)and hex-encode it. - Compare with
v1using a constant-time comparison. - Reject the request if
tis more than 5 minutes from your clock, so a captured request can't be replayed later.
Node.js
Python
Delivery and retries
| Your response | What Axle does |
|---|---|
Any 2xx | Marks the delivery delivered. |
| Non-2xx, or a network error | Retries with exponential backoff, up to 5 attempts in total, starting at 5 seconds. |
| A redirect (3xx) | Treated as a permanent failure and not retried. Axle never follows redirects. |
- Respond fast. Do the minimum to record the event and return 200, and do slower work afterwards.
- De-duplicate. Retries mean you can receive the same event more than once. Use
idas your idempotency key. - Don't rely on ordering. Events can arrive out of order across retries. When it matters, re-read the payment.
Polling as a fallback
If you can't receive webhooks, or want to backfill after downtime, list events with your API key. Results are cursor-paginated.
Pass limit (1 to 100, default 20) and startingAfter (an event ID) to page through. See Errors & limits for the rate limit that applies.