Units. Every
*_micro field is an integer of micro-USDC (1_000_000 = $1.00). The money
fields beside them — user_stake, mm_risk, total_payout, and the amount on the settlement
event — are decimal USDC. (payout_odds is a multiplier, not an amount.) Both conventions appear in
the same payload, so scale before you compare.How it works
1
Receive the auction
A
cashout_request event arrives on
GET /v1/mm/quote-requests/stream. It carries the
position’s legs and economics. Auctions are broadcast, so you see exits on positions you have
no relationship with.2
Price the exit
Re-value the parlay at current odds, and read
legs[].resolution carefully. void beats
everything: any void leg cancels the whole position at settlement and refunds the stake, so
such a position is worth about the stake even if a sibling leg already resolved lost. Absent
a void leg, one lost leg makes the parlay worthless. Nothing stops an auction opening on a
position whose legs are already decided, so resolution is your only defence. Then read
Your economics — what a win costs you depends on whether you are the
position’s current counterparty.3
Bid one all-in price
PUT your bid to
/v1/mm/cashout-requests/{id}/quote before
expires_at. price_micro is what the user receives, all-in. Highest live bid wins; ties
go to the earliest bid.4
Reconcile the outcome
If you win as the counterparty you receive
position:bought_back. If you win as anyone else
you acquire the position and, today, receive no event — see
Knowing you won.The auction
price_micro is a single all-in figure: no direction field, no separate fee term, no gross-vs-net.
What you send is what the user sees on screen and what they walk away with. It can be below their
stake — a losing parlay exits for less than it cost, and the user never pays more than their
stake to leave.
A bid may outlive the auction. Nothing tells you your bid lost, lapsed, or that the auction closed,
so age bids out locally against expires_at. To pull a live bid before it lapses,
DELETE /v1/mm/cashout-requests/{id}/quote: 204 when it was removed, 200 with
{"status": "already_withdrawn"} if it had already gone (idempotent, not an error), 409 once the
auction is no longer active.
Your economics
You bid the same way on every auction, but a win lands differently depending on whether you already back that position — which you can tell from your own records. LetS be user_stake and P your
price_micro (both micro-USDC), and f = fee_bps/10_000.
You are the position’s current counterparty
Your win closes the position: your exposure unwinds and your lockedmm_risk is released. A
closing exit carries the profit fee, and it always comes out of your side — the user receives
exactly P either way. The fee goes to Totalis, never to another market maker.
When P > S (the user is ahead, you pay out) your outflow is A ≈ (P - S) / (1 - f), of which
floor(A * f) goes to the Totalis fee vault and the remainder reaches the user. At fee_bps = 100,
S = 990_000, P = 1_990_000: A = 1_010_101, the fee is 10_101, and the user receives exactly
1_990_000.
Note which number you are bidding. P is the user’s proceeds, not your cash outflow — budget
(P - S) / (1 - f), and choose the P whose grossed-up cost you are willing to bear. Bidding the
number you want to pay overpays by the fee.
When P <= S (the user is down — the common case) the direction flips. Nothing is paid out and
you collect (S - P) * (1 - f). The fee is still taken, out of your proceeds rather than on top
of a payment. Do not apply the formula above here; it returns a negative number.
Your bid must also fit inside your locked collateral. The server derives that ceiling from
total_payout - user_stake, not from the mm_risk field. The two are stored separately and do
drift: on a sample of about 29,000 positions they disagreed on 0.16% of them, by up to 0.0001 USDC
(100 micro-USDC) — enough to have a boundary bid rejected if you size against the wrong one. Both
are decimal USDC, so scale first:
total_payout. Exceed it and the bid is rejected with
price_above_buyback_bound. In the P <= S direction the ceiling is just P >= 0, so it never
binds — it only ever constrains a payout.
You are not the counterparty
Your win acquires the position: you step into the bettor’s side, not the house’s. You pay exactlyP to the seller, inherit the staked side, and the position stays open and settles later
against its original counterparty, which is untouched. No fee applies — nothing closed.
So this trade is long the parlay, not a hedge. You are out P now, you collect total_payout if
every remaining leg wins, and you lose P if it does not: max loss P, max gain
total_payout - P. Size it as opening a new parlay position of your own.
- You need
Pfree, notPplus the stake. The position’s collateral travels with it and funds its own lock, so the net effect isfree -= P. - Funds are checked when the user commits, not when you bid. Nothing gates your bid on
collateral, so a winning bid can fail at commit with
insufficient_buyer_balanceorbuyer_no_vault. Keep free balance ahead of your outstanding bids. - A position can be acquired only once. Once it has been transferred, a further transfer is
refused (
already_transferred), so plan to hold what you acquire to settlement rather than assuming you can flip it through another auction.
fee_bps rather than hardcoding a guess.
Knowing you won
There is no per-auction result event on the MM stream.
A bought-back parlay reports a realized cash-out P&L rather than a win or loss — the exit is a real
settlement, not a refund.
Errors
Commit-time rejections never reach you as an HTTP response — you learn of them only by not being
filled. The ones worth knowing:
amount_over_bound (the fee-aware ceiling, re-checked when the fee
rate was unavailable at bid time), insufficient_buyer_balance / buyer_no_vault (acquisition, your
funding), and already_transferred (the position had already moved).
Treat 404 and the race-class 409s as ordinary lost auctions and log them quietly. Treat 400,
401 and 403 as your own bug: if every bid fails that way you are silently out of the market.
What changed
To migrate:
- Send one all-in price. Drop
buyback_priceandmm_pays_user. The body is strict, so the old shape is rejected outright. - Stop calling the decline endpoint. It no longer exists.
- Drop the takeover path.
takeover-requestsis gone; one endpoint serves both. - Price inside 10 seconds, down from about 90.
Checklist
- Handle
cashout_requeston the MM stream, and reconnect withlast_event_id— with a 10 s lifetime, a reconnect gap means lost auctions. - Price from
legs,user_stakeandtotal_payout, respectinglegs[].resolution. These are decimal USDC; your bid is micro-USDC. - Decide from your own book whether you back the position, pick the right regime, and make the number fee-inclusive.
- Bid before
expires_at; expire your own bids locally. - Keep free balance ahead of outstanding bids.
- Alert on 400, 401 and 403. Log 404 and race-class 409s quietly.
- Reconcile wins from
position:bought_backor, for an acquisition, your position reads.
