> For the complete documentation index, see [llms.txt](https://docs.uptiq.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.uptiq.ai/document-ai/developer-documentation/getting-started/errors.md).

# Errors

The API uses conventional HTTP status codes, with two exceptions that matter enough to lead with: **some endpoints return `500` for input you got wrong**, and `GET /generate/{_id}` returns `500` for an ID that simply does not exist. Neither is a server fault you should retry.

### Status codes

| Code  | Meaning                                                                                 | Retry?                  |
| ----- | --------------------------------------------------------------------------------------- | ----------------------- |
| `200` | Success. For async submissions this means *accepted*, not *finished*                    | —                       |
| `201` | Created — returned by `POST /generate`                                                  | —                       |
| `202` | Accepted — returned by `POST /extract` for a multi-extraction (`documentTypes`) request | —                       |
| `400` | The request body is missing or invalid                                                  | No — fix the request    |
| `401` | Missing, invalid, expired or revoked API key                                            | No — fix the credential |
| `404` | No record with that ID                                                                  | No                      |
| `500` | Usually a genuine server error — but see below                                          | Sometimes               |

{% hint style="info" %}
Success codes vary with the request, not only with the endpoint — `POST /extract` answers `200` for a single document and `202` for a multi-extraction. Accept any `2xx` rather than testing for one value.
{% endhint %}

### The `500` responses that are not server errors

Four endpoints return `500` where `400` belongs, when the request body is missing or malformed:

* `POST /extract`
* `POST /extract/sync`
* `POST /classify`
* `POST /classify/sync`

Their bulk counterparts (`/extract/bulk`, `/classify/bulk`) and both generation endpoints correctly return `400` for the same mistake.

### `GET /generate/{_id}` returns `500` for every input

Not only for unknown IDs. Tested with the exact `generationId` returned by `POST /generate`, a hyphen-stripped form, a prefixed form and a nil UUID — all four return:

```json
[{"input": {"message": "Invalid generation ID format", "status": "error"},
  "loc": [], "msg": "Input should be a valid array", "type": "list_type",
  "url": "https://errors.pydantic.dev/2.11/v/list_type"}]
```

There is no ID format that succeeds, so the endpoint is currently unusable. The comparable `GET /document-extractions/{id}` and `GET /classifications/{id}` both work and return a correct `404` for an unknown ID.

To collect a generated document, use `POST /generate/sync` — which returns the file directly — or set `webhookUrl` on the async request and take `downloadUrl` from the completion payload. See [Generate a document](/document-ai/cookbooks/generate-a-document.md).

{% hint style="danger" %}
The practical consequence: **you cannot treat `5xx` as "transient, safe to retry" on this API.** A retry loop that assumes otherwise will hammer the endpoint with a request that can never succeed. Validate your payload locally, and on a `500` inspect the body before deciding to retry.
{% endhint %}

These are known defects and have been raised. They are documented here because they are the current behaviour, not because they are intended.

### Error bodies

Authentication failures return a flat object:

```json
{
  "message": "Authentication required",
  "status": "error",
  "timestamp": "2026-07-29T03:00:11.034942+00:00"
}
```

Validation failures return a **JSON array** of Pydantic validation errors, not an object:

```json
[
  {
    "type": "list_type",
    "loc": [],
    "msg": "Input should be a valid array",
    "input": { "message": "Request body is required", "status": "error" },
    "url": "https://errors.pydantic.dev/2.11/v/list_type"
  }
]
```

{% hint style="warning" %}
Parse defensively. A handler that assumes every error body is an object with a `message` key will throw on validation errors, which are an array. The two shapes are genuinely different.
{% endhint %}

The most useful fields are `loc` — the path to the offending property — and `msg`.

### A job that fails is not a request that failed

Asynchronous work has two independent outcomes, and conflating them is a common mistake.

|                                 |                                             |
| ------------------------------- | ------------------------------------------- |
| **HTTP status**                 | Whether the API accepted your call          |
| **`documentStatus` / `status`** | Whether the document processed successfully |

`POST /extract` returning `200` means the job is queued. The document can still end at `Failed`. Always check the job status when you retrieve the record — see [Conventions](/document-ai/developer-documentation/getting-started/conventions.md) for the lifecycle and the envelope trap that goes with it.

### Retry guidance

| Situation                                              | Do                                                                                                 |
| ------------------------------------------------------ | -------------------------------------------------------------------------------------------------- |
| `401`                                                  | Stop. Check the key's expiry and revocation state                                                  |
| `400`, or a `500` from one of the four endpoints above | Stop. Fix the payload                                                                              |
| `404`                                                  | Stop, unless you are racing a just-submitted job                                                   |
| `500` elsewhere, or a network timeout                  | Retry with exponential backoff, and cap the attempts                                               |
| Job status `Failed`                                    | Do not resubmit blindly — inspect the record first; a document that cannot be read will fail again |

For webhook delivery retries, which follow different rules, see [Webhook payloads](/document-ai/developer-documentation/getting-started/webhook-payloads.md).

### What the spec says about errors

The OpenAPI document describes only `200`/`201` and `400`. It documents no `401` — despite that being reachable on thirteen of fourteen operations — no `404`, and no `5xx`. `POST /generate/sync` has no documented responses at all.

The table at the top of this page comes from testing every operation against the live API, not from the spec.

### Related pages

* [Authentication](/document-ai/developer-documentation/getting-started/authentication.md) — the cause of most `401`s.
* [Conventions](/document-ai/developer-documentation/getting-started/conventions.md) — envelopes, the status lifecycle, and the unenforced request rules that cause `400`s.
* [Webhook payloads](/document-ai/developer-documentation/getting-started/webhook-payloads.md) — delivery retries and their own backoff schedule.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.uptiq.ai/document-ai/developer-documentation/getting-started/errors.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
