> ## Documentation Index
> Fetch the complete documentation index at: https://docs-alpha.pepay.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook Events

> Event types and payload examples for webhooks.

## Overview

Use this page to map webhook event types to payload shapes. The same `PepayEvent` envelope is used by WebSocket `event_v1` frames.

## Authentication

Webhook deliveries are signed. Validate `X-Pepay-Timestamp` and signature headers before parsing business logic payloads.
See [Webhook Authentication](/api-spec/webhooks-authentication).

## Request

Webhook endpoints receive `POST` requests with JSON payloads:

```bash theme={null}
curl -X POST "https://merchant.example.com/webhooks/pepay" \
  -H "Content-Type: application/json" \
  -d '{"id":"evt_example","object":"event","type":"test.ping","created":1700000000,"data":{"object":{"object":"ping","status":"ok"}}}'
```

## Response

### Event envelope (shared)

All webhook deliveries use the canonical `PepayEvent` envelope. WebSocket `event_v1` frames use the same shape, so you can reuse handlers across webhooks and WebSockets.

```json theme={null}
{
  "id": "evt_1700000000000-123",
  "object": "event",
  "type": "invoice.updated",
  "created": 1700000000,
  "data": {
    "object": {
      "object": "invoice",
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "status": "paid"
    }
  }
}
```

See [WebSockets](/api-spec/realtime/websockets) for the realtime stream that mirrors these event types.

## Errors

* `400` should be returned for invalid signature or invalid payload.
* Non-2xx responses trigger retry delivery attempts.
* Treat handlers as idempotent due at-least-once delivery semantics.

## Examples

### Event types (overview)

| Category          | Event types                                          | Description                                                                        |
| ----------------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------- |
| Invoice lifecycle | `invoice.created`, `invoice.updated`                 | Invoice state changes (source of truth for payments).                              |
| Payment lifecycle | `invoice_payment.created`, `invoice_payment.updated` | Individual payment attempts and settlement status.                                 |
| Commerce orders   | `commerce.order.created`, `commerce.order.updated`   | Order lifecycle updates (includes linked invoice/payment context when applicable). |
| Test deliveries   | `test.ping`                                          | Test event sent from the dashboard.                                                |

## Invoice events

### `invoice.created`

```json theme={null}
{
  "id": "evt_1700000001000-456",
  "object": "event",
  "type": "invoice.created",
  "created": 1700000001,
  "data": {
    "object": {
      "object": "invoice",
      "id": "inv_123",
      "status": "unpaid",
      "amount_usd": 49,
      "environment": "devnet"
    }
  }
}
```

### `invoice.updated`

```json theme={null}
{
  "id": "evt_1700000002000-789",
  "object": "event",
  "type": "invoice.updated",
  "created": 1700000002,
  "data": {
    "object": {
      "object": "invoice",
      "id": "inv_123",
      "status": "paid",
      "paid_at": 1735689600,
      "environment": "mainnet"
    }
  }
}
```

## Payment events

### `invoice_payment.created`

```json theme={null}
{
  "id": "evt_1700000003000-111",
  "object": "event",
  "type": "invoice_payment.created",
  "created": 1700000003,
  "data": {
    "object": {
      "object": "invoice_payment",
      "id": "pay_123",
      "invoice_id": "inv_123",
      "status": "pending",
      "settlement_status": "pending",
      "amount_usd": 49,
      "environment": "devnet"
    }
  }
}
```

### `invoice_payment.updated`

```json theme={null}
{
  "id": "evt_1700000004000-222",
  "object": "event",
  "type": "invoice_payment.updated",
  "created": 1700000004,
  "data": {
    "object": {
      "object": "invoice_payment",
      "id": "pay_123",
      "invoice_id": "inv_123",
      "status": "confirmed",
      "settlement_status": "settled",
      "settlement": {
        "summary_status": "settled",
        "stage": "final",
        "merchant_settlement_id": 30,
        "settlement_tx_hash": "0xabc123",
        "network": "bsc",
        "status": "settled"
      },
      "environment": "mainnet"
    }
  }
}
```

## Commerce order events

### `commerce.order.created`

```json theme={null}
{
  "id": "evt_1700000005000-333",
  "object": "event",
  "type": "commerce.order.created",
  "created": 1700000005,
  "data": {
    "object": {
      "object": "commerce_order",
      "id": "order_123",
      "status": "placed",
      "invoice_id": "inv_123",
      "environment": "devnet"
    }
  }
}
```

### `commerce.order.updated`

```json theme={null}
{
  "id": "evt_1700000006000-444",
  "object": "event",
  "type": "commerce.order.updated",
  "created": 1700000006,
  "data": {
    "object": {
      "object": "commerce_order",
      "id": "order_123",
      "status": "fulfilled",
      "invoice_id": "inv_123",
      "environment": "mainnet"
    }
  }
}
```

## Test event

### `test.ping`

```json theme={null}
{
  "id": "evt_1700000007000-555",
  "object": "event",
  "type": "test.ping",
  "created": 1700000007,
  "data": {
    "object": {
      "object": "ping",
      "status": "ok"
    }
  }
}
```

## Tips for handlers

* Treat `invoice` as the source of truth for payment state.
* Dedupe by `event.id` (also sent as `X-Pepay-Event-ID`).
* Keep handlers idempotent for at-least-once delivery.

Next: [Webhook Authentication](/api-spec/webhooks-authentication)
