Runs API
Runs are individual agent executions within a thread.
Create Run
POST /threads/{thread_id}/runs
Request Body
{
"input": {
"messages": [
{"role": "user", "content": "Hello!"}
]
},
"agent_id": "my-agent",
"metadata": {"source": "cli"},
"config": {
"temperature": 0.7
}
}
| Field | Description |
|---|---|
input | Input data passed to the agent |
agent_id | Optional. Graph/agent name to invoke (defaults to the first agent in langgraph.json) |
metadata | Optional. Arbitrary key/value metadata for the run |
config | Optional. Run configuration passed through to the agent |
Query Parameters
| Parameter | Default | Description |
|---|---|---|
wait (alias timeout) | - | Wait for completion. Accepts a duration string (e.g. 30s, 5m) or a plain integer number of seconds. Capped at 10 minutes. |
If the run hits an infrastructure or guest-crash failure, AgentVisor restarts it automatically (see Run Status below) before this call's wait completes — the 10-minute cap is unchanged, but restart backoff now consumes part of it. A run that previously failed fast enough to return a definitive status: "error" within the wait window can instead exhaust this endpoint's own wait timeout first, returning 504 Gateway Timeout — the run itself is not cancelled by that timeout and keeps restarting host-side; poll GET /threads/{id}/runs/{runId} afterward for the actual outcome rather than treating a 504 as a definitive failure. Set temporal.run_restart.max_attempts: 0 to disable automatic restart entirely if this budget-sharing is undesirable for a given deployment.
Response (Async — 202 Accepted)
{
"run_id": "thread-abc123_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"thread_id": "thread-abc123",
"status": "running",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
The current implementation transitions a run straight to running on creation — pending
is a defined status value but not currently reachable via this endpoint.
run_id is a composite {thread_id}_{run_uuid} string. Use it as-is with the
Run by ID endpoints, or the trailing UUID portion as {run_id} under
/threads/{thread_id}/runs/{run_id}.
Response (with ?wait=30s — 200 OK)
{
"run_id": "thread-abc123_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"thread_id": "thread-abc123",
"status": "completed",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:05Z",
"output": {
"messages": [
{"role": "user", "content": "Hello!"},
{"role": "assistant", "content": "Hi there!"}
]
}
}
Example
# Async
curl -X POST http://localhost:8090/threads/thread-abc123/runs \
-H "Content-Type: application/json" \
-d '{"input": {"messages": [{"role": "user", "content": "Hello"}]}}'
# Sync (wait for result)
curl -X POST "http://localhost:8090/threads/thread-abc123/runs?wait=30s" \
-H "Content-Type: application/json" \
-d '{"input": {"messages": [{"role": "user", "content": "Hello"}]}}'
List Runs
GET /threads/{thread_id}/runs
Query Parameters
| Parameter | Default | Description |
|---|---|---|
limit | 100 | Max number of runs to return (max 1000) |
offset | 0 | Pagination offset |
Response
[
{
"run_id": "thread-abc123_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"thread_id": "thread-abc123",
"status": "completed",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:05Z"
},
{
"run_id": "thread-abc123_b2c3d4e5-f6a7-8901-bcde-f12345678901",
"thread_id": "thread-abc123",
"status": "completed",
"created_at": "2024-01-15T10:25:00Z",
"updated_at": "2024-01-15T10:25:03Z"
}
]
Get Run
GET /threads/{thread_id}/runs/{run_id}
Response
{
"run_id": "thread-abc123_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"thread_id": "thread-abc123",
"status": "completed",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:05Z",
"output": {
"messages": [
{"role": "user", "content": "Hello!"},
{"role": "assistant", "content": "Hi there!"}
]
}
}
There is no input field on the run response — the run's input is not persisted;
retrieve it from thread state or history
instead.
Run Status
| Status | Description |
|---|---|
pending | Defined for a run not yet started; not currently returned by any endpoint |
running | Execution in progress (the status of a newly-created run). A run that hit an infrastructure or guest-crash failure and is automatically restarting also reports running throughout — restart never introduces a separate status |
completed | Finished successfully |
interrupted | Waiting for human input |
error | Execution failed (including a restartable failure whose restart budget was exhausted) |
cancelling | Cancellation requested, not yet stopped |
cancelled | Cancelled by user |
Automatic Restart Fields
Two additional fields on the run object surface AgentVisor's automatic restart history for infrastructure/guest-crash failures, and — since a Continue-As-New-triggered interruption of an active run is bookkept the same way — a CAN boundary too (see Recovery Scenarios). Both are omitted entirely unless the run has restarted at least once — a run that succeeded or failed on its first attempt never sets them:
| Field | Type | Description |
|---|---|---|
attempt | integer | 1-indexed count of InvokeRun attempts made for this run. Absent/0 means the run has not restarted; treat an absent value as 1. |
last_failure | string | The failure class (CRASH, INFRA, or CAN for a Continue-As-New-triggered interruption) of the failure that most recently triggered a restart. Empty if the run never restarted. |
{
"run_id": "thread-abc123_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"thread_id": "thread-abc123",
"status": "completed",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:12Z",
"attempt": 2,
"last_failure": "CRASH",
"output": {
"messages": [
{"role": "user", "content": "Hello!"},
{"role": "assistant", "content": "Hi there!"}
]
}
}
These fields report only the final attempt's outcome via status/output/error — they are restart history, not a state machine of their own. See temporal.run_restart in the configuration reference for the attempt-budget, backoff, and max_attempts: 0 disable knob.
Stream Run
GET /threads/{thread_id}/runs/{run_id}/stream
Returns a Server-Sent Events (SSE) stream. See the Streaming guide for the full event framing reference. Summary for this endpoint:
- One event per chunk, named after the chunk type (
values,updates,messages, orcustom), withdataset to the chunk's raw payload object. - A final
doneevent with an empty{}payload once the stream completes. - An
errorevent with{"error": "<message>"}if streaming fails.
Response
event: values
data: {"messages": [{"role": "assistant", "content": "Hi"}]}
event: values
data: {"messages": [{"role": "assistant", "content": "Hi there!"}]}
event: done
data: {}
Example
curl -N http://localhost:8090/threads/thread-abc123/runs/run-xyz789/stream
Cancel Run
POST /threads/{thread_id}/runs/{run_id}/cancel
Idempotent for already-terminal runs.
Response
Returns 202 Accepted with the full run object:
{
"run_id": "thread-abc123_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"thread_id": "thread-abc123",
"status": "cancelling",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:02Z"
}
Stateless Runs
Execute without a persistent thread.
Stateless Run (Async)
POST /runs
Creates an ephemeral thread and run. Returns 202 Accepted immediately with the same
RunResponse shape as Create Run (including status: "running").
Request Body
{
"input": {"messages": [{"role": "user", "content": "Hello"}]},
"agent_id": "my-agent"
}
Stateless Run (Sync)
POST /runs/wait
Blocks until completion and returns 200 OK with the final RunResponse. Same 10-minute
default/cap and automatic-restart budget-sharing as Create Run's wait parameter.
curl -X POST http://localhost:8090/runs/wait \
-H "Content-Type: application/json" \
-d '{"input": {"messages": [{"role": "user", "content": "Hello"}]}}'
Stateless Run (Streaming)
POST /runs/stream
Returns an SSE stream. Unlike Stream Run, this endpoint first emits a
start event, then one event per chunk with data wrapped as {"type": "<chunk type>", "data": {...}},
and finally a done event whose data is the full final RunResponse object (empty {}
if the final run lookup fails).
event: start
data: {"run_id": "...", "thread_id": "...", "status": "pending"}
event: values
data: {"type": "values", "data": {"messages": [...]}}
event: done
data: {"run_id": "...", "thread_id": "...", "status": "completed", ...}
curl -N -X POST http://localhost:8090/runs/stream \
-H "Content-Type: application/json" \
-d '{"input": {"messages": [{"role": "user", "content": "Hello"}]}}'
Run by ID
After creating a stateless run, you can manage it by run ID without referencing the (ephemeral) thread it was created in. The same set of endpoints also work for runs created within an explicit thread — pass the full composite run_id ({thread_id}_{run_uuid}) returned by any run response.
Get Run
GET /runs/{run_id}
Returns the run's current status and (if completed) output. Same response shape as GET /threads/{id}/runs/{runId} — there is no input field.
curl http://localhost:8090/runs/thread-abc123_a1b2c3d4-e5f6-7890-abcd-ef1234567890 | jq
Wait for Run
GET /runs/{run_id}/wait
Blocks until the run reaches a terminal status (completed, error, or cancelled) and returns the final response, 200 OK. Accepts the same wait/timeout query parameter as Create Run; defaults to and caps at 10 minutes. If the wait window elapses first — now more likely for a run that's mid automatic-restart-backoff, see the Create Run note — this returns 504 Gateway Timeout instead; the run is not cancelled and its current status can still be polled via GET /runs/{run_id} above.
curl http://localhost:8090/runs/thread-abc123_a1b2c3d4-e5f6-7890-abcd-ef1234567890/wait | jq
Stream Run
GET /runs/{run_id}/stream
Returns Server-Sent Events for the run. Not the same event shape as
GET /threads/{id}/runs/{runId}/stream: chunk data here is wrapped as
{"type": "<chunk type>", "data": {...}}, and the final done event's data is the
full final RunResponse object (empty {} if the final run lookup fails) rather than {}.
curl -N http://localhost:8090/runs/thread-abc123_a1b2c3d4-e5f6-7890-abcd-ef1234567890/stream
Cancel Run
POST /runs/{run_id}/cancel
Returns 202 Accepted with the full run object. Idempotent for already-terminal runs.
curl -X POST http://localhost:8090/runs/thread-abc123_a1b2c3d4-e5f6-7890-abcd-ef1234567890/cancel
Delete Run
DELETE /runs/{run_id}
Removes a run record, returning 204 No Content. Blocked (409 Conflict) while the run
is pending or running.
curl -X DELETE http://localhost:8090/runs/thread-abc123_a1b2c3d4-e5f6-7890-abcd-ef1234567890