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
Important: Verify
X-Webhook-Signaturebefore parsing the JSON. Read the exact raw request body without parsing or reserializing it. Compute HMAC-SHA256 using the webhook signing secret as the key, encoded as UTF-8 text without hex-decoding it. Encode the digest as hexadecimal and compare it with the value after thesha256=prefix using a constant-time comparison.After verification, require every listed header and confirm that
X-Webhook-Event-Id,X-Webhook-Event-Type, andX-App-Networkmatch the payload. Reject missing, malformed, or mismatched values.The signature covers the request body; delivery headers are outside that signature. A timestamp-age check alone does not establish whether an event has already been processed. Event deduplication is described in Acknowledgement and Retries.
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. Settlement updates are available through the Settlements API rather than a separate webhook event.
Using Events for Business Decisions
Use webhooks as notifications of payment or order activity. Before fulfillment, or if an expected notification has not arrived, check the current order through the Orders API.
order.payment.received_fullpaid means that the recorded payment amount reached
the checkout amount. order.payment.confirmed indicates that a recorded payment
reached Stafiel's confirmation threshold. The event's data.confirmation
object contains the confirmation details. The data.order.status field reports
the order status carried with the event; it is not itself a confirmation signal.
For refund events, use the Refund API and the current order state when updating your own refund records.
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 label used in the webhook payload. It may differ from the chain label used when creating the checkout session. |
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. |
Correlate webhook events with your order records using network and
data.order.id.
Acknowledgement and Retries
Stafiel treats 2xx responses as successful delivery. Network errors, timeouts,
408, 425, 429, and 5xx responses are eligible for retry. Other
unsuccessful HTTP responses end delivery attempts for the event. Redirects are
not followed.
Receivers generally persist an event before returning 2xx. A temporary
acceptance failure can be reported with a retryable response.
Idempotent handling can use (network, eventId) as the deduplication key and
avoid repeating the same business side effect. Once a duplicate is recognized,
the receiver can acknowledge it with 2xx.
Delivery attempts use the same verification process. A retry keeps the same
payload and event ID, while X-Webhook-Delivery-Attempt increases and
X-Webhook-Timestamp records the new delivery attempt.
Note: Normal delivery uses a bounded attempt budget: the initial delivery plus up to four retry attempts. Retry delays generally increase between attempts and may be affected by
Retry-After, queue lag, or delivery controls. This is delivery behavior, not a delivery-time SLA.

