Subscribe to RFQs
The WebSocket stream, snapshot semantics, and the REST fallback
The stream
import WebSocket from "ws";
const ws = new WebSocket("wss://<host>/maker/stream", {
headers: { "X-API-Key": process.env.API_KEY },
});The key rides the X-API-Key upgrade header. Browser-native WebSocket
cannot set upgrade headers and therefore cannot authenticate this maker
stream; use a Node client such as ws. A missing or unknown key closes the
socket with code 4401 before you receive anything; so does rotating the
key the socket opened with.
Every frame is JSON {type, payload} — the full catalog is on
WS Events.
Snapshot: reconnect IS resync
On connect the server immediately replays the current state:
rfq.createdfor every open RFQ you are invited to;quote.created+quote.statusfor your live quote on each.
Then live events follow. This means you never need a separate bootstrap call and never need to persist state across reconnects — drop your local state on disconnect and rebuild it from the snapshot:
ws.on("close", () => {
tracked.clear(); // stale by definition
setTimeout(connect, 1000); // snapshot rebuilds everything
});If the server itself loses state it sends resync {} — treat it exactly
like a disconnect: close, reconnect, consume the fresh snapshot.
REST fallback for simple bots
The socket is not just a feed — it is also presence: the desk only
offers a taker your hint while your /maker/stream socket is open, so a
bot with no open socket is never invited and /rfq/incoming stays empty
forever, no error, nothing to catch. Keep the socket connected even if you
drive everything off REST; then, if reacting to events is inconvenient,
poll instead of parsing frames:
curl -H "X-API-Key: $API_KEY" $API_URL/rfq/incomingIt returns every open RFQ you are invited to — plus those that closed in the
last 10 minutes (open: false), whose myQuote shows your quote's outcome
(won, lost, …). Quote the entries with open: true whose myQuote is
null or no longer pending (it expired or was revoked).
The honest trade-offs: polling latency eats into the RFQ deadline, and
settlement progress (trade.step) is stream-only — settled trades land in
GET /maker/trades. The WebSocket is the canonical path.