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

# WebSockets

> Realtime streams for merchants, commerce, and payor sessions.

## Overview

WebSocket streams provide realtime events for customer activity across merchants, commerce orders, and payor checkout sessions. Frames are delivered as JSON objects in the canonical `PepayEvent` envelope.

Typical use cases:

* Live merchant dashboards (invoice and payment activity).
* Commerce order tracking in apps or internal tools.
* Payor checkout UIs (session-scoped updates).

Endpoints (upgrade):

* `GET /ws/merchant/events`
* `GET /ws/commerce/events`
* `GET /ws/payment`

## Streams

* **Merchant stream**: account-wide `invoice.*` and `invoice_payment.*` activity (filterable by invoice, customer, environment).
* **Commerce stream**: `commerce.order.*` updates and linked invoice/payment context.
* **Payor stream**: session-scoped checkout updates (event\_v1 frames only).
  Commerce invoices expire after \~30 minutes; unpaid invoices emit `invoice.updated` with `status=expired`.
  Standard invoices emit `invoice.updated` with `status=expired` on the merchant stream; payor sessions reflect expired status.

## Authentication

Merchant/commerce streams:

* Server-to-server: `x-api-key` (merchant) or `x-commerce-api-key` (commerce) in the WS upgrade headers.
* Browser/mobile: `token=<ws_token>` query param (mint via `POST /api/v1/ws/token`).

Payor stream:

* `token=<payment_session_jwt>` query param (returned from `POST /api/v1/payments/payment-addresses`)

Underpaid lock-in (payor):

* If a partial payment is received, the invoice becomes `underpaid`.
* Fetch `GET /api/v1/payments/session-details` to read `locked_network`, `locked_token_id`, and `locked_payment_address`.
* The remaining balance must be paid using the locked network/token.

## Request

Connection URL examples (token auth):

```bash theme={null}
wss://api-beta.pepay.io/ws/merchant/events?token=<ws_token>&since=evt_1700000000000-123
wss://api-beta.pepay.io/ws/commerce/events?token=<ws_token>
wss://api-beta.pepay.io/ws/payment?token=<payment_session_jwt>&format=event_v1
```

If you authenticate server-to-server with API key headers, omit the `token` query param.

Filters (query params):

* `types`: Comma-separated event types (supports wildcards, e.g. `invoice.*`, `commerce.order.*`).
* `since`: Replay cursor (event id), e.g. `evt_1700000000000-123`.
* Merchant stream: `invoice_type`, `invoice_id`, `customer_id`, `environment`.
* Commerce stream: `order_id`, `invoice_id`, `customer_id`, `wallet_address`, `wallet_network`.
* `format`: `event_v1` (only supported format; defaults to `event_v1` if omitted).

## Response

Representative event frame:

```json theme={null}
{
  "id": "evt_1700000000000-123",
  "object": "event",
  "api_version": "2025-12-16",
  "created": 1700000000,
  "type": "invoice.updated",
  "livemode": false,
  "pending_webhooks": 0,
  "request": { "id": null, "idempotency_key": null },
  "data": {
    "object": {
      "object": "invoice",
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "status": "paid",
      "currency": "usd",
      "amount": 10000,
      "amount_unit": "usd_millis",
      "environment": "devnet"
    },
    "previous_attributes": {}
  }
}
```

## Errors

* `401`/`403` when token or API-key auth is invalid.
* `400` for malformed query params (for example invalid `types` or cursor format).
* Terminal stream errors are delivered as `ws_error` frames before disconnect.

## Resilience and replay

* Delivery is at-least-once; dedupe by `event.id`.
* Use `since=<event_id>` to replay within retention.
* If `since` is too old, the server sends a bounded snapshot and resumes realtime.
* Handle `ws_control` frames (replay hints) and `ws_error` frames (terminal errors).

## WebSockets vs webhooks

* WebSockets deliver the same event types as webhooks, but with replay and REST backfill for realtime resilience.
* The merchant stream includes replay/backfill, making it more redundant and reliable than webhooks alone for live updates.
* Webhooks are best for server-side side effects and audit trails.
* Many teams use both for redundancy: WebSockets for live updates, webhooks for durable processing.

## Examples

Playground (interactive reference):

* [Merchant events WebSocket](/api-spec/endpoints/ws-merchant-events)

Next: [Webhooks](/api-spec/webhooks)
