Webhook Events
This page documents all webhook event types sent by DirectCryptoPay.
Event Types#
| Event | Description | When It Fires |
|---|---|---|
payment.pending |
Transaction declared, awaiting blockchain confirmations | Customer submits transaction hash |
payment.processing |
Transaction found on-chain, being validated | Transaction detected with partial confirmations |
payment.succeeded |
Payment confirmed on-chain | Transaction reaches required confirmations |
payment.failed |
Payment failed | Transaction reverted or validation failed |
payment.expired |
Payment intent expired | No transaction submitted before expiration |
payment.late_confirmation |
Transaction confirmed after intent expired | Late transaction detected on-chain |
payment.pending#
Sent when a customer declares a transaction hash but it has not yet been found on-chain.
{
"event": "payment.pending",
"intent_id": "cm1abc2def3ghi",
"tx_hash": "0x7a3f8b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0",
"chain_id": 11155111,
"currency": "USDC",
"amount_wei": "49990000",
"pending_at": "2025-01-15T11:30:00.000Z",
"metadata": { "orderId": "ORD-12345" }
}
| Field | Type | Description |
|---|---|---|
event |
string | payment.pending |
intent_id |
string | Payment intent ID |
tx_hash |
string | On-chain transaction hash |
chain_id |
number | EVM chain ID |
currency |
string | Token symbol (e.g., USDC) |
amount_wei |
string | Amount in the token's smallest unit |
pending_at |
string | ISO 8601 timestamp |
metadata |
object | Custom metadata from payment creation |
payment.processing#
Sent when the transaction has been found on-chain but has not yet reached the required number of confirmations.
{
"event": "payment.processing",
"intent_id": "cm1abc2def3ghi",
"tx_hash": "0x7a3f8b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0",
"chain_id": 11155111,
"currency": "USDC",
"confirmations": 1,
"processing_at": "2025-01-15T11:31:00.000Z",
"metadata": { "orderId": "ORD-12345" }
}
| Field | Type | Description |
|---|---|---|
event |
string | payment.processing |
intent_id |
string | Payment intent ID |
tx_hash |
string | On-chain transaction hash |
chain_id |
number | EVM chain ID |
currency |
string | Token symbol |
confirmations |
number | Current number of block confirmations |
processing_at |
string | ISO 8601 timestamp |
metadata |
object | Custom metadata from payment creation |
payment.succeeded#
Sent when a blockchain transaction has been verified and the payment is considered complete. This is the primary event for fulfilling orders.
{
"event": "payment.succeeded",
"intent_id": "cm1abc2def3ghi",
"tx_hash": "0x7a3f8b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0",
"chain_id": 11155111,
"amount_wei": "49990000",
"currency": "USDC",
"confirmations": 3,
"paid_at": "2025-01-15T11:35:00.000Z",
"metadata": { "orderId": "ORD-12345", "plan": "premium" }
}
| Field | Type | Description |
|---|---|---|
event |
string | payment.succeeded |
intent_id |
string | Payment intent ID |
tx_hash |
string | On-chain transaction hash |
chain_id |
number | EVM chain ID (e.g., 137 for Polygon) |
amount_wei |
string | Amount paid in the token's smallest unit |
currency |
string | Token symbol (e.g., USDC, ETH) |
confirmations |
number | Number of block confirmations reached |
paid_at |
string | ISO 8601 timestamp when the payment was verified on-chain |
metadata |
object | Custom metadata you passed when creating the payment |
When to Use#
This is the primary event for fulfilling orders. When you receive payment.succeeded, the payment has been independently verified by the DCP backend via blockchain RPC.
payment.failed#
Sent when a payment could not be completed.
{
"event": "payment.failed",
"intent_id": "cm1abc2def3ghi",
"reason": "transaction_reverted",
"tx_hash": "0x...",
"failed_at": "2025-01-15T12:00:00.000Z"
}
| Field | Type | Description |
|---|---|---|
event |
string | payment.failed |
intent_id |
string | Payment intent ID |
reason |
string | Failure reason (see table below) |
tx_hash |
string? | Transaction hash (if a transaction was submitted) |
failed_at |
string | ISO 8601 timestamp |
Failure Reasons#
| Reason | Description |
|---|---|
transaction_reverted |
The on-chain transaction was reverted |
insufficient_confirmations |
Transaction did not reach required confirmations |
amount_mismatch |
Amount received does not match the expected amount |
payment.expired#
Sent when a payment intent expires before a transaction is submitted.
{
"event": "payment.expired",
"intent_id": "cm1abc2def3ghi",
"expires_at": "2025-01-15T11:45:00.000Z"
}
| Field | Type | Description |
|---|---|---|
event |
string | payment.expired |
intent_id |
string | Payment intent ID |
expires_at |
string | ISO 8601 timestamp when the intent expired |
payment.late_confirmation#
Sent when a transaction is confirmed on-chain after the payment intent has already expired. This can happen if the customer submitted a transaction just before expiration.
{
"event": "payment.late_confirmation",
"intent_id": "cm1abc2def3ghi",
"tx_hash": "0x...",
"chain_id": 11155111,
"currency": "USDC",
"confirmations": 3,
"confirmed_at": "2025-01-15T12:05:00.000Z",
"metadata": { "orderId": "ORD-12345" }
}
Headers#
Every webhook request includes these headers:
| Header | Description |
|---|---|
Content-Type |
application/json |
X-DCP-Signature |
Contains timestamp and HMAC-SHA256 signature in format t=<timestamp>,v1=<hmac_hex> |
User-Agent |
DirectCryptoPay-Webhooks/1.0 |
The X-DCP-Signature header encodes both the timestamp and the signature in a single value. To verify, parse the t= and v1= components, then compute HMAC-SHA256(secret, "<timestamp>.<rawBody>") and compare the hex digest against v1. See the Webhooks guide for full verification examples.
Responding to Webhooks#
Your endpoint should:
- Return a 2xx status code (200, 201, 202, or 204)
- Respond within 10 seconds
Any non-2xx response or timeout triggers a retry according to the retry policy.
200 OK immediately and process the payment asynchronously. This ensures you never hit the 10-second timeout, even if your fulfillment logic is slow.