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

# Errors

> Every error the Totalis API returns: the envelope, HTTP statuses, top-level codes, per-surface rejection reasons, and which ones to retry.

## Envelope

Every error, on every endpoint, has the same shape:

```json theme={null}
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Human-readable description.",
    "details": {}
  }
}
```

| Field           | Use                                                          |
| --------------- | ------------------------------------------------------------ |
| `error.code`    | Stable. Safe to branch on.                                   |
| `error.message` | For humans. Can change without notice, so never match on it. |
| `error.details` | Surface-specific context, most often `details.reason`.       |

## HTTP statuses

| Status | Meaning                                                                    | Your move                                                                                                      |
| ------ | -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `400`  | The request is malformed or a value is out of range.                       | Fix the request. Retrying will not help.                                                                       |
| `401`  | The credential is missing, invalid, revoked, or expired.                   | Check the key. Do not retry with the same one.                                                                 |
| `403`  | The credential is valid but not permitted to do this.                      | Check scopes, or the key format.                                                                               |
| `404`  | The object does not exist, is not yours, or has already been evicted.      | Treat as normal on short-lived objects.                                                                        |
| `409`  | A state conflict. Something changed underneath you.                        | Re-read state and decide. Often not retryable.                                                                 |
| `410`  | The object expired and is gone, such as a cashout auction past its window. | Start a new one.                                                                                               |
| `413`  | Payload too large.                                                         | Reduce the request body.                                                                                       |
| `429`  | Rate limited.                                                              | Wait the seconds in the `Retry-After` header. Main API routes also put them in `error.retry_after`.            |
| `500`  | Something failed on our side.                                              | Retry with backoff, unless the endpoint documents an indeterminate outcome. Then check state before you retry. |
| `502`  | Partial success, such as `PARTIAL_VAULT_ONLY` on a withdrawal.             | Read `error.details` before retrying. See [Funding](/guides/funding#when-it-fails).                            |
| `503`  | Temporarily unavailable.                                                   | Retry with backoff.                                                                                            |

A parlay id you do not own returns `404`, not `403`, so the API never confirms that another
account's parlay exists. A quote request id you do not own returns `403`.

## Top level codes

| `error.code`          | Status |
| --------------------- | ------ |
| `VALIDATION_ERROR`    | 400    |
| `UNAUTHORIZED`        | 401    |
| `FORBIDDEN`           | 403    |
| `NOT_FOUND`           | 404    |
| `CONFLICT`            | 409    |
| `GONE`                | 410    |
| `PAYLOAD_TOO_LARGE`   | 413    |
| `RATE_LIMITED`        | 429    |
| `INTERNAL_ERROR`      | 500    |
| `PARTIAL_VAULT_ONLY`  | 502    |
| `SERVICE_UNAVAILABLE` | 503    |

## Authentication

| Condition                               | REST  | SSE   | `details.reason`     |
| --------------------------------------- | ----- | ----- | -------------------- |
| Credential does not start with `api_`   | `403` | `403` | `wrong_api_key_type` |
| Well-formed key, but invalid or revoked | `401` | `401` |                      |
| Valid key, missing the required scope   | `403` | `403` | `insufficient_scope` |

The `403` for a malformed key carries a hint about the expected format. Keys match
`api_(live|test)_[A-Za-z0-9_-]{28,36}`. The scope catalog is on
[Authentication](/guides/authentication#scopes).

## Committing a quote

Returned by [`POST /v1/quote-requests/{id}/commit`](/api-reference/quote-service/commit) in
`error.details.reason`. No commit failure moves funds: if the commit did not return success, the
trade did not happen.

| Reason                    | What happened                                                                                   | Your move                                                                                          |
| ------------------------- | ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `QUOTE_CHANGED`           | The request changed since the version you committed against.                                    | Re-read the request and commit against the new version.                                            |
| `QUOTE_EXPIRED`           | Every quote on the request has expired, or the best quote expired between selection and commit. | Wait for a fresh `best_quote` event, then retry.                                                   |
| `MARKET_NOT_LIVE`         | A leg's market expired, halted, or was delisted.                                                | Remove the affected leg. Retrying will not help.                                                   |
| `COMMIT_FAILED`           | The commit could not complete, for example because no eligible maker had enough collateral.     | Not retryable.                                                                                     |
| `COMMIT_FAILED_RETRYABLE` | A temporary failure.                                                                            | Retry after a short delay.                                                                         |
| `mm_confirmation_timeout` | The chosen maker did not confirm in time. Returned as `409`, with the maker in `failed_mm_id`.  | The request stays open. Commit the next best quote, or leave that maker out with `exclude_mm_ids`. |

## Submitting a quote

Returned by [`PUT /v1/mm/quote-requests/{id}/quote`](/api-reference/quote-service-mm/submit-quote).

| Status | `details.reason`               | Meaning                                                                                             |
| ------ | ------------------------------ | --------------------------------------------------------------------------------------------------- |
| 400    | `insufficient_collateral`      | The quote needs more collateral than you have free. Carries `required` and `available`.             |
| 503    | `collateral_check_unavailable` | Your balance could not be verified. Retry.                                                          |
| 503    | `fee_unavailable`              | The taker fee rate could not be read, so the quote was rejected rather than priced at 0 bps. Retry. |
| 409    | `version_mismatch`             | Your `request_version` or `request_hash` is stale. Re-read the latest event and resubmit both.      |

Both `503`s fail closed on purpose, so a quote is never admitted against a balance or fee rate that
could not be read. Priced at an unknown fee, your odds would apply to the gross bet and you would
underwrite more than you modeled.

## Bidding on a cashout auction

Returned by [`PUT /v1/mm/cashout-requests/{id}/quote`](/api-reference/quote-service-mm/cashout-quote).

| Status | `details.reason`            | Meaning                                                                                                                  |
| ------ | --------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| 400    | validation                  | Non-integer or non-positive `price_micro`, `expires_in_ms` out of range, unknown field, or malformed JSON.               |
| 400    | `price_above_max_payout`    | Your bid exceeds `total_payout`. The response carries `max_price_micro`.                                                 |
| 400    | `price_above_buyback_bound` | You back this position and the bid exceeds what your collateral covers after the fee.                                    |
| 403    | `insufficient_scope`        | Your key lacks `mm:quote`.                                                                                               |
| 404    |                             | The auction is unknown, or already evicted at its TTL (time to live). The normal "too slow" outcome.                     |
| 409    | `self_quote`                | You opened this auction, so you are the seller. Bidding on a position you back is allowed: that win closes the position. |
| 409    | `not_active`                | The auction was 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 near its close to exit.                                                                     |
| 409    | `buyback_wallet_missing`    | You back this position but have no wallet on record, so a closing exit cannot be built. See below.                       |

`buyback_wallet_missing` fails only your bids. Others can still win the auction as an acquisition,
so do not blacklist the position.

### Rejections you never see

These cashout rejections happen when the user commits, not when you bid. They never reach you as an
HTTP response. You learn of them by not being filled.

| Reason                       | Meaning                                                                                                    |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `amount_over_bound`          | The fee-aware ceiling, re-checked at commit when the fee rate was unavailable at bid time.                 |
| `insufficient_buyer_balance` | You would acquire the position but your free balance does not cover it.                                    |
| `buyer_no_vault`             | You would acquire the position but have no vault.                                                          |
| `already_transferred`        | The position had already moved.                                                                            |
| `buyer_is_counterparty`      | A safety check: your wallet already backs the position, so the win has to close it rather than acquire it. |

## Triage

| Class                                                | Treatment                                                                                                      |
| ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `400`, `401`, `403`                                  | **Alert.** These are your bug. If every request fails this way, you are silently out of the market.            |
| `404` and race-class `409` (`not_active`, `expired`) | **Log quietly.** On short-lived objects these are ordinary lost races, not failures.                           |
| `429`                                                | **Back off** for the seconds in `Retry-After`.                                                                 |
| `500`, `503`                                         | **Retry** with exponential backoff, unless the outcome is documented as indeterminate. Then check state first. |

<Warning>
  Separate `400` / `401` / `403` from `404` / `409` in your alerting from day one. Logged together, a
  losing auction and a broken integration look identical. A market maker whose key silently lost the
  `mm:quote` scope sees nothing but quiet `403`s.
</Warning>
