> ## Documentation Index
> Fetch the complete documentation index at: https://docs.testnet.dev.adipredictstreet.com/llms.txt
> Use this file to discover all available pages before exploring further.

# WebSocket overview

> Two gateways — /ws/market and /ws/user, both authenticated — sharing one Kalshi-style command envelope. Channels keyed by tokenId / conditionId / vault address.

PredictStreet streams live data over **two WebSocket gateways** that
share a single client protocol. Pick the gateway by what data you
want; do not mix public and private subscriptions on one socket.

Both gateways authenticate at handshake with **`X-Api-Key`** — the
same key you use for HTTP, same scope rules. A missing or invalid
key closes the socket with `4401 <reason>` before any subscribe
command is accepted.

| Gateway | Endpoint                                            | Auth                                                    | What's there                                                              |
| ------- | --------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------- |
| Market  | [`WSS /ws/market`](/api-reference/websocket/market) | **`X-Api-Key`** (partners) OR Bearer JWT (retail, SIWE) | Public trades, orderbook snapshots, market lifecycle, platform status     |
| User    | [`WSS /ws/user`](/api-reference/websocket/user)     | **`X-Api-Key`** (partners) OR Bearer JWT (retail, SIWE) | Own orders / fills / vault state, deposits, withdrawals, ERC-1155 changes |

<Note>
  Both gateways accept the same two schemes as HTTP — `X-Api-Key` for
  partners, Bearer JWT for the first-party frontend. Private channels
  (`user_orders`, `user_fills`, `vault_positions`) require the
  `portfolio:read` scope on API keys; JWT callers bypass the scope
  check as the wallet owner. For `vault_positions`, each requested
  vault must be covered by the API key's `associated_vault` row. See
  [`/ws/user`](/api-reference/websocket/user) for the full handshake
  matrix and close codes.
</Note>

Same envelope, same commands (`subscribe`, `update_subscription`,
`unsubscribe`, `list_subscriptions`, `ping`), same per-subscription
`sid` model on both.

<Card title="WSS /ws/market" icon="globe" href="/api-reference/websocket/market">
  Market data gateway — subscribe by `tokenId` / `conditionId`.
</Card>

<Card title="WSS /ws/user" icon="lock" href="/api-reference/websocket/user">
  Private gateway — `X-Api-Key` (partners) or Bearer JWT (retail).
</Card>

<Card title="Commands" icon="terminal" href="/api-reference/websocket/commands">
  All five client → server commands with payloads + responses.
</Card>

<Card title="Server events" icon="bolt" href="/api-reference/websocket/events">
  Per-channel event catalog (push payloads).
</Card>

## Conventions

* **Numeric fields** are decimal strings (`"price": "0.52"`) — don't
  parse into `Number`.
* **Addresses** lowercased; **`tokenId`** decimal-string with no leading
  zeroes; **`conditionId`** lowercased 32-byte hex.
* **`sid`** is connection-local, not stable across reconnects.
* **Auth is validated at handshake only** — rotating a key does not
  invalidate an open socket. Close + reopen to switch identity.
  Revoking a key on the admin panel closes every live socket bound
  to that `keyId` immediately via Redis pub/sub.
* **Heartbeat:** send `{ "id": N, "cmd": "ping" }` every 20–30 s; the
  server replies `{ "id": N, "type": "pong", "ts": <ms> }`.

## Implementation status (testnet)

| Channel                                                       | Status                                                                                                           |
| ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `token_trade_matches` (`/ws/market`)                          | ✅ live — `trade_matched` as soon as the matcher prints                                                           |
| `token_trade_settlements` (`/ws/market`)                      | ✅ live — `trade_settled` on chain `OrderFilled`                                                                  |
| `token_ohlc` (`/ws/market`)                                   | ✅ live — 5-second candles, `interval: "s5"`                                                                      |
| `token_book` (`/ws/market`)                                   | ✅ live — `book_snapshot` (depth 100) + `book_update` + `book_delta`; `get_book_snapshot` for mid-session refresh |
| `system` (`/ws/market`)                                       | ✅ live                                                                                                           |
| `user_orders` / `user_fills` / `vault_positions` (`/ws/user`) | ✅ live                                                                                                           |
| `condition_lifecycle` (`/ws/market`)                          | ⚠️ subscribe accepted, most events not yet emitted by upstream                                                   |
