Skip to main content
This guide walks one real session against the MCP server. An agent takes an invoice PDF that exists only on the local machine, builds a parse → extract → validate workflow, runs the document through it, and reads the structured result. Every request and response below comes from a live production run, with identifiers shortened. The MCP server reference covers connection setup and the full tool table. This page is the flow. The whole session is five tool calls:
  1. stage_files turns a local file into a reference the API accepts.
  2. create_workflow builds the typed graph.
  3. upload_documents puts the file into the workflow.
  4. run_document_packet starts the extraction, retry-safe.
  5. get_run waits for the result and reads it.

1. Stage a local file

An MCP tool call carries JSON, not bytes, so no tool takes a file directly. stage_files closes the gap: it returns an upload form for the bytes, plus a short object_id to refer to them by afterwards.
The response carries one slot per file:
The agent POSTs the bytes itself as multipart/form-data: every upload.fields entry verbatim, then the file.
From here on the file is the pair { "object_id": "06a953d9…", "filename": "single_invoice.pdf" }. Prefer the id over read_url everywhere inside the API. The id is a few characters where the presigned URL is kilobytes, and it does not expire mid-conversation. read_url exists for handing the bytes to something outside anyformat.
Staged bytes are scratch. They belong to no workflow, and storage deletes them within a day. Stage, then consume promptly. A file that must live on goes into a workflow, in step 3.

2. Create the workflow

create_workflow takes the same typed graph as POST /v3/workflows/: exactly one parse node, an extract node with the field schema, and here a validate node with two deterministic rules. A deterministic rule is a check that runs in code, instantly and for free.
The response is the stored workflow, and every field now carries a server-assigned persistent_id:
Keep those ids. When you later edit the workflow, echo each existing field’s persistent_id unchanged, including across renames. That edit loop is get_workflow, change the graph, update_workflow. The id is the field’s identity. It keeps quality metrics, ground truth and analytics attached. A field sent without one is always treated as new, and silently detaches all of that. If the graph is invalid, the error names every broken rule. This is a real rejection of a graph with no parse node:
Every failed tool call has this envelope: branch on error_code, and quote request_id in a support request.

3. Upload the document

upload_documents puts files into the workflow as one document packet. It accepts fetchable HTTPS urls, staged staged_files, or a mix. The staged file from step 1 goes in by id:
The import is atomic: any fetch failure means nothing is persisted. Uploading runs nothing. That separation makes the run step retry-safe.

4. Run it, retry-safe

Every call without a key starts a new billed run. That is the re-run affordance after you edit the workflow. With a key, a retried call replays the original run instead of billing a second extraction. The replay reports the run’s current status. Retry after the run finished and the answer is the same run_id with status: "processed", telling you the result is already there.

5. Read the result

get_run long-polls server-side, so one call waits instead of the agent polling. parse_output: "none" drops the parsed markdown from the response when only the extracted fields matter. On a real document the parse section is about 90% of the payload.
Fifty seconds later, the run is processed and results.extractions[0] carries every field with its confidence and the evidence it was read from:
Every extracted value is a JSON string on the wire, whatever the field’s declared type. The float field above answers "4594.62". Parse numbers before comparing or summing them. Confidence is an integer from 0 to 100.

Editing the workflow later

The edit loop is fetch, edit, replace. get_workflow returns the {name, description, nodes, edges} shape that update_workflow accepts. Renaming total_amount to grand_total while echoing its persistent_id keeps the field’s identity, so its metrics and history follow the rename:
Runs always execute the latest version. Old versions stay readable for comparison and audit. Call list_workflow_versions, then get_workflow(workflow_id, version=...).

Quick parse, and what it is not

parse_document is the one-call shortcut when you need only a single document’s markdown. Pass {object_id, filename}, or an HTTPS url, then call get_run. The markdown is at results.parse.markdown. The invoice above parsed in about 13 seconds. It is a quick one-off, not a production pipeline:
  • Every call starts a new billed run. There is no idempotency key on this path.
  • It runs against an auto-provisioned system parse workflow, not one you manage.
  • Save the markdown yourself. anyformat does not retain quick-parse output. Treat the run as ephemeral.
Anything recurring, retry-safe, or worth keeping belongs in a workflow: upload_documentsrun_document_packetget_run, as above.

Asking questions across documents

A workflow with a Knowledge node indexes every parsed document into a navigable corpus. ask_knowledge then answers questions about the content, with citations resolved to a page and region of the source PDF. Mint a thread_id with the prefix kb- to keep follow-ups in context. Every question is billed and can take a few minutes. KNOWLEDGE_NOT_READY means the index is still building. Retry.