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 Case | Recommended Approach |
|---|---|
| Quick agent call (<30s) | Synchronous /invoke or /invoke-stream |
| Long-running agent tasks | Async Runs — submit and poll/stream |
| Fire-and-forget | Async Runs — submit and check later |
| Reliable delivery with retries | Async Runs — with Idempotency-Key |
Workflow Overview
Endpoints
Create a Run
POST /v1/agents/{agent_id}/runs
Headers:
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer token |
Idempotency-Key | No | UUID — 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
versionIdis omitted, the agent's current (latest) version is used. - The
Idempotency-Keyheader 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:
| Parameter | Type | Default | Description |
|---|---|---|---|
status | string | — | Filter by status: queued, in_progress, completed, failed, cancelling, cancelled |
offset | integer | 0 | Pagination offset |
limit | integer | 50 | Max 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:
| Parameter | Type | Default | Description |
|---|---|---|---|
starting_after | string | 0-0 | Stream 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.
type | Description |
|---|---|
response.created | Response object created (first event) |
response.output_text.delta | Incremental text from the assistant |
response.function_call_arguments.done | Tool call completed |
response.completed | Full response object (final content) |
response.incomplete | Response 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
| Status | Terminal? | Description |
|---|---|---|
queued | No | Run created, waiting for a worker |
in_progress | No | Worker is executing the agent |
completed | Yes | Finished successfully — check outputPayload |
failed | Yes | Execution error — check error field |
cancelling | No | Cancel requested, worker stopping |
cancelled | Yes | Run was cancelled |
Response Fields
| Field | Type | Description |
|---|---|---|
id | UUID | Unique run identifier |
agentId | UUID | Agent this run belongs to |
versionId | UUID | Agent version used for execution |
status | string | Current lifecycle status |
inputPayload | object | null | The invoke request payload (stored on creation) |
outputPayload | object | null | Agent output (populated on completion) |
error | object | null | Error details (populated on failure) |
attempt | integer | Execution attempt number (starts at 1) |
createdAt | ISO 8601 | When the run was created |
startedAt | ISO 8601 | null | When a worker started execution |
completedAt | ISO 8601 | null | When execution finished (success or failure) |
Error Handling
| HTTP Status | Cause |
|---|---|
201 | Run created successfully |
202 | Cancel accepted |
400 | Invalid request (e.g., task agent payload sent to tool agent) |
404 | Agent, version, or run not found |
500 | Internal 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