mm:quote scope, USDC to back your
quotes, trading enabled once in the app (see Funding), and a bot on two channels: the SSE quote request stream for pricing, and the WebSocket for
fills.


How it works
1
Stream quote requests
Open
GET /v1/mm/quote-requests/stream. You get a
snapshot of every active request, then updates. Match event names exactly as the
event table lists them. Each leg
carries current_yes_price and current_no_price, the venue prices when the request was
created, as a starting point for your own pricing.2
Submit a quote
Send
payout_odds for the exact version and request_hash you received, with
PUT /v1/mm/quote-requests/{id}/quote.3
Confirm the fill
When a user commits your quote, you get
quote:accepted on the mm:quotes:{mm_id} channel of
the WebSocket, with the quote_id and a confirmation_deadline a few
seconds away. Confirm with POST /v1/mm/quotes/{quoteId}/confirm
before that timestamp. The SSE stream only says a request closed, so keep the socket open. Confirm with the event’s
quote_id, not the draft id your submit returned.4
Reconcile
After a disconnect or a restart, list
GET /v1/mm/quotes?status=accepted
and confirm anything still open.Endpoints
Quoting rules
- One live quote per request version. Submitting again replaces it.
- Send only
request_version,request_hash,payout_oddsand, optionally,expires_in_ms. The server derives every money field from your odds and the net stake. - A new version invalidates your old quote. Re-price and submit again. There is nothing to withdraw.
DELETEa quote only to stand down on the current version before the user commits.- Submit and confirm take no
Idempotency-Key. After a timeout, readGET /v1/mm/quotes?status=acceptedbefore you confirm again.
Your balance
Quotes are backed by wallet USDC plus your vault’s free balance. The submit check (insufficient_collateral) and the confirm check both read that sum.
Your vault is created with your first filled position. Until then keep USDC in the wallet.
POST /v1/vault/deposit returns 404 before the vault exists,
and is optional after.
Live quotes do not reserve collateral. Each quote is checked on its own when you submit it, so if
several fill at once, a confirm can fail for lack of funds. A missed confirmation loses the fill, and
the bettor can leave you out of that request.
Profit and loss
There is no maker P&L endpoint yet. Record
user_stake and mm_risk when you confirm, and settle
them against mm_result on the maker position.settled webhook.
Collateral and the ILP
You do not lock each position’s fullmm_risk on its own. Totalis locks your worst-case loss across
your whole book of open positions. It finds that number exactly by solving an integer linear program
(ILP) over every way your markets can resolve, so positions that cannot all pay out at once share
collateral.


The solver treats these as linked, and everything else as independent:
- The legs of one position. It pays only if every leg hits.
- Markets in one event where only one can resolve yes, such as the winner of a game.
- Kalshi strike ladders on the same underlier or game, such as BTC above 100k and above 90k, or a game’s totals and spreads.
- A team’s total over a line and the game’s total over the same line.
- Markets that have resolved but not yet settled. They are fixed to their result.
mm_risk.
In Totalis backtests the saving was 10% to 30% for generalist books, 20% to 40% for books on a single
league, and over 70% for books of long-tail parlays with four or more legs.
Early cashouts
A user can leave a live position before it settles. Totalis broadcasts a short auction to every maker as acashout_request event on the
stream you already read. Reply with one all-in price_micro, the amount the user receives in
microUSDC, through
PUT /v1/mm/cashout-requests/{id}/quote. The
highest live bid wins, and ties go to the earliest. To pass, submit nothing.
If you back the position, a win closes it, and the profit fee comes out of whichever side profits.
If you do not, a win acquires it: the position moves to your account as the bettor side, and shows
in GET /v1/rfqs. Cashout auctions has the rules, payload and errors.
Your identity
Yourmm_id is your user_id, a Privy DID (did:privy:...). auth:success on the WebSocket and
the connected event on the SSE stream both
return it, and it names your mm:quotes:{mm_id} channel.
Get set up
Get an API key
A key with the four read scopes and
mm:quote.Cashout auctions
Bid on parlay exits and know which outcome you are in.
Errors
Every rejection reason, and which to alert on.
MM API reference
Every maker endpoint and event.

