Skip to main content

Async Runs

Async Runs allow you to invoke an agent without holding an HTTP connection open for the full execution. You submit a run, then poll for status or stream output in real time via SSE.

When to Use Async Runs

Use CaseRecommended Approach
Quick agent call (<30s)Synchronous /invoke or /invoke-stream
Long-running agent tasksAsync Runs — submit and poll/stream
Fire-and-forgetAsync Runs — submit and check later
Reliable delivery with retriesAsync Runs — with Idempotency-Key

Workflow Overview

Endpoints

Create a Run

POST /v1/agents/{agent_id}/runs

Headers:

HeaderRequiredDescription
AuthorizationYesBearer token
Idempotency-KeyNoUUID — prevents duplicate runs on retries

Request Body (Tool/RAG Agent):

{
"invokeRequest": {
"messages": [
{
"role": "user",
"content": "Summarize the Q4 financial report"
}
]
},
"versionId": "optional-version-uuid"
}

Request Body (Task Agent):

{
"invokeRequest": {
"inputData": {
"title": "Q4 Financial Report",
"content": "Revenue increased by 15%..."
}
},
"versionId": "optional-version-uuid"
}

Response (201 Created):

{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"agentId": "11111111-2222-3333-4444-555555555555",
"versionId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"status": "queued",
"inputPayload": { "messages": [{ "role": "user", "content": "..." }] },
"outputPayload": null,
"error": null,
"attempt": 1,
"createdAt": "2026-05-27T14:30:00Z",
"startedAt": null,
"completedAt": null
}

Notes:

  • If versionId is omitted, the agent's current (latest) version is used.
  • The Idempotency-Key header ensures that retrying the same request returns the existing run instead of creating a duplicate.

List Runs

GET /v1/agents/{agent_id}/runs

Query Parameters:

ParameterTypeDefaultDescription
statusstringFilter by status: queued, in_progress, completed, failed, cancelling, cancelled
offsetinteger0Pagination offset
limitinteger50Max results per page

Response (200):

{
"runs": [
{
"id": "a1b2c3d4-...",
"agentId": "11111111-...",
"versionId": "aaaaaaaa-...",
"status": "completed",
"inputPayload": { ... },
"outputPayload": { "response": "The Q4 report shows..." },
"error": null,
"attempt": 1,
"createdAt": "2026-05-27T14:30:00Z",
"startedAt": "2026-05-27T14:30:01Z",
"completedAt": "2026-05-27T14:30:15Z"
}
],
"pagination": {
"totalItems": 42,
"offset": 0,
"limit": 50,
"hasMore": false
}
}

Get Run Status

GET /v1/agents/{agent_id}/runs/{run_id}

Response (200):

Returns a single RunModel (same shape as the create response).


Cancel a Run

POST /v1/agents/{agent_id}/runs/{run_id}/cancel

Response (202 Accepted):

Returns the updated RunModel with status cancelling (or cancelled/completed/failed if already terminal).

Cancellation is idempotent — calling cancel on a run that has already finished returns its current state without error.


Stream Run Output (SSE)

GET /v1/agents/{agent_id}/runs/{run_id}:stream

Query Parameters:

ParameterTypeDefaultDescription
starting_afterstring0-0Stream ID to resume from (for reconnection)

Response (200, text/event-stream):

The stream emits data: events. Each event is a JSON object with a streamId field you can use for reconnection.

Chunk Events (LLM output)

Each data: event carries a type that corresponds to the streaming event type. Chunk events include a payload (JSON-serialized StreamingChunk); terminal events may omit payload.

typeDescription
response.createdResponse object created (first event)
response.output_text.deltaIncremental text from the assistant
response.function_call_arguments.doneTool call completed
response.completedFull response object (final content)
response.incompleteResponse cut short (e.g. content filter)
data: {"seq":"1","type":"response.output_text.delta","payload":"{\"type\":\"response.output_text.delta\",\"delta\":\"The quarterly revenue...\",\"id\":\"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\"role\":\"assistant\"}","streamId":"1716825600000-1"}

Terminal Events

data: {"seq":"5","type":"completed","streamId":"1716825600500-1"}
data: {"seq":"5","type":"failed","payload":"{\"error\":\"Model invocation timed out\"}","streamId":"1716825600500-1"}
data: {"seq":"5","type":"cancelled","streamId":"1716825600500-1"}

Keepalive (every ~15s during idle)

: keepalive

Reconnection: If the connection drops, reconnect with ?starting_after={lastStreamId} to resume from the last received event. Events are not replayed from the beginning.


Run Lifecycle

StatusTerminal?Description
queuedNoRun created, waiting for a worker
in_progressNoWorker is executing the agent
completedYesFinished successfully — check outputPayload
failedYesExecution error — check error field
cancellingNoCancel requested, worker stopping
cancelledYesRun was cancelled

Response Fields

FieldTypeDescription
idUUIDUnique run identifier
agentIdUUIDAgent this run belongs to
versionIdUUIDAgent version used for execution
statusstringCurrent lifecycle status
inputPayloadobject | nullThe invoke request payload (stored on creation)
outputPayloadobject | nullAgent output (populated on completion)
errorobject | nullError details (populated on failure)
attemptintegerExecution attempt number (starts at 1)
createdAtISO 8601When the run was created
startedAtISO 8601 | nullWhen a worker started execution
completedAtISO 8601 | nullWhen execution finished (success or failure)

Error Handling

HTTP StatusCause
201Run created successfully
202Cancel accepted
400Invalid request (e.g., task agent payload sent to tool agent)
404Agent, version, or run not found
500Internal server error

Error response format:

{
"statusCode": 404,
"error": "Not found",
"message": "Run abc123 not found for agent def456."
}

Example: Full Workflow

import httpx
import time

BASE = "https://your-env.example.com/v1"
AGENT_ID = "11111111-2222-3333-4444-555555555555"
HEADERS = {"Authorization": "Bearer <token>"}

# 1. Create a run
resp = httpx.post(
f"{BASE}/agents/{AGENT_ID}/runs",
headers={**HEADERS, "Idempotency-Key": "unique-request-id"},
json={
"invokeRequest": {
"messages": [{"role": "user", "content": "Analyze this document"}]
}
},
)
run = resp.json()
run_id = run["id"]
print(f"Run created: {run_id}, status: {run['status']}")

# 2. Poll until complete
while run["status"] not in ("completed", "failed", "cancelled"):
time.sleep(2)
resp = httpx.get(f"{BASE}/agents/{AGENT_ID}/runs/{run_id}", headers=HEADERS)
run = resp.json()

# 3. Get result
if run["status"] == "completed":
print(run["outputPayload"])
else:
print(f"Run {run['status']}: {run.get('error')}")

Streaming Example

import httpx

with httpx.stream(
"GET",
f"{BASE}/agents/{AGENT_ID}/runs/{run_id}:stream",
headers=HEADERS,
) as response:
last_id = None
for line in response.iter_lines():
if line.startswith("data: "):
import json
event = json.loads(line[6:])
last_id = event["streamId"]

if event["type"] == "response.output_text.delta":
payload = json.loads(event["payload"])
print(payload.get("delta", ""), end="", flush=True)
elif event["type"] in ("completed", "failed", "cancelled"):
print(f"\n[Run {event['type']}]")
break
elif event["type"] == "error":
print(f"\n[Stream error: {event.get('error')}]")
break