Webhooks and Events
Receive signed webhook events from Stafiel.
Receive signed, asynchronous order events from Stafiel at your HTTPS endpoint.
Configure the Endpoint
In the Merchant Dashboard, open **Merchants > select merchant > Merchant Settings
Webhook Configuration**. Save a public HTTPS endpoint. The signing secret is generated on first save and shown in the same section.
Delivery Headers
Every webhook delivery includes the following headers:
| Header | Description |
|---|---|
X-Webhook-Signature |
HMAC-SHA256 signature for the raw request body, formatted as sha256=<hex>. |
X-Webhook-Event-Id |
Event ID within the network identified by X-App-Network. |
X-Webhook-Event-Type |
Canonical event type, such as order.payment.received_fullpaid. |
X-Webhook-Timestamp |
Delivery-attempt time as a Unix timestamp in milliseconds, for example 1782777600000. |
X-Webhook-Delivery-Attempt |
Delivery attempt number for this event, starting at 1. |
X-App-Network |
Event network: mainnet or testnet. |
Signature Verification
X-Webhook-Signature authenticates the exact raw request body. Verify it before
parsing the JSON. Compute HMAC-SHA256 with your webhook signing secret and
compare the result using a constant-time comparison.
After verification, require every listed header and confirm that
X-Webhook-Event-Id, X-Webhook-Event-Type, and X-App-Network match the
payload. Reject missing, malformed, or mismatched values.
Event Types
Handle event types as exact strings.
| Event Type | When It Is Sent |
|---|---|
order.payment.detected |
Stafiel detects an incoming payment for the order. |
order.payment.received_fullpaid |
Recorded payment reaches the expected order amount. |
order.payment.received_underpaid |
Recorded payment is below the expected order amount. |
order.payment.received_overpaid |
Recorded payment is above the expected order amount. |
order.payment.confirmed |
A recorded payment reaches Stafiel's confirmation threshold. |
order.after_sales.available |
After-sales functionality becomes available for the order. |
order.expired |
The order enters the expired state. |
order.closed |
The order is closed. |
order.compliance_hold |
The order is held by risk or compliance controls. |
order.refund.by_merchant |
A merchant refund is completed. |
order.refund.by_platform |
A platform-initiated refund is completed. |
For payment stages and merchant handling guidance, see Payment Flow and Order Lifecycle and Payment Status.
Webhook Payload Example
{
"event": "order.payment.received_fullpaid",
"eventId": "wh_your_event_id",
"timestamp": "2026-06-30T00:00:00.000Z",
"network": "mainnet",
"data": {
"order": {
"id": "ord_your_order_id",
"merchantId": "mch_your_merchant_id",
"chainId": 8453,
"chainName": "base-main",
"tokenAddress": "0x...",
"tokenSymbol": "USDC",
"amountUsd": "49.00",
"orderAddress": "0x...",
"status": "fullpaid",
"totalPaidAmountUsd": "49.00",
"expiresAt": "2026-06-30T00:15:00.000Z",
"createdAt": "2026-06-29T23:30:00.000Z",
"metadata": {
"customerReference": "CUS-8842"
}
},
"oldBalance": "0.00",
"newBalance": "49.00",
"delta": "49.00"
}
}
Webhook Payload Fields
| Field | Type | Description |
|---|---|---|
event |
string | Canonical event type. See Event Types. |
eventId |
string | Event ID within the payload's network. |
timestamp |
string | Event creation time in ISO 8601 format, for example 2026-06-30T00:00:00.000Z. |
network |
string | mainnet or testnet. |
data |
object | Contains data.order and any applicable conditional fields listed below. |
data.order.id |
string | Stafiel order ID. |
data.order.merchantId |
string | Merchant ID that owns the order. |
data.order.chainId |
number or null | Numeric chain ID when available. |
data.order.chainName |
string or null | Normalized chain name, such as base-main, solana-main, or bsc-testnet. |
data.order.tokenAddress |
string or null | Token contract or mint address. |
data.order.tokenSymbol |
string or null | Token symbol. |
data.order.amountUsd |
string or null | Original checkout amount. |
data.order.orderAddress |
string or null | Payment contract or payment address. |
data.order.status |
string | Order status for this event. |
data.order.totalPaidAmountUsd |
string or null | Total recorded payment amount when token decimals are known. |
data.order.expiresAt |
string or null | ISO 8601 expiration timestamp. |
data.order.createdAt |
string or null | ISO 8601 order creation timestamp. |
data.order.metadata |
object or null | Sanitized order metadata. |
Conditional Payload Fields
These fields are included only for the listed events:
| Field | Events | Description |
|---|---|---|
data.oldBalance, data.newBalance, data.delta |
order.payment.detected, order.payment.received_fullpaid, order.payment.received_underpaid, order.payment.received_overpaid; also order.expired when a recorded balance is available |
Previous balance, current balance, and balance change as decimal strings. |
data.confirmation |
order.payment.confirmed |
Contains transactionHash, blockNumber, confirmationCount, and confirmedAt. |
data.contract |
order.after_sales.available |
Contains contractAddress, transactionHash, and contractStatus. |
data.refund |
order.refund.by_merchant, order.refund.by_platform |
Contains refundType, refundAmountUsd, transactionHash, recipients, chainId, and notes. |
Acknowledgement and Retries
Return a 2xx response after durably accepting the event; no response body is
required. Redirects are not followed. Redirects, non-2xx responses, timeouts,
and connection failures are retried.
Use (network, eventId) as the durable idempotency key. Apply each event once and
return 2xx for duplicates. A retry keeps the same signed payload and eventId;
X-Webhook-Delivery-Attempt increases and X-Webhook-Timestamp records the new
delivery time. Verify every delivery independently.
Events may be duplicated, delayed, or delivered out of order. Process each event against the current order state.