Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.adipredictstreet.com/llms.txt

Use this file to discover all available pages before exploring further.

All client commands use the command envelope:
{ "id": <client_correlation_id>, "cmd": "<verb>", "params": { ... } }

Subscribe

Each gateway is a separate socket with its own sid namespace. Subscribe per gateway.

/ws/user — private

{
  "id": 1,
  "cmd": "subscribe",
  "params": {
    "subscriptions": [
      { "channel": "user_orders" },
      { "channel": "user_fills" },
      { "channel": "vault_positions", "ids": ["0x1234...abcd"] }
    ]
  }
}
{
  "id": 1,
  "type": "subscribed",
  "accepted": [
    { "sid": 10, "channel": "user_orders" },
    { "sid": 11, "channel": "user_fills" },
    { "sid": 12, "channel": "vault_positions", "ids": ["0x1234...abcd"] }
  ],
  "rejected": []
}

/ws/market — public

{
  "id": 1,
  "cmd": "subscribe",
  "params": {
    "subscriptions": [
      { "channel": "token_trade_matches",     "ids": ["12345...", "67890..."] },
      { "channel": "token_trade_settlements", "ids": ["12345..."] },
      { "channel": "token_book",              "ids": ["12345..."] },
      { "channel": "token_ohlc",              "ids": ["12345..."] },
      { "channel": "condition_lifecycle",     "ids": ["0xabc..."] },
      { "channel": "system",                  "ids": ["platform_status"] }
    ]
  }
}
{
  "id": 1,
  "type": "subscribed",
  "accepted": [
    { "sid": 10, "channel": "token_trade_matches",     "ids": ["12345...", "67890..."] },
    { "sid": 11, "channel": "token_trade_settlements", "ids": ["12345..."] },
    { "sid": 12, "channel": "token_book",              "ids": ["12345..."] },
    { "sid": 13, "channel": "token_ohlc",              "ids": ["12345..."] },
    { "sid": 14, "channel": "condition_lifecycle",     "ids": ["0xabc..."] },
    { "sid": 15, "channel": "system",                  "ids": ["platform_status"] }
  ],
  "rejected": []
}
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.
  • tokenId is decimal-string form (no leading zeroes).
  • conditionId is lowercased 32-byte hex.
  • Duplicate ids removed.

Rejection codes

A subscription that fails validation comes back under rejected[]:
{ "channel": "user_orders", "code": "api_key_scope_missing", "message": "api_key_scope_missing: channel user_orders needs portfolio:read" }
The 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.
CodeWhen
invalid_paramsunknown channel, malformed id (bad hex, bad address), or missing required ids
forbiddenwrong gateway (public channel on /ws/user or private channel on /ws/market); also raised when vault_positions ids aren’t covered by the API key’s associated_vault
api_key_scope_missingkey is missing a scope required by the channel (private channels need portfolio:read)
subscription_cap_exceededthis connection already holds the maximum subscriptions allowed

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
{ "channel": "user_orders" }

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
{ "channel": "user_fills" }

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
{ "channel": "vault_positions", "ids": ["0x1234...abcd"] }
Each requested vault must be covered by the API key’s 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 outcome tokenIds (decimal-string)
  • Pushes: trade_matched (source: "matcher") per off-chain match
{ "channel": "token_trade_matches", "ids": ["12345..."] }
Fires the moment the matcher prints a trade — fastest tape feed, arrives several seconds before the chain settlement.

token_trade_settlements — chain-confirmed settlements

  • Gateway: /ws/market
  • ids: one or more outcome tokenIds
  • Pushes: trade_settled (source: "chain") on indexed OrderFilled
{ "channel": "token_trade_settlements", "ids": ["12345..."] }
Use this for accounting-grade trade confirmations. Carries txHash, blockNumber, and orderHash.

Public — books

token_book — public orderbook snapshots + updates

  • Gateway: /ws/market
  • ids: one or more outcome tokenIds
  • Pushes: book_snapshot (initial), book_update (full top-of-book), book_delta (diff against prevSeq), book_snapshot_failed (snapshot fetch couldn’t complete)
{ "channel": "token_book", "ids": ["12345..."] }
After subscribe the server pushes one 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.
Silent gaps look like idle. When the gateway drops a frame, you see no book_update for a while — the same way you’d see nothing on a quiet market. Don’t equate silence with “no change”. If you stop receiving book_update for the channel for ~30 s on a market you’d expect to be active, issue get_book_snapshot proactively to confirm state.
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 outcome tokenIds
  • Pushes: ohlc_update (interval: "s5") — fires whenever the matcher prints inside the active 5-second window
{ "channel": "token_ohlc", "ids": ["12345..."] }
Cheaper bandwidth than the trade tape if you only need chart bars. Bars are emitted continuously while open and once final with isClosed: true.

Public — lifecycle

condition_lifecycle — market-level lifecycle

  • Gateway: /ws/market
  • ids: one or more conditionIds (lowercased bytes32 hex)
  • Pushes: market_paused, market_unpaused, market_resolved, market_status
{ "channel": "condition_lifecycle", "ids": ["0xabc..."] }
Use for pause/unpause, oracle outcome announcement, resolution / payout state — anything market-wide that’s keyed to the condition rather than to one outcome token.

system — platform-wide status

  • Gateway: /ws/market
  • ids: must be ["platform_status"]
{ "channel": "system", "ids": ["platform_status"] }
Use for the platform freeze / maintenance / degraded-mode banner.

Resource limits

Per-connection caps. Exceeding any of them surfaces as a structured error rather than a silent drop:
LimitDefaultError code
Max active subscriptions per connection256subscription_cap_exceeded (in rejected[])
Max ids per single subscription / add_ids call100subscription_too_many_ids (in rejected[]) — message: "subscription accepts at most 100 ids"
Max bytes per inbound command frame65 536server closes with 1009 too big
Command rate~50 / stoo_many_commands (in error push)
Outbound buffer per connection8 MBserver closes with 1009 outbound_buffer_full (slow consumers)
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, 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

{
  "id": 2,
  "cmd": "update_subscription",
  "params": { "sid": 12, "action": "add_ids", "ids": ["789"] }
}

Remove ids

{
  "id": 3,
  "cmd": "update_subscription",
  "params": { "sid": 12, "action": "remove_ids", "ids": ["123"] }
}
Response (server returns the resulting full id set):
{
  "id": 2,
  "type": "ok",
  "sid": 12,
  "channel": "token_trade_matches",
  "ids": ["456", "789"]
}

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).
{
  "id": 7,
  "cmd": "get_book_snapshot",
  "params": { "sid": 14 }
}
Or by token ids when you’d rather not look up the sid:
{
  "id": 7,
  "cmd": "get_book_snapshot",
  "params": { "tokenIds": ["12345..."] }
}
Server pushes a book_snapshot per resolved subscription, or book_snapshot_failed per token if the snapshot fetch failed:
{
  "type": "book_snapshot_failed",
  "sid":  14,
  "channel": "token_book",
  "id":   "12345...",
  "data": { "tokenId": "12345...", "reason": "upstream_unavailable", "tsMs": 1776949200000 }
}
Without this command, snapshot refresh used to require an unsubscribe + resubscribe round-trip — get_book_snapshot removes the gap window.

Unsubscribe

{
  "id": 4,
  "cmd": "unsubscribe",
  "params": { "sids": [12, 13] }
}
{ "id": 4, "type": "unsubscribed", "sids": [12, 13] }

List subscriptions

{ "id": 5, "cmd": "list_subscriptions" }
{
  "id": 5,
  "type": "subscriptions",
  "items": [
    { "sid": 10, "channel": "user_orders" },
    { "sid": 11, "channel": "user_fills" }
  ]
}
Useful after a reconnect to confirm what the server thinks you’re subscribed to before rebuilding state.

Ping

{ "id": 6, "cmd": "ping" }
{ "id": 6, "type": "pong", "ts": 1776949200000 }
ts is the server clock in milliseconds — clients can use it as a clock-skew probe.