---
title: "Webhook Event Reference"
description: "Complete reference for all EdenPay webhook events and their payload structures."
sidebar_position: 3
last_updated: 2026-08-20
---

# Webhook Event Reference

Complete reference for all EdenPay webhook events and their payload structures.

## Event Structure

All webhook events follow this common structure:

```json
{
  "id": "evt_abc123",
  "type": "payment.confirmed",
  "createdAt": "2025-01-15T10:30:00Z",
  "data": {
    // Event-specific data
  }
}
```

| Field | Type | Description |
|-------|------|-------------|
| `id` | string | Unique identifier for this event |
| `type` | string | Event type (see below for all types) |
| `createdAt` | string | ISO 8601 timestamp when event was created |
| `data` | object | Event-specific payload |

## Payment Events

### payment.created

Triggered when a new payment is initiated.

**Use case**: Track payment creation, start monitoring, or update UI status.

```json
{
  "id": "evt_abc123",
  "type": "payment.created",
  "createdAt": "2025-01-15T10:30:00Z",
  "data": {
    "paymentId": "pay_xyz789",
    "amount": "100.00",
    "currency": "USDC",
    "network": "ethereum",
    "status": "pending",
    "recipientAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "workspaceId": "ws_abc123",
    "metadata": {
      "orderId": "order_123",
      "customerId": "cust_456"
    }
  }
}
```

### payment.pending

Triggered when a payment transaction is detected on the blockchain but not yet confirmed.

**Use case**: Show "Payment detected, awaiting confirmation" status to customers.

```json
{
  "id": "evt_def456",
  "type": "payment.pending",
  "createdAt": "2025-01-15T10:31:00Z",
  "data": {
    "paymentId": "pay_xyz789",
    "amount": "100.00",
    "currency": "USDC",
    "network": "ethereum",
    "status": "pending",
    "txHash": "0x1234567890abcdef...",
    "confirmationsReceived": 1,
    "confirmationsRequired": 12,
    "recipientAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "workspaceId": "ws_abc123",
    "metadata": {
      "orderId": "order_123",
      "customerId": "cust_456"
    }
  }
}
```

### payment.confirmed <ion-icon name="star" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#f59e0b"}}></ion-icon>

**This is the most important event.** Triggered when a payment has received sufficient blockchain confirmations and is considered final.

**Use case**: **Fulfill orders, credit accounts, grant access, or trigger any business logic that requires payment confirmation.**

:::tip Recommended Action
This is the event you should use to fulfill orders and grant access to paid content. The payment is irreversible at this point.
:::

```json
{
  "id": "evt_ghi789",
  "type": "payment.confirmed",
  "createdAt": "2025-01-15T10:35:00Z",
  "data": {
    "paymentId": "pay_xyz789",
    "amount": "100.00",
    "currency": "USDC",
    "network": "ethereum",
    "status": "confirmed",
    "txHash": "0x1234567890abcdef...",
    "confirmationsReceived": 12,
    "recipientAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "workspaceId": "ws_abc123",
    "confirmedAt": "2025-01-15T10:35:00Z",
    "metadata": {
      "orderId": "order_123",
      "customerId": "cust_456"
    }
  }
}
```

**Confirmation Requirements by Network:**
- **Ethereum**: 12 confirmations (~2.5 minutes)
- **Polygon**: 100 confirmations (~3.5 minutes)
- **Base/Optimism**: 12 confirmations (~30 seconds)
- **Arbitrum**: 12 confirmations (~20 seconds)
- **Sui**: 1 confirmation (~2 seconds)
- **Aptos**: 1 confirmation (~4 seconds)

### payment.failed

Triggered when a payment fails or expires.

**Use case**: Cancel orders, notify customers, or clean up pending transactions.

```json
{
  "id": "evt_jkl012",
  "type": "payment.failed",
  "createdAt": "2025-01-15T11:00:00Z",
  "data": {
    "paymentId": "pay_xyz789",
    "amount": "100.00",
    "currency": "USDC",
    "network": "ethereum",
    "status": "failed",
    "failureReason": "Payment expired after 30 minutes",
    "recipientAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "workspaceId": "ws_abc123",
    "metadata": {
      "orderId": "order_123",
      "customerId": "cust_456"
    }
  }
}
```

**Common Failure Reasons:**
- `Payment expired after 30 minutes` - Customer didn't complete payment in time
- `Insufficient amount received` - Customer sent less than required
- `Wrong network` - Customer sent to wrong blockchain
- `Transaction reverted` - Blockchain transaction failed

### payment.refunded

Triggered when a payment is refunded to the customer.

**Use case**: Revoke access, update accounting, or notify customer of refund.

```json
{
  "id": "evt_mno345",
  "type": "payment.refunded",
  "createdAt": "2025-01-15T12:00:00Z",
  "data": {
    "paymentId": "pay_xyz789",
    "amount": "100.00",
    "currency": "USDC",
    "network": "ethereum",
    "status": "refunded",
    "refundTxHash": "0xabcdef1234567890...",
    "refundReason": "Customer requested refund",
    "recipientAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "workspaceId": "ws_abc123",
    "refundedAt": "2025-01-15T12:00:00Z",
    "metadata": {
      "orderId": "order_123",
      "customerId": "cust_456"
    }
  }
}
```

## Checkout Session Events

### checkout.session.created

Triggered when a new checkout session is created.

**Use case**: Track checkout funnel, monitor conversion rates.

```json
{
  "id": "evt_pqr678",
  "type": "checkout.session.created",
  "createdAt": "2025-01-15T10:29:00Z",
  "data": {
    "sessionId": "cs_abc123",
    "status": "pending",
    "amount": "100.00",
    "currency": "USDC",
    "network": "ethereum",
    "productId": "prod_xyz789",
    "workspaceId": "ws_abc123",
    "checkoutUrl": "https://checkout.bex.co/cs_abc123",
    "expiresAt": "2025-01-15T11:00:00Z",
    "metadata": {
      "orderId": "order_123"
    }
  }
}
```

### checkout.session.completed

Triggered when a checkout session is successfully completed (payment confirmed).

**Use case**: Track successful checkouts, conversion analytics.

```json
{
  "id": "evt_stu901",
  "type": "checkout.session.completed",
  "createdAt": "2025-01-15T10:35:00Z",
  "data": {
    "sessionId": "cs_abc123",
    "status": "completed",
    "amount": "100.00",
    "currency": "USDC",
    "network": "ethereum",
    "productId": "prod_xyz789",
    "paymentId": "pay_xyz789",
    "workspaceId": "ws_abc123",
    "completedAt": "2025-01-15T10:35:00Z",
    "metadata": {
      "orderId": "order_123"
    }
  }
}
```

### checkout.session.expired

Triggered when a checkout session expires without payment.

**Use case**: Track abandoned checkouts, send recovery emails.

```json
{
  "id": "evt_vwx234",
  "type": "checkout.session.expired",
  "createdAt": "2025-01-15T11:00:00Z",
  "data": {
    "sessionId": "cs_abc123",
    "status": "expired",
    "amount": "100.00",
    "currency": "USDC",
    "network": "ethereum",
    "productId": "prod_xyz789",
    "workspaceId": "ws_abc123",
    "expiredAt": "2025-01-15T11:00:00Z",
    "metadata": {
      "orderId": "order_123"
    }
  }
}
```

## Event Lifecycle

A typical payment flow produces events in this order:

```mermaid
sequenceDiagram
    participant Customer
    participant EdenPay
    participant Blockchain
    participant YourServer

    Customer->>EdenPay: Create checkout
    EdenPay->>YourServer: checkout.session.created

    Customer->>Blockchain: Send transaction
    Blockchain->>EdenPay: Transaction detected
    EdenPay->>YourServer: payment.created
    EdenPay->>YourServer: payment.pending

    Blockchain->>EdenPay: Confirmations received
    EdenPay->>YourServer: payment.pending (updates)

    Blockchain->>EdenPay: Fully confirmed
    EdenPay->>YourServer: payment.confirmed
    EdenPay->>YourServer: checkout.session.completed

    Note over YourServer: Fulfill order here!
```

**Alternative flows:**
- **Payment expires**: `payment.failed` → `checkout.session.expired`
- **Payment refunded**: `payment.refunded` (after successful payment)

## Event Handling Best Practices

### 1. Handle Events Idempotently

Always check if you've already processed an event:

```javascript
async function handleWebhook(event) {
  // Check if already processed
  const existing = await db.webhookEvents.findOne({
    eventId: event.id
  });

  if (existing) {
    console.log('Event already processed:', event.id);
    return; // Skip duplicate
  }

  // Process the event
  await processPayment(event.data);

  // Mark as processed
  await db.webhookEvents.create({
    eventId: event.id,
    type: event.type,
    processedAt: new Date()
  });
}
```

### 2. Use Metadata for Context

Pass your system's IDs through metadata:

```javascript
// When creating a payment
const payment = await createPayment({
  amount: '100.00',
  currency: 'USDC',
  metadata: {
    orderId: 'order_123',
    customerId: 'cust_456',
    productSku: 'PROD-001'
  }
});

// In your webhook handler
function handlePaymentConfirmed(data) {
  const { orderId, customerId, productSku } = data.metadata;
  await fulfillOrder(orderId, customerId, productSku);
}
```

### 3. Return 200 Quickly

Acknowledge receipt immediately, process asynchronously:

```javascript
app.post('/webhooks', async (req, res) => {
  // Verify signature first
  if (!verifySignature(req)) {
    return res.status(401).send('Unauthorized');
  }

  const event = JSON.parse(req.body.toString());

  // Return 200 immediately
  res.status(200).json({ received: true });

  // Process asynchronously
  processEventAsync(event).catch(err => {
    console.error('Error processing event:', err);
    // Log to error tracking service
  });
});
```

### 4. Handle Unknown Event Types

Be defensive - new event types may be added:

```javascript
switch (event.type) {
  case 'payment.confirmed':
    await handlePaymentConfirmed(event.data);
    break;

  case 'payment.failed':
    await handlePaymentFailed(event.data);
    break;

  default:
    // Log but don't fail
    console.log('Unknown event type:', event.type);
    // Still return 200 to acknowledge receipt
}
```

## Recommended Event Subscriptions

Choose based on your use case:

### Minimal Setup (E-commerce)
- <ion-icon name="checkmark-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#10b981"}}></ion-icon> `payment.confirmed` - Fulfill orders

### Recommended Setup
- <ion-icon name="checkmark-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#10b981"}}></ion-icon> `payment.confirmed` - Fulfill orders
- <ion-icon name="checkmark-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#10b981"}}></ion-icon> `payment.failed` - Cancel orders and notify customers
- <ion-icon name="checkmark-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#10b981"}}></ion-icon> `checkout.session.expired` - Track abandoned carts

### Full Analytics Setup
- <ion-icon name="checkmark-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#10b981"}}></ion-icon> `checkout.session.created` - Track checkout starts
- <ion-icon name="checkmark-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#10b981"}}></ion-icon> `payment.created` - Track payment attempts
- <ion-icon name="checkmark-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#10b981"}}></ion-icon> `payment.pending` - Update UI with progress
- <ion-icon name="checkmark-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#10b981"}}></ion-icon> `payment.confirmed` - Fulfill orders
- <ion-icon name="checkmark-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#10b981"}}></ion-icon> `payment.failed` - Handle failures
- <ion-icon name="checkmark-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#10b981"}}></ion-icon> `payment.refunded` - Process refunds
- <ion-icon name="checkmark-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#10b981"}}></ion-icon> `checkout.session.completed` - Track conversions
- <ion-icon name="checkmark-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#10b981"}}></ion-icon> `checkout.session.expired` - Track abandonment

## Testing Events

Test your webhook handler using the dashboard:

1. Navigate to **Accept Payments** → **Webhooks**
2. Click **Test** button on your endpoint
3. A `webhook.test.event` will be sent
4. Check your server logs to verify receipt and processing

## Common Payload Fields

| Field | Type | Always Present | Description |
|-------|------|----------------|-------------|
| `paymentId` | string | Payment events | Unique payment identifier |
| `sessionId` | string | Checkout events | Unique checkout session identifier |
| `amount` | string | Yes | Payment amount (decimal string) |
| `currency` | string | Yes | Currency code (USDC, USDT, ETH, etc.) |
| `network` | string | Yes | Blockchain network |
| `status` | string | Yes | Current status |
| `workspaceId` | string | Yes | Your workspace identifier |
| `metadata` | object | Yes | Custom data you provided |
| `txHash` | string | When available | Blockchain transaction hash |
| `recipientAddress` | string | Payment events | Receiving wallet address |

## Next Steps

- 🔒 **Security**: Read the [Signature Verification Guide](./signatures.md)
- 🚀 **Get Started**: Follow the [Quick Start Guide](./quickstart.md)
- 🛠️ **Debug**: Check the [Troubleshooting Guide](./troubleshooting.md)

## Need Help?

- **Dashboard**: View delivery attempts and debug events
- **Forum**: [https://bex.co/forum](https://bex.co/forum)
- **Support**: hello@bex.co
