Document workflows
Document workflows run a model over an already-parsed file. Every one of them
needs the extract.run permission, viewer access to the file, and a parsed
file — an unparsed file returns 409 file_must_be_parsed.
Before you start. You need an API key from the quickstart
and a document that has already been parsed. Failures carry a
stable code; the full list is on the error codes page.
They do not all answer the same way. Some queue and return an operation id to
poll; some run the model inside the request and return the result. Get this
wrong and you will poll an endpoint that already gave you the answer, or read
output from a 202 and find null.
Which are queued, which are not
| Endpoint | Shape | Response |
|---|---|---|
POST /v1/split/rules/suggest | queued | 202 { operation_id, status } |
POST /v1/split/runs | queued | 202 { operation_id, status } |
POST /v1/extract/runs | queued | 202 { operation_id, status } |
POST /v1/extract/schemas/generate | queued | 202 { operation_id, status } |
POST /v1/classify/classes/suggest | synchronous | 200 with the classes |
POST /v1/classify/runs | synchronous | 200 with the classifications |
POST /v1/edit/runs | synchronous | 200 with the proposal |
POST /v1/split/download | synchronous | 200 PDF bytes |
The synchronous ones hold the connection open for the whole model call. That call is slow — minutes on a large document — so set a generous client timeout and expect any proxy in front of the API to be the binding constraint. The split walkthrough is on split and the classify walkthrough on classify; this page owns the operation model they share.
The two extract rows are on this page for their operation shape only. What a
schema looks like and what the run returns is on extraction.
The operation model
A queued workflow writes one operation row. GET /v1/operations/{operationId}
returns it in a fixed
shape:
Code
status is one of queued, in_progress, completed, failed,
cancelled.
operation_type is one of split_run, extract_run, classify_run,
edit_run, split_rules_suggest, schema_generate — the set the database
CHECK constraint enumerates.
output is null until status is completed. It is not an empty result
and it is not partial. A client that reads output on its first poll gets
null; keep polling. error carries the failure message on failed and
cancelled, and is null otherwise.
progress is advisory. done and total are both 0 before the run reports
anything, and phase names the stage: a split run reports windows then
repair, a rules suggestion reports rules.
An unknown id, another organisation's id, and an operation whose file was
permanently deleted are all 404 operation_not_found — they are deliberately
indistinguishable.
Polling
Code
Cancelling
Code
POST /v1/operations/{operationId}/cancel
is cooperative. The request records the intent and the run stops
at its next checkpoint. A split checks between windows and again inside the
repair pass, so a cancel does not leave repair model calls running. The
response has the same shape as the GET, and output is still null unless the
run had already completed.
Cancelling an operation that is already terminal changes nothing and returns
its current state. Reading an operation needs only viewer file access;
cancelling one needs extract.run as well, because it moves credits.
Edit
POST /v1/edit/runs is synchronous and proposes only. It never
mutates the source file or its artifacts.
Code
Returns { operation_id, proposal_id, proposal, modelId, credit }. The
proposal is the model's own JSON; its diff (or patch) is stored
alongside it. The proposal is saved with status proposed and proposal_id
identifies it. Applying it is your side of the contract.
Model controls
Every workflow body accepts the same three optional fields.
| Field | Meaning |
|---|---|
model_id | Model to run. Omit for the deployment default. |
reasoning_level | none, low, medium or high. Defaults to medium. |
idempotency_key | Request identity. Derived from the inputs when omitted. |
An unavailable or unknown model is 400 unsupported_model. Which models a
deployment accepts is deployment configuration, not a fixed list.
Lower reasoning_level is faster. At the default medium, one split window
costs roughly 60 to 90 seconds, and a split runs several windows, so a large
document takes minutes.
Credits
Each of these debits exactly one credit per charged attempt: split rules
suggest, split run, classify classes suggest, classify run, edit run, extract
run, schema generate. POST /v1/split/download debits nothing.
The debit happens before any work starts. A 402 insufficient_credits or
402 monthly_quota_exceeded therefore leaves nothing queued and nothing
running.
Queued and synchronous workflows then diverge, and this is the part worth reading twice.
Queued workflows refund. When a queued operation ends failed or
cancelled, the credit for that attempt is refunded in the same transaction
that writes the terminal status. Retrying is real work and is charged again;
each attempt appears in the ledger under its own key, …:attempt-1,
…:attempt-2, and so on. A failed split therefore costs nothing and does not
block a retry.
Synchronous workflows do not refund. Classify and edit debit once against
the request's idempotency key. If the model call then fails, the operation is
marked failed and the credit stays spent. Retrying with the same
idempotency key is not charged a second time — the ledger entry already exists,
and the response reports credit.debited: false — but it does re-run the
model. Retrying with a fresh idempotency_key is a new charge.
A completed synchronous run replays for free: the same idempotency key returns the stored output without touching the model.
Where to next
- Split — suggest rules, run the split, download each record.
- Classify — suggest classes and label a document and its pages.
- Extraction — the schema, the output shape and the citations
behind
POST /v1/extract/runs. - API reference — every request body and response field for the endpoints on this page.

