# Error Codes

Every non-2xx response from the Bead API returns a standard JSON error object. Use the HTTP status code for high-level handling (retry vs. fix request) and the `error` string for granular logic.

## Error Object Shape

```json
{
  "code": 400,
  "error": "invalid_request",
  "message": "Missing required field address",
  "details": [
    { "field": "address", "issue": "required" }
  ]
}
```

| Field     | Type    | Description                                                   |
| --------- | ------- | ------------------------------------------------------------- |
| `code`    | integer | Mirrors the HTTP status                                       |
| `error`   | string  | Machine-readable error key                                    |
| `message` | string  | Human-readable explanation                                    |
| `details` | array   | Optional list of field issues; present on validation failures |

## Standard Error Catalogue

| HTTP | `error` key            | When it happens                                       | Retry?                       |
| ---- | ---------------------- | ----------------------------------------------------- | ---------------------------- |
| 400  | `invalid_request`      | Malformed JSON or missing top-level fields            | ✘                            |
| 400  | `validation_error`     | Field-level problems (see `details`)                  | ✘                            |
| 401  | `invalid_client`       | Bad `client_id` / `client_secret`                     | ✘                            |
| 401  | `invalid_grant`        | Wrong username or password                            | ✘                            |
| 401  | `unauthorized`         | Token missing or expired                              | Obtain new token             |
| 403  | `insufficient_scope`   | Token lacks required scope                            | ✘                            |
| 403  | `forbidden`            | Auth OK but caller not allowed on this resource       | ✘                            |
| 404  | `not_found`            | Resource ID doesn’t exist or not visible              | ✘                            |
| 409  | `conflict`             | Duplicate operation (e.g., same name)                 | ✘                            |
| 422  | `unprocessable_entity` | Business rule violated (e.g., delete in-use location) | ✘                            |
| 429  | `rate_limit_exceeded`  | Quota exhausted; see `X-RateLimit-Reset`              | ✔︎ after back-off            |
| 500  | `internal_error`       | Unexpected server fault                               | ✔︎ with exponential back-off |
| 503  | `service_unavailable`  | Maintenance or downstream outage                      | ✔︎ after `Retry-After`       |

## Validation Error `details`

Each entry in `details` has:

```json
{ "field": "tenderTypes[0]", "issue": "unsupported_value" }
```

| Issue                    | Meaning                           |
| ------------------------ | --------------------------------- |
| `required`               | Field missing                     |
| `invalid_format`         | Wrong data type or regex mismatch |
| `unsupported_value`      | Value outside allowed enum        |
| `too_long` / `too_short` | String length limits              |
| `out_of_range`           | Numeric bounds violated           |

### Retry guidance quick-chart

| Category                | Example                      | Client action                                         |
| ----------------------- | ---------------------------- | ----------------------------------------------------- |
| **Caller fix**          | 400, 401, 403, 404, 409, 422 | Correct request before retrying                       |
| **Back-off then retry** | 429, 500, 503                | Exponential back-off; obey `Retry-After` when present |

Integrations should log both the HTTP status and `error` key to ease troubleshooting and monitoring.
