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

# Errors overview

> HTTP status code conventions + PredictStreet error codes.

All error responses share the same envelope:

```json theme={null}
{
  "code": "insufficient_funds",
  "message": "overdraft on 0xabc.../USDC (lock): Δavail=-100, Δlock=100",
  "details": { ... }
}
```

## HTTP status code conventions

| Status | Meaning                                               | Example                                                                     |
| ------ | ----------------------------------------------------- | --------------------------------------------------------------------------- |
| `200`  | Success — but check `code` field for business rejects | User tried to trade more than their balance                                 |
| `201`  | Resource created                                      | Withdrawal request accepted                                                 |
| `400`  | Client error — malformed request                      | Missing field, invalid address                                              |
| `401`  | Unauthenticated                                       | Missing / malformed / revoked / expired `X-Api-Key` (see `api_key_*` codes) |
| `403`  | Forbidden                                             | Banned wallet or API-key scope missing (`api_key_scope_missing`)            |
| `404`  | Not found                                             | Order doesn't exist                                                         |
| `409`  | Conflict — valid request, wrong state                 | Market not OPEN                                                             |
| `429`  | Rate limited                                          | Exceeded per-wallet quota                                                   |
| `503`  | Service unavailable                                   | Matcher down                                                                |

## Business rejects vs HTTP errors

Two reject-envelope shapes are in use across the partner surface;
which one you get depends on the endpoint family. **Branch on the
path, not on a single shape detector.**

### Order placement — full success-envelope shape with `status: 'REJECTED'`

`POST /api/orders/place` always returns the same JSON shape regardless
of outcome — placement success and business reject share the schema,
distinguished by the top-level `status` field. The HTTP status is
`201 Created` even on reject because the request was accepted and
processed; the body tells you the matcher's verdict.

```json theme={null}
{
  "orderId": "",
  "status": "REJECTED",
  "filledQty": "0",
  "remainingQty": "0",
  "trades": [],
  "code": "insufficient_funds",
  "message": "overdraft ..."
}
```

Branch on `body.status`: `OPEN` / `FILLED` / `PARTIAL` are good,
`REJECTED` carries the failure reason in `code`.

### Vault signature requests — bare `{code, message}` envelope

`POST /api/vault/{split,merge,convert}-signature` (and most other
non-`/orders/place` endpoints when they reject through the
core-api → exchange-service hop) return a **doubly-nested** envelope:
the outer `status` numeric mirrors the HTTP status, and the actual
error code lives under `error.code`:

```http theme={null}
HTTP/1.1 503 Service Unavailable
Content-Type: application/json

{
  "status": 503,
  "error": {
    "code":     "lock_invariant_violation",
    "message":  "vault state diverged from off-chain mirror",
    "trace_id": "06d5d318-0204-47cb-9607-3b37a531e760",
    "details":  { ... optional structured context ... }
  }
}
```

`trace_id` is always present and matches the `X-Trace-Id` header on
the response — quote it when filing tickets. `details` is endpoint-
specific (rate-limit windows, conflicting state, etc.).

### Upstream errors — flattened to `error.code` directly

Errors raised by downstream services (matcher, vault helper,
exchange-service, withdrawal pipeline) reach the partner with the
**original code surfaced at the top of the envelope**:

```json theme={null}
{
  "status": 404,
  "error": {
    "code":     "withdrawal_not_found",
    "message":  "no record for id wd-...",
    "trace_id": "...",
    "details": {
      "upstream": {
        "status": 404,
        "body": {
          "code":    "withdrawal_not_found",
          "message": "no record for id wd-..."
        }
      }
    }
  }
}
```

Affects (non-exhaustive) `POST /api/orders/place` upstream rejects,
`POST /api/me/withdrawals/{id}/cancel` (`invalid_state`,
`not_found`), `POST /api/vault/{split,merge,convert}-signature`
upstream rejects from exchange-service. Branch on `error.code`
directly — the upstream `code` is now lifted to the top level, so the
[`/errors/codes`](/errors/codes) catalogue is canonical regardless of
whether the rejection originated in core-api or in a downstream
service.

<Note>
  `error.details.upstream` still carries the original wrapper
  (`{status, body}`) for diagnostic and logging tooling that needs
  the upstream-side context. New partner code should rely on
  top-level `error.code` exclusively. The `exchange_upstream_error`
  wrapper code is still emitted as a fallback for non-structured
  upstream responses (HTML proxy page, empty body, raw text).
</Note>

### Why two shapes

`POST /api/orders/place` is the only endpoint where the matcher's
"sync" rejection path returns a structured fill record (zero-fills,
zero-trades) — that record is the same shape a successful fill
returns, so the schema unifies success and reject and the HTTP code
stays `201`. Vault signature endpoints are pure pre-flight checks
with no fill record to return; they reuse the platform-wide HTTP
error envelope.

Look at `code` either way to distinguish success from reject — it's
present in both shapes.

<Card title="Error codes" icon="list" href="/errors/codes">
  Complete code reference across all endpoints.
</Card>
