Skip to main content

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 (per payment session).

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):
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>
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 (recommended).

Response

Representative event frame:
{
  "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": {}
  }
}

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): Next: Webhooks