The document and its ground truth
invoice_no
total
→
The frozen headline
accuracy
matched47
mismatched3
ungraded1
run_number3
NodesCredits35 per pageYou get
The loop is four calls:
- Upload files and ground truth into the workflow’s dataset with
POST /v3/workflows/{workflow_id}/dataset/upload/. - Launch the eval with
POST /v3/workflows/{workflow_id}/evals/, and keep the returnedeval_id. - Poll the eval until its
statusleavesin_progress, withGET /v3/workflows/{workflow_id}/evals/{eval_id}/. - Read the headline (
accuracy,matched,mismatched,ungraded), or list prior runs to compare over time.
When to use this
- Regression gate in CI. Run an eval on every workflow change and fail the build if accuracy drops below a threshold.
- Nightly accuracy tracking. Launch a run on a cron and chart
accuracyperrun_numberover time. - Version comparison. Evaluate two versions on the same dataset and diff their headlines.
ANYFORMAT_API_KEY and a WORKFLOW_ID (the target workflow’s UUID):
Step 1: Upload files and ground truth
Seed the dataset one document at a time. Each call uploads 1–100 files as a single document packet plus an optionalground_truth, the expected values for that document. Ground-truth keys are the workflow schema’s field persistent_ids; a scalar field maps to string | null, a table/object field to an array of row objects. Creation is all-or-nothing, so a rejected file or a bad ground-truth payload stores nothing.
Ground truth attaches to the workflow’s current version. Fields the dataset has no ground truth for aren’t scored: they land as
ungraded, never a failure. See Upload to Dataset for conflict handling (on_conflict) and multi-file packets.Step 2: Launch the eval
Launching is async and fire-and-forget: it enqueues a fresh extraction for every dataset document, pins the realized cohort into a new eval, and returns202 immediately with an eval_id and run_number. Grading finalizes on a worker, and you poll for it in the next step. Omit version_id to evaluate the current version; pass one to pin a specific version.
The public API always evaluates the whole dataset. There is no way to narrow the scope from the endpoint. The app can additionally scope a run to a single sub-dataset, but that narrowing isn’t exposed on the API yet, so an API-launched eval always covers every dataset document.
enqueued_count documents were queued, failed_count failed to enqueue. If the dataset is empty (or every document fails to enqueue) the call returns 422 EMPTY_EVAL and no eval is created, so upload at least one document in Step 1 first.
Step 3: Poll until grading finishes
GET .../evals/{eval_id}/ always returns 200 while the eval exists. There are no precondition errors while grading is in flight. Poll it until status is terminal:
Step 4: Read the headline
On aprocessed eval, accuracy is the matched / (matched + mismatched) fraction. It stays null when the graded denominator is 0, which means nothing was gradable: every field landed ungraded for lack of ground truth. Treat a null accuracy as “no signal”, not as “zero”.
Response (200 OK, processed)
End-to-end
The whole loop as one runnable script: upload a folder of documents with their ground truth, launch, poll, and print the headline.When it goes wrong
A retried job bills the whole dataset twice. Launching runs a fresh extraction per document, and re-launching runs the whole cohort again. Without anIdempotency-Key every launch creates a new eval, so a retried CI job pays for a second full run. Supply the header and a retry with the same key returns the original eval. See Idempotency and How credits work.
A replayed key returns 422 after you publish a new version. When you omit version_id, the replay resolves against whatever version is current, and a publish makes that a different target. Pass an explicit version_id when the replay has to survive a version change.
accuracy is null on a processed eval. Accuracy is the matched / (matched + mismatched) fraction, and it stays null when the graded denominator is 0. That happens when every field landed ungraded for lack of ground truth. Treat null as “no signal” rather than zero, and fail the CI gate on it instead of passing.
Your fields score ungraded although you uploaded ground truth. Ground-truth keys are the schema’s field persistent_ids, not the display names, and ground truth attaches to the workflow’s current version. A key that matches no field is never scored, and it never fails either. Check the persistent_ids before you blame the model.
The launch returns 422 EMPTY_EVAL. A launch with nothing to enqueue creates no eval at all. Upload at least one document through Step 1 first. Note also that each run is immutable: editing ground truth or refining the workflow never moves a past eval, so launch a fresh run to measure a change. That is what makes the delta between two runs real signal.
Next steps
Launch Eval
Full launch contract: target version, idempotency, error codes.
Get Eval
The status model and frozen-headline response in detail.
List Evals
Keyset pagination over a workflow’s run history.
Evaluations in the app
Drive the same loop from Health → Evaluations, with per-file and per-field drill-down.

