Die Statusmaschine eines Runs, die Endpunkte jeder Phase und die genauen JSON-Formen.
A run moves through exactly one of these statuses at a time:
┌───────────────┐
POST /v1/runs -> │ queued │
└───────┬───────┘
│ worker claims the job
▼
┌───────────────┐
┌──── │ running │ ────┐
│ └───────┬───────┘ │
pauses on │ │ │ finishes
human step │ ▼ │
│ ┌───────────────┐ │
│ │ (resumed) │ │
▼ └───────┬───────┘ ▼
┌─────────────────┐ │ ┌───────────────┐
│ needs_input │ │ │ succeeded │
│ needs_approval │──────┘ ├───────────────┤
│ needs_review │ │ failed │
└─────────────────┘ ├───────────────┤
│ │ cancelled │
└── cancel ──────────>└───────────────┘queued — accepted, credits reserved, waiting for apps/worker to claim it.running — the kernel's Diagnose → Assemble → Act → Assess loop is executing.needs_input / needs_approval / needs_review — paused on a person; see Human-in-the-loop. Resuming (or rejecting) sends it back to running, or straight to a terminal status.succeeded / failed / cancelled — terminal. A webhook fires for whichever one it is (and for the three needs_* pauses) if you've subscribed to that event.Only queued, needs_input, needs_approval, and needs_review runs can be cancelled — cancelling a run already running returns 409 run_currently_running (there's no mid-flight abort signal in this version), with one exception: a running run whose worker is gone (no live heartbeat for its job and a lock older than three minutes — a worker replaced by a deploy mid-run) is cancelled with error.code = interrupted and its reserved credits refunded, the same outcome the worker's own recovery sweep produces a few minutes later. Cancelling a terminal run returns 409 run_already_terminal.
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": { "fields": { "note": "Q1 batch" }, "fileIds": ["b4a0c9e1-...-1f0a"] },
"options": { "webhookUrl": "https://example.com/hooks/afb" }
}'Returns 202 { "id", "status": "queued", "estimateCredits" }, or 402 billing.insufficient_credits with { "required", "available" } if the workspace's balance can't cover the estimate.
options.model does not change the model a run uses: it only prices the estimateCredits returned here, and the run itself uses the agent's saved models. options.budget is stored on the run and returned with it, but the run is held to the agent's own { maxCredits, maxIterations, maxDurationMs } caps.
curl "https://api.agentflowbind.com/v1/runs/5e6a7b8c-...-9d0e" -H "Authorization: Bearer afb_live_xxxxxxxxxxxx"{
"id": "5e6a7b8c-...-9d0e",
"workspaceId": "1a2b3c4d-...",
"profileVersionId": "3c1a9e40-...",
"status": "succeeded",
"input": { "fields": { "documentArtifactIds": ["..."] }, "artifacts": [{ "filename": "invoice.pdf", "mime": "application/pdf", "sha256": "..." }] },
"output": { "supplier": { "name": "Acme GmbH" }, "total": 1249.5 },
"error": null,
"budget": null,
"creditsReserved": 42,
"creditsSettled": 38,
"startedAt": "2026-03-14T09:12:03.000Z",
"finishedAt": "2026-03-14T09:12:41.000Z",
"createdAt": "2026-03-14T09:12:00.000Z",
"updatedAt": "2026-03-14T09:12:41.000Z",
"costCredits": 38,
"humanRequest": null
}humanRequest is non-null exactly when status is one of the three needs_* values — it's the pending question/approval/review payload the run is blocked on.
GET /v1/runs is cursor-paginated, newest first:
curl "https://api.agentflowbind.com/v1/runs?status=succeeded&agentId=3c1a9e40-...-77bd&limit=20" \
-H "Authorization: Bearer afb_live_xxxxxxxxxxxx"{ "runs": [ { "id": "...", "status": "succeeded", "...": "..." } ], "nextCursor": "eyJjcmVhdGVkQXQiOiI..." }Pass the previous response's nextCursor back as ?cursor=... to fetch the next page; nextCursor is null on the last page.
GET /v1/runs/:id/steps returns the kernel's ordered trace — one row per Diagnose/Assemble/Act/Assess/tool-call/system step:
curl "https://api.agentflowbind.com/v1/runs/5e6a7b8c-...-9d0e/steps?include=io" \
-H "Authorization: Bearer afb_live_xxxxxxxxxxxx"{
"steps": [
{
"id": "...", "seq": 3, "iteration": 1, "phase": "tool", "tool": "extract_fields",
"provider": "anthropic", "model": "claude-sonnet-5", "hostRegion": "us",
"tokens": { "input": 4218, "output": 612, "cached": 0 },
"cost": 6, "duration": 3120, "status": "ok",
"createdAt": "2026-03-14T09:12:11.000Z",
"input": { "artifactId": "...", "schema": { "...": "..." } },
"output": { "supplier": { "name": "Acme GmbH" } }
}
]
}Omit ?include=io to leave off the (potentially large) input/output fields.
# Final JSON output
curl "https://api.agentflowbind.com/v1/runs/5e6a7b8c-...-9d0e/output" -H "Authorization: Bearer afb_live_xxxxxxxxxxxx"
# As CSV, if the preset's output flattens cleanly (409 csv_not_supported otherwise)
curl "https://api.agentflowbind.com/v1/runs/5e6a7b8c-...-9d0e/output?format=csv" -H "Authorization: Bearer afb_live_xxxxxxxxxxxx"
# The same CSV (or XLSX) rendered for a reader instead of a parser: add ?locale=
curl "https://api.agentflowbind.com/v1/runs/5e6a7b8c-...-9d0e/output?format=csv&locale=it" -H "Authorization: Bearer afb_live_xxxxxxxxxxxx"
# Download a specific artifact the run produced or was given
curl "https://api.agentflowbind.com/v1/runs/5e6a7b8c-...-9d0e/artifacts/b4a0c9e1-...-1f0a" \
-H "Authorization: Bearer afb_live_xxxxxxxxxxxx" -OJ
# Cancel a queued/paused run
curl -X POST "https://api.agentflowbind.com/v1/runs/5e6a7b8c-...-9d0e/cancel" -H "Authorization: Bearer afb_live_xxxxxxxxxxxx"GET /v1/runs/:id/output returns 409 output_not_available before the run has produced one — poll or subscribe to events instead of racing this endpoint.
Without ?locale=, format=csv and format=xlsx are the machine flavour: the flattened JSON paths as headers, . decimals, , separators, no BOM. With it, the same table is rendered for a person in that locale — the output schema's own column titles, that locale's decimal separator and dates, the list separator it implies (; wherever the decimal is a comma, which is what Excel expects there), CRLF endings and a UTF-8 BOM. The console downloads the localized flavour; an integration that parses the file should leave locale off.
GET /v1/runs/:id/events streams text/event-stream instead of polling: it replays every stored event (from Last-Event-ID, or the beginning), then streams live ones, with a heartbeat every 15 seconds, and closes once the run reaches a terminal status.
const events = new EventSource(
`https://api.agentflowbind.com/v1/runs/${runId}/events`,
// Browsers can't set an Authorization header on EventSource — front this
// through your own server, or use the SDK (preview)'s `runs.wait(id, { onEvent })`,
// which uses `fetch` and can send the header directly.
);
events.addEventListener("status_changed", (event) => {
const payload = JSON.parse(event.data) as { status: string };
console.log("run status ->", payload.status);
if (["succeeded", "failed", "cancelled"].includes(payload.status)) events.close();
});Each frame's id: is the event's per-run sequence number — a reconnecting client that sends it back as Last-Event-ID resumes exactly where it left off rather than re-processing (or missing) events.
POST /v1/runs/:id/feedback records a rating and/or a corrected output on a finished run:
curl -X POST "https://api.agentflowbind.com/v1/runs/5e6a7b8c-...-9d0e/feedback" \
-H "Authorization: Bearer afb_live_xxxxxxxxxxxx" -H "Content-Type: application/json" \
-d '{ "rating": 4, "correctedOutput": { "supplier": { "name": "Acme GmbH" }, "total": 1259.5 }, "comment": "Total was off by 10" }'Supplying correctedOutput also creates a reusable eval case for this agent (see Evals), unless you pass "useAsExample": false.