> ## 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.

# Webhooks

> Payload shape, subscriptions, and delivery semantics.

## Overview

Webhooks deliver canonical `PepayEvent` objects to your server when merchant or commerce activity changes. They mirror the same event types available via WebSockets, but are optimized for durable server-to-server processing.

## Authentication

Deliveries are signed using `X-Pepay-Timestamp` and `X-Pepay-Signature`. Validate signatures before processing payloads.
See [Webhook Authentication](/api-spec/webhooks-authentication).

## Request

1. Create a webhook endpoint in the dashboard (or via the API).
2. Choose an environment scope: `devnet`, `mainnet`, or all environments.
3. Subscribe to event types using `enabled_events` (filters).
4. Pepay delivers signed HTTPS POSTs with a canonical event payload.

## Access and subscriptions (enabled\_events)

Each webhook endpoint has its own subscription list. You only receive events that match the list.

Allowed event types:

* `invoice.*` (all invoice lifecycle events)
* `invoice.created`, `invoice.updated`
* `invoice_payment.*` (all payment lifecycle events)
* `invoice_payment.created`, `invoice_payment.updated`
* `commerce.order.*` (all commerce order lifecycle events)
* `commerce.order.created`, `commerce.order.updated`
* `test.ping` (test delivery)

Wildcard filters are prefix-based. For example, `invoice.*` matches both `invoice.created` and `invoice.updated`.

## Environment filtering

The webhook `environment` field controls which events are delivered:

* `null` or omitted: receive devnet and mainnet events.
* `devnet` or `mainnet`: receive only matching events.

You can run multiple endpoints (e.g., devnet + mainnet) with different secrets and subscriptions.

## Response

Webhook payloads use the same `PepayEvent` envelope as WebSockets:

```json theme={null}
{
  "id": "evt_1700000000000-123",
  "object": "event",
  "type": "commerce.order.updated",
  "created": 1700000000,
  "data": {
    "object": { "object": "commerce_order", "id": "uuid", "status": "placed" }
  }
}
```

See [Webhook Events](/api-spec/webhook-events) for full examples across invoice, payment, and commerce events.

## Headers you can rely on

* `X-Pepay-Event`: event type string
* `X-Pepay-Event-ID`: event id
* `X-Pepay-Timestamp`: signed timestamp (ms)
* `X-Pepay-Network-Environment`: `devnet` or `mainnet` (when available)

## Errors

* Return `400` for invalid signatures or malformed payloads.
* Non-2xx responses trigger retry delivery attempts.
* Ensure handlers are idempotent because duplicate deliveries can occur.

## Delivery semantics

* At-least-once delivery; dedupe by `event.id` (or `X-Pepay-Event-ID` header).
* Expect retries on non-2xx responses.

## Webhooks vs WebSockets

* WebSockets provide realtime streams with replay + REST backfill for resilient live updates.
* Webhooks remain the durable server-to-server channel for side effects, analytics, and audit trails.
* For merchants, WebSockets are a scalable redundancy layer alongside webhooks: use both, dedupe by `event.id`.

## Examples

* Single endpoint for all events: `enabled_events=["invoice.*","invoice_payment.*","commerce.order.*"]`
* Split by environment: one `devnet` endpoint and one `mainnet` endpoint, each with different secrets.

Next: [Webhook Events](/api-spec/webhook-events)
