# Errors (/standards/errors)



Every non-`2xx` response from protected `/v1/*` endpoints returns a flat JSON body.

## Response Schema [#response-schema]

| Field        | Type            | Required | Description                                                                                                                                                                                    |
| ------------ | --------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `code`       | `string`        | Yes      | Stable, documented, `SCREAMING_SNAKE_CASE` machine-readable error code. Branch on this field, not on `message`.                                                                                |
| `message`    | `string`        | Yes      | Human-readable explanation, safe to display to end users. Messages may be reworded between releases; do not pattern-match on them.                                                             |
| `status`     | `integer`       | Yes      | Mirrors the HTTP status code.                                                                                                                                                                  |
| `request_id` | `string (ULID)` | Yes      | Unique request identifier. Also returned in the `X-Request-Id` response header. If you send `X-Request-Id` on the request, we echo it back; otherwise we generate one.                         |
| `docs_url`   | `string`        | No       | Deep link to the relevant error code in the public docs.                                                                                                                                       |
| `details`    | `array<object>` | No       | Present for `422` validation errors and any response with multiple sub-issues. Each element has `{ "field": string, "issue": string }`. This field is empty or absent for single-issue errors. |

Endpoint-specific error codes are documented alongside each operation in the API reference. This page describes the shared error format and the HTTP status codes used across the API.

## Examples [#examples]

Simple `400` response:

```json
{
  "code": "INVALID_REQUEST",
  "message": "The request could not be processed.",
  "status": 400,
  "request_id": "01HXJ8K2M5N6P7Q8R9S0T1V2W3",
  "docs_url": "https://docs.vizit.com/api/errors/INVALID_REQUEST",
  "details": []
}
```

Validation `422` response:

```json
{
  "code": "VALIDATION_ERROR",
  "message": "The request body failed validation.",
  "status": 422,
  "request_id": "01HXJ8K2M5N6P7Q8R9S0T1V2W3",
  "docs_url": "https://docs.vizit.com/api/errors/VALIDATION_ERROR",
  "details": [
    {
      "field": "image_url",
      "issue": "must be an HTTP or HTTPS URL"
    },
    {
      "field": "product_category_id",
      "issue": "must be a valid UUID"
    }
  ]
}
```

## Headers [#headers]

| Header         | When present              | Description                                                                            |
| -------------- | ------------------------- | -------------------------------------------------------------------------------------- |
| `X-Request-Id` | Always                    | Request identifier for this response. If you send one on the request, we echo it back. |
| `Retry-After`  | `429` and sometimes `503` | Number of seconds to wait before retrying.                                             |

## HTTP Status Codes [#http-status-codes]

The API uses these response status codes:

| Status Code | Meaning               | Description                                                             |
| ----------- | --------------------- | ----------------------------------------------------------------------- |
| `200`       | OK                    | The request succeeded.                                                  |
| `202`       | Accepted              | The request was accepted for asynchronous processing.                   |
| `400`       | Bad Request           | The request was malformed or failed a request-level rule.               |
| `401`       | Unauthorized          | The request did not include valid authentication credentials.           |
| `403`       | Forbidden             | The caller is authenticated but not permitted to perform the operation. |
| `404`       | Not Found             | The resource does not exist or is not visible to this caller.           |
| `409`       | Conflict              | The request conflicts with the current state of the resource.           |
| `422`       | Unprocessable Entity  | The request failed schema or field-level validation.                    |
| `429`       | Too Many Requests     | The caller exceeded the rate limit.                                     |
| `500`       | Internal Server Error | An unexpected server-side error occurred.                               |
| `502`       | Bad Gateway           | A dependency returned an invalid response.                              |
| `503`       | Service Unavailable   | The API is temporarily unavailable.                                     |
| `504`       | Gateway Timeout       | A dependency did not respond in time.                                   |

## Client Guidance [#client-guidance]

* Branch on `code`, never on `message`.
* Retry `429` and `5xx` responses with exponential backoff. Do not retry other `4xx` responses until you have fixed the request.
* When contacting support, include `request_id` and the UTC time of the request.
* Error codes are part of the `/v1/*` contract. New codes may be added over time, and operation-specific codes are documented in the API reference.
