curl -X POST 'https://api.anyformat.ai/v3/workflows/0686bb97-8c30-70f0-8000-97669e000eb8/upload/run/' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Idempotency-Key: 7c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f' \
-F 'files=@/path/to/invoice.pdf' \
-F 'metadata={"customer_reference": "CR-2026-99001-ZTX"}'
import requests
import time
workflow_id = "0686bb97-8c30-70f0-8000-97669e000eb8"
base = "https://api.anyformat.ai"
headers = {"Authorization": "Bearer YOUR_API_KEY"}
with open("invoice.pdf", "rb") as f:
triggered = requests.post(
f"{base}/v3/workflows/{workflow_id}/upload/run/",
headers={**headers, "Idempotency-Key": "7c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f"},
files=[("files", f)],
).json()
run_url = f"{base}/v3/runs/{triggered['run_id']}/"
for _ in range(100):
run = requests.get(run_url, headers=headers).json()
if run["status"] == "processed":
print(run["results"])
break
if run["status"] in ("error", "cancelled"):
print(f"Terminal: {run['status']}")
break
time.sleep(3)
interface RunTriggered {
run_id: string;
document_packet_id: string;
workflow_id: string;
status: 'queued' | 'in_progress' | 'processed' | 'error' | 'cancelled';
}
const workflowId = '0686bb97-8c30-70f0-8000-97669e000eb8';
const formData = new FormData();
formData.append('files', fileInput.files[0]);
const response = await fetch(
`https://api.anyformat.ai/v3/workflows/${workflowId}/upload/run/`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Idempotency-Key': crypto.randomUUID(),
},
body: formData,
}
);
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const triggered: RunTriggered = await response.json();
console.log(triggered.run_id);
{
"run_id": "069dcc2c-e14c-7606-8000-2ee4fb17b4f9",
"document_packet_id": "069dcc2c-e14c-7606-8000-2ee4fb17b4e1",
"workflow_id": "0686bb97-8c30-70f0-8000-97669e000eb8",
"status": "queued"
}
Upload and Run
Upload a document packet and start a run in a single call
curl -X POST 'https://api.anyformat.ai/v3/workflows/0686bb97-8c30-70f0-8000-97669e000eb8/upload/run/' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Idempotency-Key: 7c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f' \
-F 'files=@/path/to/invoice.pdf' \
-F 'metadata={"customer_reference": "CR-2026-99001-ZTX"}'
import requests
import time
workflow_id = "0686bb97-8c30-70f0-8000-97669e000eb8"
base = "https://api.anyformat.ai"
headers = {"Authorization": "Bearer YOUR_API_KEY"}
with open("invoice.pdf", "rb") as f:
triggered = requests.post(
f"{base}/v3/workflows/{workflow_id}/upload/run/",
headers={**headers, "Idempotency-Key": "7c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f"},
files=[("files", f)],
).json()
run_url = f"{base}/v3/runs/{triggered['run_id']}/"
for _ in range(100):
run = requests.get(run_url, headers=headers).json()
if run["status"] == "processed":
print(run["results"])
break
if run["status"] in ("error", "cancelled"):
print(f"Terminal: {run['status']}")
break
time.sleep(3)
interface RunTriggered {
run_id: string;
document_packet_id: string;
workflow_id: string;
status: 'queued' | 'in_progress' | 'processed' | 'error' | 'cancelled';
}
const workflowId = '0686bb97-8c30-70f0-8000-97669e000eb8';
const formData = new FormData();
formData.append('files', fileInput.files[0]);
const response = await fetch(
`https://api.anyformat.ai/v3/workflows/${workflowId}/upload/run/`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Idempotency-Key': crypto.randomUUID(),
},
body: formData,
}
);
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const triggered: RunTriggered = await response.json();
console.log(triggered.run_id);
{
"run_id": "069dcc2c-e14c-7606-8000-2ee4fb17b4f9",
"document_packet_id": "069dcc2c-e14c-7606-8000-2ee4fb17b4e1",
"workflow_id": "0686bb97-8c30-70f0-8000-97669e000eb8",
"status": "queued"
}
202 response returns the new run_id. Poll GET /v3/runs/{run_id}/ until status is terminal. The results arrive inline on that same read.
Send the files as multipart form data under the files field, repeating it for a packet with several files. A file above the per-file size cap is rejected before any bytes reach S3. Packet creation is all-or-nothing.
Retries are safe with Idempotency-Key. Retrying with the same key replays the original packet and run. The API stores no duplicate upload and bills no second extraction. See Idempotency.
Filenames are unique within a workflow. The on_conflict form field decides what a collision does. Under the default, error, a colliding filename fails the request with 409 before anything is uploaded or run. Pass rename to rename instead, so invoice.pdf becomes invoice (1).pdf. This 202 response returns only the run_id, so read the stored names from GET /v3/document-packets/{document_packet_id}/.
Attach caller context with the optional metadata form field. It takes a JSON-encoded object, sent as a string because multipart carries no nested objects. Free text in natural language goes inside it, under the __anyformat_free_text__ key. The value flows into this run’s extract prompt as a “Document Metadata” section. A top-level key whose name matches an extract-schema field reaches the LLM and becomes the extracted value. GET /v3/document-packets/{document_packet_id}/ echoes the metadata back. See Attaching metadata.
An organization without extraction credit receives 402 PAYMENT_REQUIRED.
curl -X POST 'https://api.anyformat.ai/v3/workflows/0686bb97-8c30-70f0-8000-97669e000eb8/upload/run/' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Idempotency-Key: 7c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f' \
-F 'files=@/path/to/invoice.pdf' \
-F 'metadata={"customer_reference": "CR-2026-99001-ZTX"}'
import requests
import time
workflow_id = "0686bb97-8c30-70f0-8000-97669e000eb8"
base = "https://api.anyformat.ai"
headers = {"Authorization": "Bearer YOUR_API_KEY"}
with open("invoice.pdf", "rb") as f:
triggered = requests.post(
f"{base}/v3/workflows/{workflow_id}/upload/run/",
headers={**headers, "Idempotency-Key": "7c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f"},
files=[("files", f)],
).json()
run_url = f"{base}/v3/runs/{triggered['run_id']}/"
for _ in range(100):
run = requests.get(run_url, headers=headers).json()
if run["status"] == "processed":
print(run["results"])
break
if run["status"] in ("error", "cancelled"):
print(f"Terminal: {run['status']}")
break
time.sleep(3)
interface RunTriggered {
run_id: string;
document_packet_id: string;
workflow_id: string;
status: 'queued' | 'in_progress' | 'processed' | 'error' | 'cancelled';
}
const workflowId = '0686bb97-8c30-70f0-8000-97669e000eb8';
const formData = new FormData();
formData.append('files', fileInput.files[0]);
const response = await fetch(
`https://api.anyformat.ai/v3/workflows/${workflowId}/upload/run/`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Idempotency-Key': crypto.randomUUID(),
},
body: formData,
}
);
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const triggered: RunTriggered = await response.json();
console.log(triggered.run_id);
{
"run_id": "069dcc2c-e14c-7606-8000-2ee4fb17b4f9",
"document_packet_id": "069dcc2c-e14c-7606-8000-2ee4fb17b4e1",
"workflow_id": "0686bb97-8c30-70f0-8000-97669e000eb8",
"status": "queued"
}
Authorizations
API key issued from app.anyformat.ai/api-key. Send as Authorization: Bearer <key>.
Headers
Optional caller-supplied key (Stripe convention). Retrying the request with the same key replays the original packet AND run — no duplicate upload, no second extraction.
Path Parameters
Body
1..100 files forming one document packet.
Optional JSON-encoded object of free-form metadata attached to the packet. The extract operator may source datapoints from a top-level key whose name matches a schema field, and from free text in natural language under __anyformat_free_text__.
How to handle an uploaded filename that already exists in the workflow (filenames are unique within a workflow). error (the default) rejects the whole request with 409 and lists the conflicting names, so a rename is never silent — the caller must opt in. rename accepts the collision and lets the server auto-rename the file by inserting a (n) counter before the extension (invoice.pdf → invoice (1).pdf).
error, rename Response
Successful Response
202 response for both run triggers (upload/run/ and
document-packets/{id}/run/).
Unique identifier of the new run (hyphenated UUID).
"069dcc2c-e14c-7606-8000-2ee4fb17b4f9"
The document packet the run executes on (hyphenated UUID).
"069dcc2c-e14c-7606-8000-2ee4fb17b4e1"
The workflow being run (hyphenated UUID).
"0686bb97-8c30-70f0-8000-97669e000eb8"
Status at acceptance time — normally queued.
queued, in_progress, processed, error, cancelled "queued"

