For the complete documentation index, see llms.txt. This page is also available as Markdown.

WebSocket changelog

Customer-visible WebSocket protocol changes

2026-09-09 — Address-scoped unsubscribe on user-entity channels

  • unsubscribe with addresses (users for userOrderUpdates) now subtracts those addresses from the subscription the connection holds, leaving the rest streaming. Previously subscribing addresses merged them into one subscription but unsubscribing required the request to name the entire merged set — any smaller request was acknowledged and removed nothing, so those addresses kept delivering and their subscription units were never released. A connection that added and dropped addresses over time therefore climbed toward its tier's total-subscription limit and eventually got Total subscription limit reached for tier ... (N/M subscriptions) while holding far fewer addresses than N.

  • Applies to userFills, userOrderUpdates, userNonFundingLedgerEvents, userTwapStatusUpdates, userLeverageUpdates, userIsolatedMarginUpdates and userCompletedTrades.

  • Unsubscribing every address held drops the subscription and sets unsubscribed: ["<channel>"], as before. Addresses you do not hold are ignored.

  • The subscription's other parameters pick the merge group: aggregateByTime for userFills, dex and addStakingEvents for userNonFundingLedgerEvents, dex for userCompletedTrades. An unsubscribe subtracts only from the group whose parameters match, so two userFills subscriptions that differ on aggregateByTime stay independent.

  • No wire-format change: no field was added, removed, or renamed.

2026-09-08 — hip4MarketEvents subscription

  • New hip4MarketEvents subscription: every HIP-4 outcome and question lifecycle transition — registration, child association, description change and settlement — pushed as the chain produces it, replacing polling registeredOutcomes / settledOutcomes on a timer. Short-duration recurring markets can register and settle between polls; this channel does not miss them.

  • Optional venue, category, subCategory and class filters, matched case-insensitively and combined with AND. Omit all of them to receive every HIP-4 market event. An event that does not carry a filtered field never matches that filter — validator-registered markets carry no venue, price markets carry no category, canonical markets carry no class.

  • Each event carries entity (outcome or question), action, and time / blockNumber / txIndex, so every step is timestamped. Only executed actions are delivered.

  • Question events carry namedOutcomes (the child outcomes of a question) and, on settlement, settleWinningOutcomeId and childSettles.

  • Reconnect replay: subscribe with your last cursor to replay missed events; the same filters apply to replay as to live.

  • Venue lifecycle (a venue activating or deactivating) is not on this channel — use the REST outcomeVenuesOverview.

  • Additive: no existing channel or field changes.

2026-09-02 — perpPrices subscription

  • New perpPrices subscription: the accepted oracle/mark/external perp prices of the subscribed coins on every dex including HIP-3 — the streaming counterpart of perpPriceHistoryByTime, with row-for-row identical fields.

  • A message is sent for every block, prices: [] when the block carried no round for your coins, so the chain height and time can be tracked continuously and the per-connection seq is gap-free.

  • Coins always merge: a repeat subscribe unions into the connection's single perpPrices subscription, unsubscribe with coins subtracts, without coins it removes the subscription.

  • Reconnect replay: subscribe with your last cursor to replay up to 30 seconds of missed rounds (tick messages are not replayed); hasGap flags a cursor older than the cache.

  • Additive: no existing channel or field changes.

2026-08-28 — Subscription ack ordering

  • A subscription change is now acknowledged after every frame that was already in flight when it committed. Previously an l4BookUpdates merge: true subscribe could be acked with the merged coin list while the next frame — a block already being fanned out — still covered only the previous set, which read as a coin intermittently going missing right after it was added.

  • Applies to l4BookUpdates, userFills / builderFills / allFills / liquidationFills / trades, userOrderUpdates / builderOrderUpdates / tpslUpdates, userNonFundingLedgerEvents / allUserNonFundingLedgerEvents / fundingRates, the TWAP status channels, and the leverage / isolated-margin channels. l2Book, l2BookDiff, bbo, the candle, asset-context, mids, and dashboard channels are unchanged for now — their acks may still precede an in-flight frame.

  • The rule to code against: each frame carries the coin set its block was fanned out with, and the ack marks the boundary. Frames before the ack may predate the change; every frame after it reflects it. Apply a coin-set change on the ack, not on the request. The hold is bounded — if fan-out stalls past it the ack is released early, and every block still being fanned out at that moment can deliver a pre-change frame after it (blocks fan out concurrently, so this is not limited to one frame). It resolves once those blocks drain.

  • Same rule for unsubscribe: a trailing frame containing dropped coins now arrives before the ack, not after it (this replaces the note in the 2026-08-20 entry below).

  • No wire-format change: no field was added, removed, or renamed.

2026-08-20 — Opt-in merge for l4BookUpdates

  • Coin-list l4BookUpdates stays one stream per subscribe by default. {"type":"l4BookUpdates","coins":["ETH"]} then {"type":"l4BookUpdates","coins":["BTC"]} is still two frames per block.

  • Set "merge": true on a coin-list subscribe to union those coins into the connection's existing coin-list slots. The next block is one frame covering the merged set, so adding an instrument does not need an unsubscribe/resubscribe cycle. The subscribe confirmation echoes the merged coin list. merge is subscribe-only: it is rejected on an unsubscribe, on the all-markets firehose, and on other subscription types.

  • unsubscribe with coins subtracts when the connection holds a single coin-list slot (one batched subscribe, or the result of merge: true). Several coin-list slots keep exact-match unsubscribe. Coins you do not hold are ignored. unsubscribe without coins still removes the all-markets firehose only. A coin-scoped unsubscribe against only the firehose is rejected.

  • The tier coin cap now applies to the effective (post-merge) coin set rather than to each raw request, so repeated merge: true (and the existing trades / allFills unions) cannot walk past it. A single request is still capped at 200 names.

  • When a subscribe carries a cursor, replay covers the coins named in that request. After a merge the ack still echoes the full merged list.

  • After a coin-scoped unsubscribe is acknowledged, one already-in-flight block may still deliver a frame containing the dropped coins. Steady state resumes from the next block.

  • Draining the coin-list while the all-markets firehose is still held does not set unsubscribed: ["l4BookUpdates"] — that field means the channel is gone, and the firehose is still on it.

  • Upcoming: merge will become the default for coin-list l4BookUpdates in a future release. Set it explicitly now — "merge": true to adopt early, "merge": false to pin today's one-stream-per-subscribe behaviour through the change. The flag stays accepted afterwards; only clients that send neither will see a behaviour change.

2026-08-07 — Trigger TWAP fields and userTwapStatusUpdates

  • New userTwapStatusUpdates subscription: TWAP status events filtered to the subscribed addresses (multi-address, merge-on-resubscribe, cursor replay supported) — the user-scoped counterpart of allTwapStatusUpdates.

  • TWAP status updates (both channels) now carry the trigger-TWAP fields the node added on 2026-07-25: triggerPx, triggerAbove, and stopPx. The fields are omitted for plain TWAPs.

  • New TWAP status values from the same node update: waitingForTrigger and stopped.

2026-08-04 — requestId bounds and outcome market activity

  • Optional requestId is capped at 128 characters and must not contain control characters. Violations reject with code: "invalid_request_id" (oversized values are not echoed).

  • Inbound WebSocket frames are capped (default 256 KiB, WS_MAX_INBOUND_MESSAGE_BYTES) so control-plane messages cannot use Axum’s 64 MiB default.

  • Active-market metadata refresh now includes HIP-4 outcome markets via outcomeMeta, so outcome wire coins (#{outcome_id * 10 + side_index}) receive the same active/inactive advisories as perps and spot. If outcomeMeta fails, the server still commits the perp+spot snapshot (partial_success) and treats outcome activity as unavailable until the next full refresh.

2026-07-22 — Attributable subscription feedback

  • Subscribe and unsubscribe requests may include an optional string requestId; confirmations and rejects echo it.

  • subscriptionUpdate retains subscribed and failed and adds operation, subscription, and, for unsubscribe confirmations, unsubscribed.

  • Subscription errors retain type and message and add request context. Orderbook-family invalid-coin and coin-count rejects also include stable code and details fields.

  • l2Book, l2BookDiff, l4BookUpdates, and bbo confirmations may include advisory warnings for inactive coins or unavailable activity metadata. These warnings do not fail the subscription.

  • Orderbook invalid-name errors now name every distinct invalid value. Requests containing more than 200 coin entries now report a coin-count error instead of an address-format error.

These changes are additive except for the corrected error text. Clients should continue reading the existing fields they need and ignore unknown fields and message types.

Last updated