Checkout Webhook Events
Status notifications for your checkouts. Register a CHECKOUT endpoint from the dashboard (Devtools → Webhooks).
Registration, signature verification, retries, and URL rules: Webhooks.
Events
| Event | Fires when |
|---|---|
checkout.updated | A checkout reaches COMPLETED or FAILED |
FAILED covers both failure and expiry (20 minutes). CANCELLED and REJECTED do not fire; reconcile those outcomes with Get Checkout.
Payload
{
"eventId": "checkout.updated:8f14e45f-ceea-467f-a830-5e3e3c7e2b8a:COMPLETED",
"event": "checkout.updated",
"webhookType": "CHECKOUT",
"timestamp": "2026-02-03T15:05:30.000Z",
"data": {
"checkoutId": "8f14e45f-ceea-467f-a830-5e3e3c7e2b8a",
"status": "COMPLETED",
"statusReason": null,
"amount": "15000",
"fee": "0",
"netAmount": "15000",
"tokenId": "550e8400-e29b-41d4-a716-446655440000",
"txHash": "0x5d53558791c9346d644d077354420f9a93600acf54eb806ecb9aad077c103ee3"
}
}The outer fields are the shared envelope. data carries:
| Field | Type | Description |
|---|---|---|
checkoutId | string (UUID) | Matches id from GET /v1/checkout/{id} |
status | string | COMPLETED or FAILED |
statusReason | string | null | Stable key when status is FAILED, otherwise null |
amount | string | Amount in sen |
fee | string | Fee in sen. Always "0" on a BLOX_ACCOUNT checkout, and on an FPX checkout unless BLOX has configured a checkout fee for your account — see Fees |
netAmount | string | What settles on-chain. Credit the customer against this, not amount — under DEDUCT_FROM_AMOUNT they differ, and a CHARGE_TO_PREFUND checkout falls back to deducting if your prefund cannot cover the fee |
tokenId | string (UUID) | Token delivered — same ids as GET /v1/wallet/networks |
txHash | string | null | The on-chain transfer, once one exists |
Amounts are strings in sen, matching the REST response.
Handling
// eventId is unique per (checkout, status) — a repeat is a redelivery.
if (await seen(payload.eventId)) return respond(200);
if (payload.data.status === "COMPLETED") {
// fulfill must also be idempotent on checkoutId.
await fulfill(payload.data.checkoutId, payload.data.netAmount);
}
await record(payload.eventId);
return respond(200);- Acknowledge with
2xxwithin 10 seconds; do the work asynchronously - Key idempotency on
eventId - Make fulfillment idempotent on
checkoutIdso a crash between fulfillment and recording the event cannot credit twice - Treat
GET /v1/checkout/{id}as the source of truth for fulfillment