Naming conventions across channels
Each channel has its own event naming grammar. Knowing the pattern up front makes every table below scannable at a glance:- SSE events are bare or underscore separated names —
best_quote,quote_request,quote_request_expired. - WebSocket events are
resource:verb, colon namespaced —quote:accepted,position:settled. - Webhook events are
resource.verb, dot namespaced —position.settled,parlay.status_changed.
quote_request and quote_request_expired use underscores, while quote_request:updated uses a colon (see the summary table on that page).
One trade, across every channel
Read this left to right for any given moment, or top to bottom to follow one trade start to finish. ”—” means the moment has no representation on that channel.quote:* WebSocket events (quote:accepted, quote:confirmed, quote:executed, quote:expired, quote:rejected, quote:withdrawn) deliver only to the awarded market maker, on their private mm:quotes:{mm_id} channel. position:* events deliver on rfq:{rfq_id}, the channel a user (or anyone tracking a specific trade) subscribes to. The two families are not interchangeable views of the same moment — several rows below have a WebSocket event for the MM and nothing for the user, or vice versa.| Lifecycle moment | SSE — user stream (/v1/quote-requests/{id}/stream) | SSE — MM stream (/v1/mm/quote-requests/stream) | WebSocket | Webhook | REST status |
|---|---|---|---|---|---|
| Quote request created | — (no dedicated “created” event; the stream opens after creation and the first frame is best_quote, often null) | quote_request | not delivered on this channel — WebSocket starts at quote acceptance | not delivered on this channel — webhooks are posttrade only | quote request status: "active" — GET /v1/quote-requests/{id} |
| Quote updated (price changes, or legs/amount edited) | best_quote | quote_request:updated — only fires when the request itself changes (PATCH); no MM stream event exists for an individual quote submission, yours or a competitor’s | not delivered on this channel | not delivered on this channel | quote request version/book_seq increments; status stays "active" |
| Quote accepted / committed | status (status: "committed") followed immediately by the terminal committed event, then the stream closes | quote_request_expired (reason: "committed"; won: true for the winning MM, false for every other MM) | quote:accepted + mm_quote:accepted — MM only. Nothing pushes to the user on WebSocket; they already have the outcome from the synchronous commit response | parlay.status_changed (status: "accepted", accepted_quote_id set) | RFQ status: "accepted" — GET /v1/rfqs/{id} |
| Confirmed by maker | not delivered on this channel — the stream already closed at commit | not delivered on this channel | quote:confirmed — MM only. No user facing WebSocket event marks this step specifically | parlay.status_changed (status: "confirmed") | RFQ status: "confirmed" |
| Executed on chain | not delivered on this channel | not delivered on this channel | quote:executed (MM channel) and position:created (rfq:{rfq_id} channel — collateral locked, position_pda set) | parlay.status_changed (status: "executed") | RFQ status: "executed" |
| Settled | not delivered on this channel | not delivered on this channel | position:settled (outcome, tx_signature) | position.settled — the terminal outcome event; parlay.status_changed’s status values stop at executed/cancelled and never include "settled" | RFQ status: "settled" |
| Bought back early (cashout) | not on this stream — see the separate cashout-request stream, whose own terminal status event reports "committed" | cashout_request announces only the opening of the buyback request to the backing MM; there’s no separate SSE event for its completion | not delivered on this channel — see the gap below | position.bought_back (amount, mm_pays_user, tx_signature) | RFQ status: "bought_back", cashout object populated |
| Cancelled | cancelled — quote request abandoned precommit via Cancel | quote_request_expired (reason: "cancelled") | position:cancelled — only for a postcommit cancellation (e.g. the maker missed the confirmation deadline); a precommit quote request cancellation never reaches a position, so nothing fires here | not delivered precommit; postcommit fires both parlay.status_changed (status: "cancelled") and position.cancelled together (dedupe on X-Totalis-Event-Id) | quote request status: "cancelled" precommit, or RFQ status: "cancelled" postcommit |
| Expired | expired — quote request’s TTL lapsed with no commit | quote_request_expired (reason: "expired") | quote:expired — a specific MM’s accepted quote lapsed before confirmation; there’s no distinct WebSocket event for a whole RFQ expiring | not delivered on this channel — no webhook event exists for expiry, before or after commit | quote request status: "expired" precommit, or RFQ status: "expired" postcommit |
The WebSocket buyback gap
Same value, different name
The same economic figures go by different names at the quote stage versus the settlement stage:| Quote stage name | Settlement stage name | Same value because |
|---|---|---|
user_cost (Quote object — quote-request stream, GET /v1/quote-requests/{id}) | user_stake (Settlement object on GET /v1/rfqs/{id}; also the name used from the start in MM-stream and cashout payloads) | Both are the user’s net stake (bet_amount minus the taker fee). The API renames it once the trade moves past pricing. |
mm_cost (Quote object, quote stage) | mm_risk (Settlement object; also used in MM stream cashout_request payloads) | Both are the market maker’s amount at risk (total_payout/payout minus the user’s stake). |
total_payout (Quote object, quote stage) | payout (Settlement object, settlement stage) | Both are the full amount paid to the winner on a win (user stake + mm risk). |
Market maker identity across channels
mm_id, market_maker_id, and target_mm_id all identify the same thing — the market maker quoting or backing a trade. Which spelling you see depends on the channel:
| Field | Where it appears |
|---|---|
mm_id | The MM SSE stream’s connected event; the mm:quotes:{mm_id} WebSocket channel name itself |
market_maker_id | Quote objects — best_quote.market_maker_id on both the quote-request stream and cashout stream, the Get quote request response, and the mm_quote:accepted WebSocket payload |
target_mm_id | The cashout request object — the single MM a given cashout request is offered to |
The noun stack
The same trade is called something different at each stage of its life. How parlays work introduces this progression; here it’s gathered in one place:| Stage | Name | Notes |
|---|---|---|
| Precommit, ephemeral | quote request | Lives only in the Quote Service. Has its own id and status (active → committed/cancelled/expired). Ceases to exist once it commits or dies — it never becomes a queryable historical record. |
| Postcommit, the API object | RFQ | Created at commit, with its own rfq_id and status progression (open → quoted → accepted → confirmed → executed → settled/bought_back, or cancelled/expired). This is what GET /v1/rfqs/{id} returns. |
| Product facing name | parlay | The same object as the RFQ — “parlay” is the name you’d use in a UI or in conversation. The docs use “parlay (RFQ)” interchangeably for this reason. |
| Once funded on chain | position | The RFQ’s on chain footprint once status >= confirmed. Carries position_id (the 16 byte on chain id, used specifically to open a cashout request) and position_pda (the on chain account address, surfaced on WebSocket position:* events). Look a position up by rfq_id/position_id via the API — the PDA is an on chain address, not a lookup key. |
