Skip to main content
Skip to main content

RFQ WebSocket API

The WebSocket API streams live updates: RFQ and quote status, balance snapshots, and top-of-book prices. It is read-only: every client-initiated mutation goes over the REST API; the socket carries the handshake, the subscriptions, and the pushes.

Frame-level documentation lives in the interactive WebSocket reference, rendered from the AsyncAPI 3.0 spec at /api/rfq-asyncapi.yaml.

Connecting

The shared client stream is wss://rfq-api.silhouette.exchange/v1/rfq/ws. Frames are JSON, tagged with a kind discriminator. After connecting, the client authenticates with an in-band auth frame (the credential pair from SIWE login signs a canonical GET /v1/rfq/ws, the same recipe as a signed REST request) and the server replies with a health frame. The socket is authenticated once; later frames carry no signature.

Liveness is an application-level keepalive: send {"kind": "ping"} roughly every 15 seconds and the server answers {"kind": "pong"}. A reconnect needs a fresh auth frame and fresh subscriptions.

Topics

Dispatch is opt-in: after the health frame the server sends nothing until the client subscribes. A subscribe frame names a topic and is acknowledged with subscribed; unsubscribe opts back out.

{ "kind": "subscribe", "topic": "prices", "instrumentId": "XTSLA-USDC-SPOT" }
TopicScopeStreams
pricesPublic, per instrumentA price frame per top-of-book change, with the current top of book on subscribe
balancesThe authenticated userA balanceSnapshot on every committed change, with a full snapshot on subscribe
rfqStatusThe authenticated user's RFQsAn rfqStatus frame per status transition, with the user's open RFQs on subscribe
quotesThe authenticated user's RFQsA quoteUpdate frame per competing quote received (live-only)
openRfqsEligible makers, per instrumentAn rfq frame per open RFQ on that instrument (live-only)
quoteStatusMakers onlyA quoteStatus frame per transition of the maker's own quotes, with recent quotes replayed on subscribe

Topics with a baseline reply with current state on subscribe; quotes and openRfqs are live-only, so a client joining mid-auction recovers what it missed over REST. Balance snapshots are full state delivered in per-user commit order, so the last snapshot a subscriber sees is always its true current balance.

The two maker topics are refused with UNSUPPORTED_CHANNEL when the account does not qualify, and the socket stays open. quoteStatus needs an active maker record; openRfqs additionally needs approval for the instrument's pair and at least one operated settlement adapter, re-checked as each RFQ is pushed rather than only at subscribe.

Errors arrive as {"kind":"error","code":…,"message":…}. AUTH_REQUIRED closes the session, and INVALID_MESSAGE closes it too unless it answers a frame sent out of sequence. UNSUPPORTED_CHANNEL, RATE_LIMITED, INVALID_INSTRUMENT_ID, UNKNOWN_INSTRUMENT and UNAVAILABLE refuse the offending frame and leave the socket open; UNAVAILABLE changed nothing, so retry the frame.

Maker price ingest

Makers publish indicative price ladders to a separate, maker-only socket at wss://rfq-api.silhouette.exchange/v1/rfq/prices/ws. The HTTP upgrade request is authenticated with the standard HMAC headers over a canonical GET /v1/rfq/prices/ws, and each message is a bare ladder object with no kind envelope. Taker price consumption stays on the prices topic of the shared stream.

Errors on this socket use the same {"kind":"error","code":…} envelope as the shared stream, so one reader dispatching on kind handles both.