Task Lifecycle
Understand how agent tasks move through states — from creation to completion — and how to track, stream, continue, or cancel them.
Every agent task follows a predictable lifecycle. This page explains each phase, the available status values, and the patterns you can use to interact with a task at each stage.
The lifecycle, status values, and event stream below are identical across agent runtimes — a Codex task and a Claude task move through the same states and emit the same events. Whichever runtime the model (or agent override) selects is transparent to every endpoint on this page.
Lifecycle Overview
POST /api/v1/tasks
│
▼
{ taskId, runId } ← created (status: queued)
│
▼
Agent starts ← status: running
│
├──► GET /api/v1/tasks/:id/status poll lightweight status
│
├──► GET /api/v1/tasks/:id/logs/stream live SSE log stream
│
├──► GET /api/v1/agent/stream?runId=... raw SSE stream
│
├──► POST /api/v1/tasks/:id/continue send a follow-up prompt
│
└──► PATCH /api/v1/tasks/:id cancel the task
│
▼
Terminal state ← completed | failed | cancelled | interruptedStatus Values
| Status | Description | Terminal? |
|---|---|---|
queued |
Task is waiting to be picked up by an agent | No |
running |
Agent is actively executing the task | No |
completed |
Task finished successfully | ✅ Yes |
failed |
Task encountered an unrecoverable error | ✅ Yes |
cancelled |
Task was cancelled by the user | ✅ Yes |
interrupted |
Task was interrupted (e.g. server restart) | ✅ Yes |
Once a task reaches a terminal state it will not change again. Poll until you see one of the four terminal statuses.
Phase 1 — Create
Start a task with POST /api/v1/tasks. You receive a runId immediately — the agent begins executing asynchronously.
curl -X POST 'https://agent.blackbox.ai/api/v1/tasks' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"prompt": "Add a README in French",
"repoUrl": "https://github.com/org/repo.git",
"selectedBranch": "main"
}'{
"taskId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"runId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"chatId": "chat_def789ghi012",
"agentCount": 1
}Save the runId — you'll use it for every subsequent operation.
Phase 2 — Monitor
You have two options for tracking progress:
Option A — Poll Status (lightweight)
Best for simple integrations. Call GET /api/v1/tasks/:id/status every few seconds:
const DONE = ["completed", "failed", "cancelled", "interrupted"];
while (true) {
const res = await fetch(`/api/v1/tasks/${runId}/status`, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
const s = await res.json();
console.log(`${s.status} — ${s.progress}%`);
if (DONE.includes(s.status)) break;
await new Promise(r => setTimeout(r, 3000));
}The progress field (0–100) is a linear estimate based on elapsed time.
Option B — Stream Logs (real-time)
Best for live UIs or CLI output. Connect to the SSE stream and receive events as they happen:
const response = await fetch(`/api/v1/tasks/${runId}/logs/stream`, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
for (const line of decoder.decode(value).split("\n")) {
if (!line.startsWith("data: ")) continue;
const event = JSON.parse(line.slice(6));
if (event.type === "text-delta") process.stdout.write(event.delta);
if (event.type === "tool-call-start") console.log(`\nTool: ${event.toolName}`);
if (event.type === "finish") { console.log(`\nDone: ${event.status}`); break; }
}
}Phase 3 — Retrieve Results
Once completed, fetch the full task details including the conversation history and GitHub context:
curl 'https://agent.blackbox.ai/api/v1/tasks/RUN_ID' \
-H 'Authorization: Bearer YOUR_API_KEY'Key fields in the response:
| Field | Description |
|---|---|
status |
Final status of the run |
github.createdBranch |
Branch the agent pushed changes to |
messages |
Full conversation history |
completedAt |
ISO 8601 timestamp of completion |
Extract the agent's reply:
const data = await res.json();
const lastReply = data.messages.filter(m => m.role === "assistant").at(-1);
const text = lastReply?.parts?.find(p => p.type === "text")?.text ?? "";
console.log("Agent reply:", text);Phase 4 — Continue (Optional)
Send a follow-up prompt to the same task. The agent receives the full conversation history as context and, if a GitHub repo was involved, continues from the branch it created:
curl -X POST 'https://agent.blackbox.ai/api/v1/tasks/RUN_ID/continue' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{ "prompt": "Now add unit tests for the new module" }'The response contains a new runId for the follow-up run. Use it to track the continuation independently.
Cancel a Running Task
Send a PATCH request with action: "cancel" at any point while the task is queued or running:
curl -X PATCH 'https://agent.blackbox.ai/api/v1/tasks/RUN_ID' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{ "action": "cancel" }'{ "success": true, "status": "cancelled", "message": "Task cancelled successfully" }Cancelling a task that is already in a terminal state returns success: false.
Multi-Agent Tasks
When you pass selectedAgents with 2–5 entries, each agent gets its own runId. Track each run independently using the same endpoints described above.
Recommended Patterns
Simple — Poll with Backoff
async function pollWithBackoff(runId, apiKey) {
const DONE = ["completed", "failed", "cancelled", "interrupted"];
let delay = 1000;
while (true) {
const res = await fetch(`/api/v1/tasks/${runId}/status`, {
headers: { Authorization: `Bearer ${apiKey}` },
});
const s = await res.json();
if (DONE.includes(s.status)) return s;
await new Promise(r => setTimeout(r, delay));
delay = Math.min(delay * 2, 30_000); // cap at 30s
}
}Advanced — Stream + Continue
// 1. Create task
const res = await fetch("/api/v1/tasks", {
method: "POST",
headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ prompt: "Build a REST API" }),
});
const { runId } = await res.json();
// 2. Stream live output
const stream = await fetch(`/api/v1/tasks/${runId}/logs/stream`, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
const reader = stream.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
for (const line of decoder.decode(value).split("\n")) {
if (!line.startsWith("data: ")) continue;
const event = JSON.parse(line.slice(6));
if (event.type === "text-delta") process.stdout.write(event.delta);
if (event.type === "finish") break;
}
}
// 3. Continue with a follow-up
const cont = await fetch(`/api/v1/tasks/${runId}/continue`, {
method: "POST",
headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ prompt: "Now add OpenAPI docs" }),
});
const { runId: newRunId } = await cont.json();