Guide

Async Task Model

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:

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).
  • 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":

FieldSet when
created_atThe document was uploaded (or, for a batch, the earliest upload in it).
started_atA worker claimed the document and began processing it. Stays null while status is still queued.
completed_atThe 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 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 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.

📮 contact email: [email protected]