Endpoint
Query parameters
Authentication is identical to the other WS streams: signed handshake for programmatic clients. Invalid credentials close with
4401; a missing subaccount_id closes with 4400.
Protocol
All frames are JSON. You only receiverfq_canceled frames for RFQs that were streamed in your feed.
Client → server
subscribe frame atomically replaces the previous filter set. An empty filters object matches every RFQ. We highly recommend using filters.
Server → client
legs is the composition of the (combo) market, where buy_flag: true means the combo holds YES of that leg, and the taker picks a side only when accepting a quote. Exactly one of qty (contracts) and target_cost (dollars) is set, matching how the requester sized it.
quote frames are private to the socket’s subaccount and delivered regardless of filters.
Recovering after a reconnect
This stream has no replay, so a client that reconnects (or a page that reloads) has no way to learn about quotes it already owns from the socket alone. CallGET /v1/rfqs/quotes?subaccount_id=<uuid> on connect and after every reconnect: it returns your live quotes with their current status, auto_confirm and, for an accepted one, the confirm_deadline_ts you are racing. Then follow the socket’s quote frames from there.
Quoting an RFQ (maker flow)
Quotes are two-sided and priced in YES-space:bid is your price to buy YES, ask your price to sell YES.
POST /v1/rfqs/{river_rfq_id}/quoteswith{ subaccount_id, bid, ask, auto_confirm }→201 { river_order_id }. The exchange is the RFQ’s; the request is the same on Kalshi and Polymarket US. On Polymarket US the subaccount must hold institutional credentials (a retail subaccount gets400). A quote carries no size: it answers the RFQ whole, so the exchange holds collateral against the requester’s size, not yours. On the ask side that is(1 - ask)per contract, which is what a cheap-looking ask on a large RFQ actually costs. The quote appears in your orders as anRFQ_MAKERorder with no side (buy_flag: null) until a taker picks one.- When a taker accepts, a
quoteframe arrives withstatus: "accepted", the side that was hit, andconfirm_deadline_ts. The exchange gives you 3 seconds to confirm. POST /v1/rfqs/quotes/{river_order_id}/confirminside the window. Or setauto_confirm: trueat creation and skip this step: confirmation then fires server-side as soon as the exchange reports the acceptance. Refuse or cancel any time before confirmation withDELETE /v1/rfqs/quotes/{river_order_id}.status: "executed"follows; the trade lands as a normal fill on the fills stream and your order flips to EXECUTED with the executed side and price stamped.
status: "cancelled", reason: "confirm_timeout").
A quote the exchange refuses outright never rests: POST /v1/rfqs/{river_rfq_id}/quotes still returns 201 (it is queued, not sent inline), then the order lands in REJECTED and a quote frame arrives with status: "cancelled" and reason set to the exchange’s own error code, for example insufficient_balance. The RFQ stays open for you to quote again.
