Skip to main content
Every server-to-client push uses the same canonical envelope:
Use sid to route inside your client; use id to identify which specific subscribed entity the event belongs to.

Event catalog

user_orders (/ws/user, id = your wallet)

The gateway normalizes these frames to the fields above; a key with no value for an event is left out. marketId is the market symbol. originalQuantity is the order size as placed (on order_placed, order_cancelled and order_expired) and remainingQty the unfilled size when the order was cancelled or expired, both in shares (canonical decimal strings; a self-trade-prevention cancel can lack both). On a cancel or expiry, originalQuantity - remainingQty is what had filled when the order ended, not a final fill total. orderType is LIMIT / MARKET, timeInForce GTC / IOC / FOK, placedAt the time the exchange accepted the order, and eventAt the time of a cancel, expiry or rejection (ISO 8601) — order those frames by eventAt, since their tsMs is the send time. Key your order state by orderId / clientOrderId. trade_failed names the market in marketId when it can be resolved. The order_* frames are state-change notifications, one per transition of an order, not a snapshot of it: they carry no filledQty (a cancel or expiry gives only the size filled by then, as above), and their status is the event label, not the REST order status. For the current state read GET /api/orders/{id}; the field-by-field mapping is in user_orders frames vs REST order detail.

user_fills (/ws/user, id = your wallet)

vault_positions (/ws/user, ids = [your vault address])

Amounts are base-unit integers as decimal strings. These frames carry no tsMs; order them by blockNumber / logIndex. vault_position_balance_changed.reason is the vault contract’s bytes4 tag, sent as hex ("0x53504c54" = SPLT). Values: SPLT (split), MERG (merge), INFL / OUTF (exchange moved tokens into / out of the vault on a fill), REDM (redeem), CONV (neg-risk conversion), EMER (emergency withdrawal). The list is open-ended; treat unknown tags as a generic balance change. Hex values: Server events.

vault_activity (/ws/user, ids = [your vault address])

The channel also carries the vault’s other on-chain events (erc1155_balance, position_split, positions_merged, position_redeemed, order_digest_invalidated, emergency_*). Every shape, and how trade_fill pairs maker and taker events: Server events.

user_activity (/ws/user, no ids)

It also repeats order and trade events in an unnormalized form; use user_orders / user_fills for those. For order sizes in particular, read originalQuantity / remainingQty on user_orders: the raw qty / remaining here are the event payload as written, and the same size can be spelled differently on two events ("40.000000" and "40").

notifications (/ws/user, no ids, id = notification id)

The live side of the web app’s notification centre: display text, created according to the preferences set in the web app, best-effort and not re-sent after a disconnect. Use the typed channels above for trading and accounting state.

dispute_intent (/ws/user, id = dispute intent id)

Status changes of disputes filed from the PredictStreet web app. A challenge sent on-chain directly has no dispute intent, so API integrations normally see nothing here; follow GET /api/challenges?challengerWallet=<address> instead.

bond_payout_credited (/ws/user, id = challenge id)

The bond of a challenge you raised was credited back (override: bond plus reward; void or emergency proposal clear: bond), and the amount is now claimable from pendingBondPayouts on the oracle with withdrawBondPayout(to). See Challenges.

me_limits_invalidated (/ws/user, id = transaction hash)

Your wallet’s on-chain deposit tier, caps or a deposit / trading / withdrawal restriction changed. The frame carries no values — re-read GET /api/me/deposit-limits. dispute_intent, bond_payout_credited and me_limits_invalidated take your wallet as the only id on an API-key socket ("ids": ["0x…"], the greeting’s walletAddress) and no ids on a JWT session. Full shapes and examples for all four channels: Server events.

token_trade_matches (/ws/market, id = tokenId)

Fires the moment the matcher prints a trade — fastest tape feed. On a binary market each trade is published on both outcome tokens (the NO frame at price 1 − p, same tradeId); if you subscribe to both, keep the frame whose outcomeIndex equals takerOutcomeIndex (or dedupe on tradeId) before counting trades or volume.

token_trade_settlements (/ws/market, id = tokenId)

Fires when the platform indexes an on-chain OrderFilled event. The exchange emits one per filled order (each maker order and the taker order of a match), so one match produces several frames, each describing the order in orderHash. Use it for accounting-grade confirmations of your own orders, keyed by orderHash; don’t add the frames up as independent trades. Example:

token_book (/ws/market, id = tokenId)

seq is monotonically increasing. After subscribe the server pushes one book_snapshot (depth 100), then one book_update (the full state) per book change. Right after each book_update comes a book_delta for the same change, carrying the same seq and data.prevSeq; the delta is skipped when the gateway doesn’t hold the previous state, so it never sends a divergent delta. Apply one stream, not both: either book_update frames, or book_snapshot + book_delta frames. In either stream, a frame whose data.prevSeq differs from the last seq you applied means you missed a change. On such a gap, or when a market you’d expect to be active goes quiet, issue get_book_snapshot to force a fresh snapshot. See Reconnect — orderbook resync. What a book frame holds. book_update is not just the best bid and ask: it is the complete book after the change, every resting price level on both sides, aggregated per price. Levels whose total size is below a small dust threshold are left out of every book frame, the same filter the REST orderbook applies. book_snapshot is capped at 100 levels per side, but prices sit on a 0.01 grid, so a side never has more than 99 levels and a snapshot also carries the whole book. book_delta carries the same change as a diff. Apply book_update frames, or book_snapshot + book_delta frames, not both. Both book_snapshot and book_update envelopes carry a hash field (16-hex truncated SHA-256 over canonical (bids, asks) — price + quantity only, count excluded). The same hash function is applied to the REST orderbook response. A client that maintains its own book view can hash it the same way and compare against the envelope hash to detect drift between the WS-applied state and the authoritative server view. book_delta envelopes omit hash. To check the book after applying a delta, compare your hash with the hash of the book_update that has the same seq — it arrives just before the delta.

token_ohlc (/ws/market, id = tokenId)

Rolling 5-second candles built from the trade_matched tape, pushed 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.

market_price_tick (/ws/market, id = lowercased <symbol>:<outcomeIndex>)

The live edge of GET /api/markets/{symbol}/price-history: at most about one frame per second per outcome, only while the market is OPEN and only when the outcome’s best bid, best ask or last trade changed. No backfill — seed from ?range=live.

all_trade_matches / all_trade_settlements / all_books (/ws/market, no ids)

Firehoses of the per-token frames for every token on the platform, with the frame’s tokenId as id. all_trade_matches still carries two frames per binary trade, a connection that also holds the per-token channel gets each frame once per sid, and all_books is high-volume. Details: Server events.

global_activity (/ws/market, no ids, id = tradeId)

One frame per matched trade platform-wide, in the taker’s view (their outcome, side and price), with market / event titles and the taker’s username attached when available. Seed from GET /api/activity/recent, which serves the same fields.

condition_lifecycle (/ws/market, id = conditionId)

These are the only frames on this channel. Nothing is pushed on subscribe, and resolution or status changes are not sent here. Subscribe to market_lifecycle (status_changed, market_resolved) for those, and read the current status from GET /api/markets/{slug}.

market_lifecycle (/ws/market, id = lowercased market symbol)

Keyed by the market symbol. It carries status, dispute and resolution; condition_lifecycle carries only the on-chain trading pause. Optional fields are omitted when absent rather than sent as null.

crypto_price_tick (/ws/market, id = lowercased instrumentId)

The underlying-asset price for crypto markets, keyed by instrument (btc-usd). price is an unscaled integer as a decimal string — render as price / 10 ** decimals with a bigint or decimal helper. at is the observation time and the point’s x-coordinate; src is "datastreams" (~1/s live spot) or "candlestick" (~1/min candle close). See Crypto price feeds. Example:

system (/ws/market, id = "platform_status")

To detect a platform freeze, poll GET /api/platform/status, which returns { frozen, reason, since, activeNotices }.