Event types
BchainPay delivers webhook events as JSON to the endpoint URLs you configure in the dashboard. Each delivery is an envelope containing the event metadata and the full resource snapshot at the time of the event.
Event envelope
Every webhook POST body has this shape:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"event_type": "payment_intent.completed",
"created_at": "2026-04-27T12:08:11Z",
"data": { /* full payment intent resource */ }
}| Field | Type | Description |
|---|---|---|
id | string | Unique event identifier (UUID). Use for dedup. |
event_type | string | Dot-separated event type (see table below). |
created_at | string | ISO 8601 timestamp of when the event was created. |
data | object | Full API resource snapshot at the moment the event was produced. |
Event types
| Type | Fires when |
|---|---|
payment_intent.created | POST /v1/payment-intents succeeds and a new intent is persisted. |
payment_intent.address_generated | POST /v1/payment-intents/:id/generate-address assigns a deposit address. |
payment_intent.confirmed | POST /v1/payment-intents/:id/confirm succeeds and monitoring begins. |
payment_intent.payment_detected | An on-chain transaction matching the intent is detected. |
payment_intent.completed | Blockchain payment reaches the required confirmation threshold. |
payment_intent.expired | The intent passes its expiry time without completing. |
payment_intent.failed | The intent fails (e.g. wallet lease expires before payment arrives). |
Example payload — payment_intent.completed
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"event_type": "payment_intent.completed",
"created_at": "2026-04-27T12:08:11Z",
"data": {
"id": "f8d9a1b2-c3d4-5678-abcd-ef9012345678",
"status": "completed",
"merchant_id": "11111111-2222-3333-4444-555555555555",
"amount_cents": 4999,
"received_amount_cents": 4999,
"remaining_amount_cents": 0,
"currency": "USD",
"settlement_currency": "USDC",
"network": "ethereum",
"token": "USDC",
"blockchain_address": "0x7d2e...01ab",
"reference": "INV-2026-001",
"metadata": {},
"created_at": "2026-04-27T12:00:00Z",
"updated_at": "2026-04-27T12:08:11Z"
}
}Delivery semantics
- At-least-once — the same event may be delivered more than once.
- Failed deliveries (any non-2xx response) are retried up to 5 times with exponential backoff (30 s × 2^attempt, capped at 1 hour). After 5 failed attempts the event is dead-lettered.
- Failed and dead-lettered deliveries are visible in the dashboard.
Idempotent processing
Because delivery is at-least-once, your handler must be idempotent.
Use the id field as a deduplication key — store processed event IDs and skip any id you have already handled.
// pseudocode
if (await db.eventAlreadyProcessed(event.id)) {
return res.status(200).end(); // ack duplicate
}
await processEvent(event);
await db.markEventProcessed(event.id);