Skip to main content
Every client → server message uses the same envelope on both gateways:
{ "id": <client_correlation_id>, "cmd": "<verb>", "params": { ... } }
  • id — client-side correlation id; echoed in the response.
  • cmd — one of subscribe / update_subscription / unsubscribe / list_subscriptions / ping.
  • params — command-specific payload (omitted for ping and list_subscriptions).
Every accepted subscription gets a connection-local sid (numeric). Use sid for subsequent updates / unsubscribes and to route incoming events. sids are not stable across reconnects.

subscribe

On /ws/user:
{
  "id": 1,
  "cmd": "subscribe",
  "params": {
    "subscriptions": [
      { "channel": "user_activity" }
    ]
  }
}
On /ws/market:
{
  "id": 1,
  "cmd": "subscribe",
  "params": {
    "subscriptions": [
      { "channel": "token_trades",     "ids": ["12345...", "67890..."] },
      { "channel": "token_book",       "ids": ["12345..."] },
      { "channel": "condition_status", "ids": ["0xabc..."] },
      { "channel": "system",           "ids": ["platform_status"] }
    ]
  }
}
Response (market gateway, with a bad tokenId rejected):
{
  "id": 1,
  "type": "subscribed",
  "accepted": [
    { "sid": 12, "channel": "token_trades",     "ids": ["12345...", "67890..."] },
    { "sid": 13, "channel": "token_book",       "ids": ["12345..."] },
    { "sid": 14, "channel": "condition_status", "ids": ["0xabc..."] },
    { "sid": 15, "channel": "system",           "ids": ["platform_status"] }
  ],
  "rejected": [
    {
      "channel": "token_trades",
      "ids":     ["not-a-token-id"],
      "code":    "invalid_params",
      "message": "invalid tokenId"
    }
  ]
}

Normalization rules

  • All addresses lowercased.
  • tokenId is decimal-string with no leading zeroes.
  • conditionId is lowercased 32-byte hex.
  • Duplicate ids removed.

Rejection codes

codeWhen
invalid_paramsunknown channel, malformed id (bad hex, bad address), missing required ids, or user_activity sent with ids
forbiddenwrong gateway (public channel on /ws/user or user_activity on /ws/market)
api_key_scope_missingkey is missing a scope required by the channel (e.g. user_activity needs portfolio:read)

update_subscription

Modify the id list of an existing id-based subscription instead of opening a new one. Not allowed on user_activity (no ids).

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_trades",
  "ids": ["456", "789"]
}
update_subscription on user_activity is rejected with invalid_params.

unsubscribe

{
  "id": 4,
  "cmd": "unsubscribe",
  "params": { "sids": [12, 13] }
}
{ "id": 4, "type": "unsubscribed", "sids": [12, 13] }
Unknown sids are silently ignored (idempotent — the response only lists sids that were actually removed).

list_subscriptions

{ "id": 5, "cmd": "list_subscriptions" }
{
  "id": 5,
  "type": "subscriptions",
  "items": [
    { "sid": 10, "channel": "user_activity" }
  ]
}
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. There is no application-level idle timeout, but production clients should still send a ping every 20–30 s to defeat intermediate proxies / load balancers that close idle TCP connections.