Subscribe
Each gateway is a separate socket with its ownsid namespace.
Subscribe per gateway.
/ws/user — private
/ws/market — public
sids are connection-local — each gateway numbers from 1
independently, so a sid: 10 on the user socket and a sid: 10 on
the market socket point at completely different subscriptions.
Trying to subscribe to the wrong-gateway channel comes back as
{ "code": "forbidden" } in rejected[].
Normalization rules
- All addresses lowercased.
tokenIdis decimal-string form (no leading zeroes).conditionIdis lowercased 32-byte hex.- Duplicate ids removed.
Rejection codes
A subscription that fails validation comes back underrejected[]:
message field redundantly prefixes the code — that’s an
artifact of the gateway, not a separate signal. Route by code;
treat message as the human-readable detail.
Per-channel reference
Private — own activity (typed)
user_orders — own order lifecycle
- Gateway:
/ws/user ids: not allowed (always scoped to the key’s wallet)- Required scope:
portfolio:read - Pushes:
order_placed,order_cancelled
user_fills — own chain-confirmed fills
- Gateway:
/ws/user ids: not allowed (always scoped to the key’s wallet)- Required scope:
portfolio:read - Pushes:
user_fill
vault_positions — per-vault balance / split / merge / redeem
- Gateway:
/ws/user ids: one or more vault addresses (lowercased)- Required scope:
portfolio:read - Pushes:
vault_position_balance_changed,vault_position_split,vault_position_merged,vault_position_redeemed
associated_vault row; vaults outside the grant come back under
rejected[] with forbidden.
Public — trades
token_trade_matches — low-latency matcher tape
- Gateway:
/ws/market ids: one or more outcometokenIds (decimal-string)- Pushes:
trade_matched(source: "matcher") per off-chain match
token_trade_settlements — chain-confirmed settlements
- Gateway:
/ws/market ids: one or more outcometokenIds- Pushes:
trade_settled(source: "chain") on indexedOrderFilled
txHash,
blockNumber, and orderHash.
Public — books
token_book — public orderbook snapshots + updates
- Gateway:
/ws/market ids: one or more outcometokenIds- Pushes:
book_snapshot(initial),book_update(full top-of-book),book_delta(diff againstprevSeq),book_snapshot_failed(snapshot fetch couldn’t complete)
book_snapshot (depth 100),
then book_update events with monotonically increasing seq.
book_delta is emitted only when the gateway has contiguous local
book state — if seq or prevSeq is missing on a producer frame the
gateway drops the live book_update rather than emit a divergent
delta.
See Reconnect — orderbook resync
for the gap-detection flow built around book_delta.prevSeq.
token_ohlc — rolling 5-second candles
- Gateway:
/ws/market ids: one or more outcometokenIds- Pushes:
ohlc_update(interval: "s5") — fires whenever the matcher prints inside the active 5-second window
isClosed: true.
Public — lifecycle
condition_lifecycle — market-level lifecycle
- Gateway:
/ws/market ids: one or moreconditionIds (lowercased bytes32 hex)- Pushes:
market_paused,market_unpaused,market_resolved,market_status
market_lifecycle — per-market lifecycle
- Gateway:
/ws/market ids: one or more marketsymbols (lower-cased in the ack)- Pushes:
status_changed,market_disputed,market_resolved, and — on a crypto Up/Down window —crypto_window_opened
symbol rather than the on-chain conditionId, so
one subscription per market page carries every market-scoped
transition. Optional fields are omitted when absent, and the frames are
a hint, not the record — re-fetch GET /api/markets/{slug} for
authoritative state.
Public — crypto price
crypto_price_tick — live underlying-asset price
- Gateway:
/ws/market ids: one or more cryptoinstrumentIds (BTC-USD,ETH-USD) — case-insensitive, lower-cased in the ack- Pushes:
crypto_price_tick
price is an unscaled integer string plus a decimals
exponent; no backfill on subscribe, and silence simply means no
producer is active for that instrument. Full contract:
Crypto price feeds.
system — platform-wide status
- Gateway:
/ws/market ids: must be["platform_status"]
Resource limits
Per-connection caps. Exceeding any of them surfaces as a structured error rather than a silent drop:
Exact values may differ by environment — defaults above are the dev
configuration.
Update existing subscription
For id-based channels (token_trade_matches, token_trade_settlements,
token_book, token_ohlc, condition_lifecycle, market_lifecycle,
crypto_price_tick, vault_positions, system) you can change ids on
an existing sid instead of opening
a new subscription. user_orders and user_fills don’t carry ids,
so update_subscription returns invalid_params for them.
Add ids
Remove ids
Snapshot refresh — get_book_snapshot
The get_book_snapshot command lets a token_book subscriber
request a fresh full-depth snapshot without unsubscribing /
resubscribing. Useful when a sequence gap is detected
(book_delta.prevSeq != lastSeq).
sid:
book_snapshot per resolved subscription, or
book_snapshot_failed per token if the snapshot fetch failed:
get_book_snapshot removes
the gap window.
Unsubscribe
List subscriptions
Ping
ts is the server clock in milliseconds — clients can use it as a
clock-skew probe.