Skip to main content
A workflow runs on one or more files that anyformat treats as a single document. That bundle is a document packet. Almost always the packet holds one file. You upload a file, you get a packet, and the two line up 1-to-1. The packet becomes visible when you bundle several files that belong together for extraction, such as a contract and its two annexes.

Why a packet, not just a file

Extraction is scoped to the packet, not to the individual files inside it. That means:
  • Cross-file context survives. Parse and Extract see the whole bundle at once, not one file at a time.
  • Results come back at the packet level, as a single set of extracted fields for the whole document.
  • Classify, Split, and Validate operate on the packet as one input.
For a packet with one file this looks identical to file-level processing. For a packet with several files, it lets you treat “contract + annexes” or “invoice + supporting scans” as one document.

Packets and files

The packet has an id. Each file inside it has its own id. You work with packets almost all the time. A file id comes up only when you look at one specific file inside a packet with several files.
Two API names, one object. On v3 every request and response uses document_packet_id. The packet endpoints show it. The frozen v2 surface calls the same object a file collection, with file_collection_id or sometimes collection_id, used the same way. The ids are interchangeable across versions. The migration guide has the mapping.

How you get one

You create a packet by uploading. Two ways hand anyformat the bytes:
  • Direct upload, multipart. You send the file bytes in the request. The response returns status: "uploaded" once the bytes are in storage, and you can run immediately. Use it when the bytes sit on the user’s machine or in your app’s memory.
  • From a URL. You give anyformat an HTTPS URL, such as a presigned S3 link or a hosted asset, and the server fetches the bytes. Use it when the file already lives in object storage you can presign, or at a public URL. Nothing streams through your backend.
Both produce a packet id you run, poll, and pull results against. Files lists supported formats and size limits. Upload with POST .../upload/, which takes 1 to 100 multipart files into one packet. Or use POST .../upload/from-url/, which takes 1 to 100 HTTPS URLs, all or nothing. Each imported file is named by its response’s Content-Disposition, else by the URL’s path. The URL import is atomic, so a 201 means the packet is fully in storage, with no pending-fetch state to reason about.

One file vs. multiple files

One file, the default. Upload one file and you get a packet that holds it. In everyday use the packet id and the file id are interchangeable. You address the packet. Multiple files. Bundle them into one packet only when they belong together as a single document. anyformat then processes the packet as a whole, and extraction runs over the bundle rather than file by file. Upload unrelated files as two packets, which keeps their results independent.

Attaching metadata

Every v3 create path accepts an optional metadata field: a free-form JSON object attached to the packet at creation time. There is no schema. The only validation is “must be a JSON object”, and a free text under __anyformat_free_text__ must be a string. GET /v3/document-packets/{document_packet_id}/ echoes the value back verbatim. When you run the packet, the Extract node sees it inline in its prompt. Three uses come with it out of the box:
  • Carry structured context alongside the bytes. Anything you already know about the document travels with the packet, with no side channel: the vendor id you fetched it for, the account it belongs to, the batch you are processing. Fetch the packet later and the same JSON comes back.
  • Let extraction source a datapoint from your context. anyformat picks up a top-level key whose name matches an extract-schema field. The model prefers that metadata value over anything it reads in the document. The extracted field’s evidence then carries the marker text: "metadata.<key>". Key on that marker client-side to tell “sourced from caller metadata” apart from “sourced from the document”. Runs & results → Evidence covers the evidence shape.
  • Write the context in your own words. Put free text under the reserved key __anyformat_free_text__, such as {"__anyformat_free_text__": "Supplier ACME, plant in Zaragoza, invoices for March 2026"}. It may sit beside your other keys. Extraction reads it as notes about the document and fills a field from them when they state or imply its value. The evidence of such a field carries the marker text: "metadata.__anyformat_free_text__". The value must be a string of at most 8192 characters; anything else is refused with 400.
anyformat hardens the prompt copy, because your metadata enters an instruction channel. Control characters collapse to spaces. Each value is capped at 512 characters, and the keys together at 8192 characters. Keys over that cap are dropped in alphabetical order until the rest fit, and the platform logs which keys it dropped. The free text is not part of that cap: it has its own limit of 8192 characters, checked when the packet is created, and its line breaks become spaces. The packet itself still echoes your original JSON back verbatim. On the multipart uploads, send it as a JSON-encoded string, because multipart cannot carry a nested object natively. That covers .../upload/ and .../upload/run/. On the URL import, .../upload/from-url/, send it as a plain JSON object.

What’s next?

Files

Supported formats, size limits, and per-file mechanics

Runs & results

What happens when you run a workflow on a packet, and the shape of what comes back