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

# Websockets

> Tickets, handshakes, and event frames for the three streams

Three streams complement the REST API. All frames are JSON text.

| Path         | Carries                                          | Event kinds                      |
| ------------ | ------------------------------------------------ | -------------------------------- |
| `/ws/user`   | Your private events                              | `RFQMatch`                       |
| `/ws/rfq`    | The RFQ flow for markets you subscribe to        | `RFQRequest`, `RFQOffer`         |
| `/ws/market` | Order book activity for markets you subscribe to | `PriceLevelUpdate`, `TradeMatch` |

## Connecting

Browsers cannot set headers on websocket upgrades, so authentication uses a
short-lived ticket passed as a query parameter:

<Steps>
  <Step title="Mint a ticket">
    `POST /auth/ws-ticket` from a session (ticket carries `read`), or
    `POST /auth/ws-ticket/signed` with an HMAC-signed request (ticket inherits the
    key's scopes).
  </Step>

  <Step title="Connect within 30 seconds">
    ```text theme={null}
    wss://<host>/ws/user?ticket=<ticket>
    ```

    Tickets expire after 30 seconds. Mint one per connection attempt, immediately
    before connecting. An invalid or expired ticket rejects the upgrade with an
    HTTP error before the socket opens.
  </Step>
</Steps>

## Subscribing

`/ws/user` needs no subscription: it streams your events as soon as the socket
opens.

`/ws/rfq` and `/ws/market` expect one handshake frame naming the markets to
watch, immediately after connecting:

```json theme={null}
{ "markets": [{ "venue": "polymarket", "asset_id": "0x..." }] }
```

The server replies with one **snapshot frame** of current state for those markets
(open requests on `/ws/rfq`; the order book, with a `seq_num`, on `/ws/market`),
then streams incremental events. A malformed handshake gets an error frame and
the connection closes.

## Event frames

Every event after the snapshot has the same envelope:

```json theme={null}
{ "event_kind": "RFQRequest", "event": { ... } }
```

The `event` payload matches the corresponding REST schema: an `RFQRequest`
carries the same shape as a quote request from `GET /rfq/request`, and an
`RFQMatch` the same terms as `GET /rfq/matches`. Ignore `event_kind` values you do not
recognize: new kinds may be added without notice.

## Reconnecting

Connections carry no resume token. On disconnect:

1. Mint a fresh ticket (the old one is long expired).
2. Reconnect and re-handshake. The new snapshot frame replaces any state you
   were tracking, so nothing is missed by rebuilding from it.
3. For `/ws/user`, events published while disconnected are not replayed; on
   reconnect, reconcile through the REST reads (`/rfq/matches`,
   `/custody/get-ledger`).
