Skip to main content
An eval is one graded run of a workflow against a target version: it re-extracts every document in the dataset, compares each result to that document’s ground truth, and freezes an accuracy headline. This recipe drives that loop over the v3 API, so you can wire it into CI, a nightly job or a regression gate.
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:
  1. Upload files and ground truth into the workflow’s dataset with POST /v3/workflows/{workflow_id}/dataset/upload/.
  2. Launch the eval with POST /v3/workflows/{workflow_id}/evals/, and keep the returned eval_id.
  3. Poll the eval until its status leaves in_progress, with GET /v3/workflows/{workflow_id}/evals/{eval_id}/.
  4. 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 accuracy per run_number over time.
  • Version comparison. Evaluate two versions on the same dataset and diff their headlines.
Throughout, set 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 optional ground_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 returns 202 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.
The response carries counts, never internal ids: 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 a processed 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)
To compare over time, list the workflow’s evals newest-first. Each item is the same headline, so a fresh page can mix graded and still-running runs:

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 an Idempotency-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.