Getting started
#Overview
Agent Flow Bind runs prebuilt agents — document extraction today, phone and finance families later — through three interchangeable "doors": the console (a human clicking through a UI), the API documented here, or a published app an end user fills in directly. All three ultimately call the same /v1 API; this guide walks through calling it directly.
Every request and response on /v1 is JSON. All timestamps are ISO 8601 UTC, and every resource id is a UUID.
#1. Get an API key
API keys are workspace-scoped and carry one or more scopes (runs:write, agents:read, ...). The very first key for a new workspace is created from the console (Settings → API keys), which authenticates with your session cookie instead of a key you don't have yet. Once you have one key, you can mint more from the API itself:
curl -X POST "https://api.agentflowbind.com/v1/api-keys" \
-H "Authorization: Bearer afb_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"name": "CI pipeline",
"scopes": ["runs:read", "runs:write", "files:write", "agents:read"]
}'{
"id": "8f1e2b0a-...-c9d3",
"name": "CI pipeline",
"scopes": ["runs:read", "runs:write", "files:write", "agents:read"],
"key": "afb_live_51f3a9...",
"createdAt": "2026-03-14T09:12:00.000Z"
}The plaintext key is only ever returned once, at creation — store it in a secret manager, not in source control. Every other request in this guide sends it as Authorization: Bearer afb_live_....
#2. Pick a preset and create an agent
GET /v1/presets lists every preset the registry knows about, with the input it expects and the output schema it produces:
curl "https://api.agentflowbind.com/v1/presets" \
-H "Authorization: Bearer afb_live_xxxxxxxxxxxx"The document family ships three presets today: document/supplier-invoice, document/delivery-note, and the schema-agnostic document/custom-form (see Presets & schemas). An agent is a workspace's own named, versioned configuration of a preset — create one with POST /v1/agents:
curl -X POST "https://api.agentflowbind.com/v1/agents" \
-H "Authorization: Bearer afb_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"presetId": "document/supplier-invoice",
"name": "AP invoice extractor"
}'{
"id": "3c1a9e40-...-77bd",
"family": "document",
"presetId": "document/supplier-invoice",
"name": "AP invoice extractor",
"currentVersion": 1,
"config": { "presetId": "document/supplier-invoice" }
}Keep the returned id — it's the agentId every run below references.
#3. Upload a document
POST /v1/files accepts a raw body (with Content-Type + X-Filename) or a multipart/form-data body. Uploads are capped at 25 MB and allow application/pdf, image/png, image/jpeg, text/csv, and text/plain:
curl -X POST "https://api.agentflowbind.com/v1/files" \
-H "Authorization: Bearer afb_live_xxxxxxxxxxxx" \
-H "Content-Type: application/pdf" \
-H "X-Filename: invoice-2026-03.pdf" \
--data-binary @invoice-2026-03.pdf{
"id": "b4a0c9e1-...-1f0a",
"filename": "invoice-2026-03.pdf",
"mime": "application/pdf",
"size": 184320,
"sha256": "9f2c1e...4b7a"
}#4. Create a run
POST /v1/runs queues an agent execution: it estimates the cost, reserves credits (a 402 billing.insufficient_credits here means the workspace balance is too low), inserts the run in queued status, and enqueues it for apps/worker to pick up. Reference the uploaded file by id under input.fileIds:
curl -X POST "https://api.agentflowbind.com/v1/runs" \
-H "Authorization: Bearer afb_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 2fbb0e7a-6b8a-4b0e-8e2a-6d6b6a2b7a10" \
-d '{
"agentId": "3c1a9e40-...-77bd",
"input": { "fileIds": ["b4a0c9e1-...-1f0a"] }
}'{ "id": "5e6a7b8c-...-9d0e", "status": "queued", "estimateCredits": 42 }The Idempotency-Key header is optional but recommended for anything that retries on a network error — see Errors & rate limits. See Runs lifecycle & statuses for the full state machine a run moves through after this.
async function createRun(agentId: string, fileId: string): Promise<{ id: string }> {
const res = await fetch("https://api.agentflowbind.com/v1/runs", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AFB_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({ agentId, input: { fileIds: [fileId] } }),
});
if (!res.ok) throw new Error(`createRun failed: ${res.status} ${await res.text()}`);
return res.json();
}#5. Wait for it to finish
Poll GET /v1/runs/:id until status is terminal (succeeded, failed, or cancelled) — or open GET /v1/runs/:id/events for a live text/event-stream instead of polling:
curl "https://api.agentflowbind.com/v1/runs/5e6a7b8c-...-9d0e" \
-H "Authorization: Bearer afb_live_xxxxxxxxxxxx"{
"id": "5e6a7b8c-...-9d0e",
"status": "succeeded",
"costCredits": 38,
"output": { "supplier": { "name": "Acme GmbH" }, "total": 1249.5, "...": "..." },
"humanRequest": null
}A status of needs_input, needs_approval, or needs_review instead means the run is paused waiting on a person — see Human-in-the-loop.
#6. Read the output
Once succeeded, GET /v1/runs/:id/output returns the final structured JSON (or ?format=csv when the preset supports it):
curl "https://api.agentflowbind.com/v1/runs/5e6a7b8c-...-9d0e/output" \
-H "Authorization: Bearer afb_live_xxxxxxxxxxxx"That's the whole loop. From here: subscribe a webhook instead of polling, wire up human-in-the-loop answers, or reach for the TypeScript SDK (preview) to skip writing this HTTP plumbing by hand.