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

# Server events

> Push envelope and the per-channel event catalog.

Every server → client push uses the same envelope:

```json theme={null}
{
  "type":    "<event_type>",
  "sid":     12,
  "channel": "token_trade_matches",
  "id":      "60550363974013526...",
  "data":    { ... }
}
```

| Field     | Meaning                                                                                                                                                                                                              |
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`    | Concrete event type (e.g. `trade_matched`, `trade_settled`, `book_snapshot`, `ohlc_update`, `platform_status`)                                                                                                       |
| `sid`     | Connection-local subscription id you got from the `subscribed` response                                                                                                                                              |
| `channel` | One of the channel names from the catalog                                                                                                                                                                            |
| `id`      | The token / condition / vault / system id this event belongs to (omitted on wallet-scoped channels — `user_orders`, `user_fills`)                                                                                    |
| `data`    | Event-specific payload (catalog below). Public events carry `tokenId` + `conditionId` + `outcomeIndex`; resolve symbols through REST if needed. Timestamps in `data` are emitted as `tsMs` (number, ms since epoch). |

Use `sid` to route inside your client; use `id` to identify which
specific subscribed entity matched.

***

## `user_orders` (`/ws/user`, no `id`) ✅ live

| `type`            | `data`                                                                                              |
| ----------------- | --------------------------------------------------------------------------------------------------- |
| `order_placed`    | exchange payload — includes `clientOrderId` when the original `POST /api/orders/place` supplied one |
| `order_cancelled` | exchange payload — includes `clientOrderId` when the original placement supplied one                |

***

## `user_fills` (`/ws/user`, no `id`) ✅ live

| `type`      | `data`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `user_fill` | `{ walletAddress, side: 'buy' \| 'sell', tradeId, orderId, clientOrderId?, tokenId, conditionId, outcomeIndex, price, quantity, source: 'matcher', tsMs }` — match-time notification (matcher, NOT chain-confirmed). `fee` / `txHash` / `blockNumber` / on-chain `orderHash` are intentionally omitted; poll [`GET /api/me/trades`](/api-reference/portfolio/me-trades) for the post-settlement view. See [user\_fill payload reference](/api-reference/websocket/user#user_fill-vs-apimetrades). |

***

## `vault_positions` (`/ws/user`, `ids` = vault address\[]) ✅ live

| `type`                           | `data`                                                                                                                                                                                             |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `vault_position_balance_changed` | `{ vaultAddress, tokenId, conditionId, outcomeIndex, balanceAfter, reason, txHash, blockNumber, tsMs }` — emitted on any ERC-1155 mirror change for the vault                                      |
| `vault_position_split`           | `{ vaultAddress, conditionId, collateralToken, amount, txHash, blockNumber, tsMs }`                                                                                                                |
| `vault_position_merged`          | `{ vaultAddress, conditionId, collateralToken, amount, txHash, blockNumber, tsMs }`                                                                                                                |
| `vault_position_redeemed`        | `{ vaultAddress, conditionId, payouts, txHash, blockNumber, tsMs }` — `payouts` is an `{ [outcomeIndex]: amount }` map; multi-outcome (neg-risk) redeems can pay both YES and NO in the same event |

`vault_position_balance_changed.reason` is the chain-event tag carried
through from `Erc1155MirrorService`. Known values: `"OUTF"` (output
filled — trade-side credit), `"INFL"` (inflow from a non-trade
source), `"SPLT"` (split), `"MERG"` (merged), `"REDM"` (redeemed),
`"XFER"` (direct ERC-1155 transfer between vaults). The list is open-
ended; treat unknown tags as a generic balance change, don't drop them.

Required scope: `portfolio:read`. Subscription must include at least
one vault id, and the API key's `associated_vault` row must cover
each requested vault — vaults outside the grant come back under
`rejected[]` with `forbidden`.

***

## `token_trade_matches` (`/ws/market`, `id` = `tokenId`) ✅ live

| `type`          | `data`                                                                                            |
| --------------- | ------------------------------------------------------------------------------------------------- |
| `trade_matched` | `{ tokenId, conditionId, outcomeIndex, tradeId, price, quantity, side, source: "matcher", tsMs }` |

Fires the moment the matcher prints a trade — fastest tape feed,
arrives several seconds before the chain settlement. `tradeId` is the
matcher-assigned id; pair it with the `tradeId` echoed on
`trade_settled` to correlate matcher output to chain settlement.

```json theme={null}
{
  "type":    "trade_matched",
  "sid":     12,
  "channel": "token_trade_matches",
  "id":      "60550363974013526351721227761627460394700175914670861601864197814576471666136",
  "data": {
    "tokenId":      "60550363974013526351721227761627460394700175914670861601864197814576471666136",
    "conditionId":  "0x22a88c544e7ab9...",
    "outcomeIndex": 0,
    "tradeId":      "t_01J3R7…",
    "price":        "0.67",
    "quantity":     "3.51",
    "side":         "buy",
    "source":       "matcher",
    "tsMs":         1776949200431
  }
}
```

No backfill on subscribe — query
`GET /api/markets/{symbol}/trades` for history.

***

## `token_trade_settlements` (`/ws/market`, `id` = `tokenId`) ✅ live

| `type`          | `data`                                                                                                                 |
| --------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `trade_settled` | `{ tokenId, conditionId, outcomeIndex, orderHash, txHash, blockNumber, price, quantity, side, source: "chain", tsMs }` |

Fires when chain-watcher indexes the on-chain `OrderFilled` event for
the matched trade. Use this for accounting-grade trade confirmations.
`orderHash` lets you tie this back to the order/matched trade in your
own ledger.

***

## `token_ohlc` (`/ws/market`, `id` = `tokenId`) ✅ live

| `type`        | `data`                                                                                                         |
| ------------- | -------------------------------------------------------------------------------------------------------------- |
| `ohlc_update` | `{ tokenId, conditionId, outcomeIndex, interval: "s5", time, open, high, low, close, volume, isClosed, tsMs }` |

5-second candles emitted by the matcher whenever a trade prints inside
the active window. `isClosed: false` while the bar is still
accumulating; `isClosed: true` is the final tick of the bar.

For history: `GET /api/markets/{symbol}/ohlc?interval=s5`.

***

## `token_book` (`/ws/market`, `id` = `tokenId`) ✅ live

| `type`                 | `data`                                                                                                                                           |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `book_snapshot`        | `{ tokenId, conditionId, outcomeIndex, bids, asks, seq, tsMs }` — initial state on subscribe (depth 100)                                         |
| `book_update`          | `{ tokenId, conditionId, outcomeIndex, seq, prevSeq, bids, asks, tsMs }` — full top-of-book state at `seq`                                       |
| `book_delta`           | `{ tokenId, conditionId, outcomeIndex, seq, prevSeq, changes, tsMs }` — diff against `prevSeq`; only emitted when local book state is contiguous |
| `book_snapshot_failed` | `{ tokenId, reason, tsMs }` — snapshot fetch couldn't complete (response to `get_book_snapshot`)                                                 |

`book_update` is the raw full-depth replacement; `book_delta` is the
computed diff. Most integrators want `book_delta`; `book_update` is
there for clients that prefer to replay state wholesale. Live
`book_update` is dropped if an upstream frame is missing `seq` /
`prevSeq` — the gateway never emits a divergent delta.

`seq` is monotonically increasing. After subscribe the server pushes
exactly one `book_snapshot` (depth 100), then `book_update` /
`book_delta` events where `delta.prevSeq == previously_seen_seq`.
On a sequence gap or unexpected silence, issue
[`get_book_snapshot`](/concepts/websocket/subscriptions#snapshot-refresh-get_book_snapshot)
to refresh without unsubscribing. See
[Reconnect — orderbook resync](/concepts/websocket/reconnect#orderbook-resync).

***

## `condition_lifecycle` (`/ws/market`, `id` = `conditionId`) ⚠️ pending

| `type`            | `data`                                                               |
| ----------------- | -------------------------------------------------------------------- |
| `market_paused`   | `{ conditionId, txHash?, blockNumber?, tsMs }`                       |
| `market_unpaused` | `{ conditionId, txHash?, blockNumber?, tsMs }`                       |
| `market_resolved` | `{ conditionId, resolution, payouts?, txHash?, blockNumber?, tsMs }` |
| `market_status`   | `{ conditionId, status, reason?, tsMs }`                             |

Subscription is accepted today; most events not yet emitted by the
upstream producer.

***

## `system` (`/ws/market`, `id` = `"platform_status"`) ✅ live

| `type`            | `data`                                                |
| ----------------- | ----------------------------------------------------- |
| `platform_status` | passthrough payload from the platform-status producer |

```json theme={null}
{
  "type":    "platform_status",
  "sid":     15,
  "channel": "system",
  "id":      "platform_status",
  "data": {
    "frozen":      true,
    "reason":      "scheduled-maintenance",
    "since":       "2026-04-23T18:00:00Z",
    "etaUnfreeze": "2026-04-23T18:30:00Z"
  }
}
```
