跳到内容

交易

WebSocket events

The REST endpoint POST /api/v1/orders returns 202 Accepted with an empty trades array — matches happen asynchronously after the response, and trade events arrive over WebSocket. This page documents the wss:// endpoint, the three channel families, the event types you can expect, and the fallback poll pattern for handling reconnect gaps.

Why a WebSocket

REST tells you what happened in the past. WebSocket tells you what is happening right now. For trading bots, the WebSocket is not optional — it is how you learn that your order filled. The REST POST /orders response intentionally returns before matching has run.

Endpoint

One WebSocket connection per authenticated session. Authenticate with a JWT in the URL token parameter. Per-IP rate limit applies on connection establishment but not on message volume once connected.

wss://staging.satoriex.io/ws?token=<jwt>

Channels

Three channel families. The user channel binds to your session automatically; market and marketdata channels require explicit subscribe messages.

ChannelWhen you get itEvent types
userAutomatic on every authenticated connection — no subscribe required.wallet_changed, deposit_confirmed, withdraw_* (6 types: requested, submitted, approved, rejected, confirmed, failed), kyc_approved/rejected, proposal_approved/rejected, referral_bonus, dispute_filed, mention, market_resolved, order_filled
market:{id}After sending { type: "subscribe", market_id }.trade, order, order_update, settlement, comment
marketdata:{id}After sending { type: "subscribe_marketdata", market_id }.price_update (mid + best bid/ask), orderbook snapshot deltas

Subscribe + dispatch example

Browser / Node WebSocket client. The same shape works in any language with a WebSocket library — subscribe by sending a JSON message, then dispatch on payload.type.

// Connect with bearer JWT
const ws = new WebSocket("wss://staging.satoriex.io/ws?token=" + jwt);

// Subscribe to per-market events (trades, orders, settlements, comments)
ws.send(JSON.stringify({ type: "subscribe", market_id: "mkt_abc123" }));

// Subscribe to live orderbook + price for one market
ws.send(JSON.stringify({ type: "subscribe_marketdata", market_id: "mkt_abc123" }));

// User-scope events (wallet, deposit, withdraw, KYC, proposals, notifications)
// arrive automatically on every authenticated connection — no explicit subscribe.

ws.onmessage = (ev) => {
  const msg = JSON.parse(ev.data);
  switch (msg.type) {
    case "trade":            // fill on a market you're subscribed to
    case "order":            // order state transition
    case "order_update":     // resting order modified
    case "settlement":       // market resolved + payout posted
    case "price_update":     // mid moved (only on subscribe_marketdata)
    case "wallet_changed":   // your USDC balance changed
    case "deposit_confirmed":
    case "withdraw_submitted":
    case "withdraw_approved":
    case "withdraw_rejected":
    case "withdraw_confirmed":
    case "withdraw_failed":
    case "kyc_approved":
    case "kyc_rejected":
    case "proposal_approved":
    case "proposal_rejected":
    case "market_resolved":
    case "dispute_filed":
    case "referral_bonus":
    case "comment":
    case "mention":
      // ... handle each type
      break;
  }
};

60-second fallback poll pattern

On reconnect, you may miss events fired while you were disconnected. The recommended pattern is a 60-second safety-net poll for any query that has a WebSocket invalidation path. This is the cadence the SatoriEx frontend uses.

  • Use 60s fallback poll: market detail, market grid, portfolio — anywhere a WebSocket event would normally invalidate the cached query.
  • Keep faster polls: chart / candle data without a WebSocket source, admin tables, anywhere staleness budget is under 60s and no WebSocket invalidator exists.
  • No poll: when WebSocket is reliably the only freshness path and a 60-second recovery delay on reconnect is acceptable — rare; prefer the fallback.

Hydration gate

If your client loads initial state via REST after the WebSocket is already open, queue incoming events until your REST snapshot has hydrated. Otherwise a trade event for an order you haven't loaded yet will arrive before your /me/orders snapshot, and your local state will be inconsistent. The SatoriEx web frontend uses a user + _hydrated boolean gate before processing WS events.

Wire-format contract

Every event's payload.type is a stable string. The same string is used in (1) the Go ws_events const, (2) the Go NotificationType, (3) the frontend WS_EVENT const, and (4) the user-WS-provider handler. The contract is stable; we will not rename event strings without a deprecation cycle in the changelog.

← Order lifecycle (REST states) · API reference →