> ## Documentation Index
> Fetch the complete documentation index at: https://docs.totalis.trade/llms.txt
> Use this file to discover all available pages before exploring further.

# Early cashout

> Bid on parlay exits: one broadcast auction, one all-in price.

A user can leave a parlay before it settles. When they do, Totalis opens a short auction on that
position and pushes it to **every** market maker on the stream you already consume. You reply with
one number. The highest live bid wins.

<Info>
  **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.
</Info>

## How it works

<Steps>
  <Step title="Receive the auction">
    A [`cashout_request`](/api-reference/quote-service-mm/stream#cashout_request) event arrives on
    [`GET /v1/mm/quote-requests/stream`](/api-reference/quote-service-mm/stream). It carries the
    position's legs and economics. Auctions are broadcast, so you see exits on positions you have
    no relationship with.
  </Step>

  <Step title="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.
  </Step>

  <Step title="Bid one all-in price">
    `PUT` your bid to
    [`/v1/mm/cashout-requests/{id}/quote`](/api-reference/quote-service-mm/cashout-quote) before
    `expires_at`. `price_micro` is what the **user receives**, all-in. Highest live bid wins; ties
    go to the earliest bid.
  </Step>

  <Step title="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](#knowing-you-won).
  </Step>
</Steps>

## The auction

| Property      | Value                                                                             |
| ------------- | --------------------------------------------------------------------------------- |
| Lifetime      | about 10 seconds (`expires_at` on the push)                                       |
| Visibility    | broadcast to every market maker                                                   |
| Your bid TTL  | `expires_in_ms`, 5000 to 60000, default 15000                                     |
| Winner        | highest `price_micro`; ties to the earliest bid (a replacement re-times your bid) |
| Refusing      | submit nothing. There is no decline endpoint                                      |
| Pulling a bid | `DELETE /v1/mm/cashout-requests/{id}/quote`                                       |

`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.

<Warning>
  Your bid being best does not guarantee a fill. The user is only ever filled at a price they
  explicitly accepted, so if the book moves down between their tap and their commit, the commit fails
  and nobody trades.
</Warning>

## Knowing you won

There is no per-auction result event on the MM stream.

| Outcome                           | What you get                                                                                                                                                                                                                                                                                                              |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| You won as the counterparty       | `position:bought_back` on `mm:quotes:{mm_id}`, plus a copy delivered straight to any connection authenticated as you (no subscription needed, so dedupe on `tx_signature`), plus the `position.bought_back` webhook (needs `positions:read`). `amount` is decimal USDC; `mm_pays_user` tells you which direction applied. |
| You won and acquired the position | **Nothing today.** Reconcile from your position reads.                                                                                                                                                                                                                                                                    |
| You lost, or the auction expired  | Nothing. Your bid simply lapses.                                                                                                                                                                                                                                                                                          |

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

| Status | `details.reason`            | Meaning                                                                                                                                                                                        |
| ------ | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400    | validation                  | Non-integer or non-positive `price_micro`, `expires_in_ms` out of range, unknown field, malformed JSON.                                                                                        |
| 400    | `price_above_max_payout`    | Bid exceeds `total_payout`. Response carries `max_price_micro`.                                                                                                                                |
| 400    | `price_above_buyback_bound` | You are the counterparty and the bid exceeds what your collateral covers after the fee.                                                                                                        |
| 401    | —                           | Missing or invalid API key.                                                                                                                                                                    |
| 403    | `insufficient_scope`        | Your key lacks `mm:quote`.                                                                                                                                                                     |
| 404    | —                           | Auction unknown or already evicted at its TTL. The normal "too slow" outcome.                                                                                                                  |
| 409    | `self_quote`                | You opened this auction (you are the seller). Bidding on a position you *back* is allowed — that win closes the position.                                                                      |
| 409    | `not_active`                | Committed or cancelled before your bid landed.                                                                                                                                                 |
| 409    | `expired`                   | The auction expired as your bid landed.                                                                                                                                                        |
| 409    | `MARKET_NEAR_CLOSE`         | The underlying market is too close to close to exit.                                                                                                                                           |
| 409    | `buyback_wallet_missing`    | You back this position but no wallet is on record, so a *closing* exit cannot be built. Only **your** bids fail — others can still win it as an acquisition, so do not blacklist the position. |
| 429    | —                           | Rate limited. Honour `Retry-After`.                                                                                                                                                            |

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 `409`s 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.

## Checklist

1. Handle `cashout_request` on the MM stream, and reconnect with `last_event_id` — with a 10 s
   lifetime, a reconnect gap means lost auctions.
2. Price from `legs`, `user_stake` and `total_payout`, respecting `legs[].resolution`. These are
   decimal USDC; your bid is micro-USDC.
3. Decide from your own book whether you back the position — that determines whether a win closes
   it or acquires it.
4. Bid before `expires_at`; expire your own bids locally.
5. Keep free balance ahead of outstanding bids.
6. Alert on 400, 401 and 403. Log 404 and race-class 409s quietly.
7. Reconcile wins from `position:bought_back` or, for an acquisition, your position reads.
