# Async Task Model — Document & Batch Processing States

> How document and batch processing works asynchronously in the v1 API — the queued/processing/succeeded/failed/canceled state machine and the created_at/started_at/completed_at timestamp chain.

Uploading a document doesn't process it, and starting processing doesn't finish it in the same request. Every document and batch moves through a fixed, closed set of statuses — this page covers that lifecycle end to end.

## Status lifecycle

Every document (and, in aggregate, every batch) is always in exactly one of five states:

```text
queued → processing → succeeded | failed | canceled
```

- `queued` — uploaded and/or waiting for a worker to pick it up.
- `processing` — a worker has claimed the document and is actively extracting data from it.
- `succeeded` — extraction finished; `line_items` in the results response is populated.
- `failed` — extraction could not complete for this document (this is a *document-level* outcome, not an API-call error — see the note on `processing_error` in [Error Handling](/developers/guides/errors)).
- `canceled` — the document was removed from the queue before processing started (for example, its batch was deleted while it was still `queued`).

This five-value set is a public contract, deliberately decoupled from whatever internal states the system uses behind the scenes. New internal intermediate states can be introduced later without ever adding a sixth value here — every v1 response you parse today will keep working.

## The timestamp chain

Both document (`GET /documents/{id}`) and batch (`GET /batches/{batch_name}/results`) responses carry the same three fields, so you can tell "queued but stuck" apart from "actively running" apart from "genuinely done":

| Field | Set when |
| --- | --- |
| created_at | The document was uploaded (or, for a batch, the earliest upload in it). |
| started_at | A worker claimed the document and began processing it. Stays null while status is still queued . |
| completed_at | The document reached a terminal state — succeeded , failed , or canceled . Stays null while still queued or processing . At the batch level, stays null until every document in the batch has one — a batch with anything still in flight isn't "completed" yet, even if some of its documents already finished. |

## Polling vs. webhooks

You have two ways to find out when a batch is done:

- **Polling** — call `GET /batches/{batch_name}` periodically and check `all_done`. Simple, no infrastructure required on your side, but wastes requests if you poll too aggressively (see [Rate Limits](/developers/guides/rate-limits) for the 120/minute ceiling on status endpoints) or too slowly (added latency before you notice completion).
- **Webhooks** — register a callback URL once with `PUT /batches/{batch_name}/webhook` and get notified the moment the batch finishes, instead of asking repeatedly. See the [Webhooks](/developers/guides/webhooks) guide — it also covers what happens if you add more documents to a batch and process it again after it already notified you once (short version: you get notified again automatically, no extra registration needed).

For a single small batch processed once, polling a few times is simplest. For anything higher-volume, or where you don't want a request loop sitting around waiting, webhooks are the better fit.

---

Source: https://imagetotable.ai/developers/guides/async-model
