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.
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.
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 optionalmetadata 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 markertext: "metadata.__anyformat_free_text__". The value must be a string of at most 8192 characters; anything else is refused with400.
.../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

