Runs lifecycle & statuses
#The status machine
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 forapps/workerto 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 torunning, or straight to a terminal status.succeeded/failed/cancelled— terminal. A webhook fires for whichever one it is (and for the threeneeds_*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), and cancelling a terminal run returns 409 run_already_terminal.
#Creating a run
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 overrides the preset's default control-phase model for this run only; options.budget overrides the preset's default { maxCredits, maxIterations, maxDurationMs } caps.
#Reading a run
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.
#Listing runs
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.
#Steps
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.
#Output, artifacts, and cancellation
# 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"
# 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.
#Live events (SSE)
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.
#Feedback
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.