Skip to main content

State API

Get and update thread state directly.

Get State

GET /threads/{thread_id}/state

Response

{
"thread_id": "thread-abc123",
"values": {
"step_count": 2,
"shopping_items": ["milk", "eggs"]
},
"messages": [
{"role": "user", "content": "Hello"},
{"role": "assistant", "content": "Hi there!"}
]
}

Query Parameters

ParameterDefaultDescription
subgraphsfalseAccepted for Agent Protocol parity. AgentVisor has no per-task subgraph tree, so this has no observable effect beyond not erroring. Parsed leniently: an unparseable value (e.g. ?subgraphs=yes) is silently treated as false rather than rejected, matching limit's parsing on this and other endpoints.

Fields

FieldDescription
thread_idThe thread ID
valuesCurrent state values
messagesOptional. Conversation history, if the agent's state schema tracks messages
checkpointOmitted on this default (root, current head) call. Present on the checkpoint-targeted variants below.

There is no next field — use Thread History to inspect checkpoints.

Example

curl http://localhost:8090/threads/thread-abc123/state | jq

Get State at a Checkpoint

Target a specific checkpoint or namespace (LangGraph subgraph) instead of the thread's current root state.

POST /threads/{thread_id}/state/checkpoint

Request Body

{
"checkpoint": {
"checkpoint_ns": "",
"checkpoint_id": "cp-002"
},
"subgraphs": false
}
FieldDescription
checkpoint.checkpoint_nsOptional. Selects a namespace (empty string is the root graph). Ignored when checkpoint_id is set — checkpoint IDs are already globally unique across namespaces.
checkpoint.checkpoint_idOptional. Targets a specific checkpoint. Empty resolves to the current head of checkpoint_ns.
subgraphsAccepted for Agent Protocol parity; has no observable effect (see above).

Alternatively, fetch by checkpoint ID directly:

GET /threads/{thread_id}/state/{checkpoint_id}?subgraphs=false

Response

Same shape as Get State, but checkpoint is always populated:

{
"thread_id": "thread-abc123",
"values": {"step_count": 2},
"checkpoint": {
"thread_id": "thread-abc123",
"checkpoint_ns": "",
"checkpoint_id": "cp-002"
}
}

Example

curl -X POST http://localhost:8090/threads/thread-abc123/state/checkpoint \
-H "Content-Type: application/json" \
-d '{"checkpoint": {"checkpoint_id": "cp-002"}}'

curl http://localhost:8090/threads/thread-abc123/state/cp-002

Update State

POST /threads/{thread_id}/state

Update thread state. Useful for:

  • Setting initial values
  • Correcting state
  • Injecting data

Request Body

The entire JSON body is the values map — there is no values/as_node wrapper, and as_node is not supported.

{
"shopping_items": ["milk", "eggs", "bread"]
}

Response

{
"thread_id": "thread-abc123",
"values": {
"shopping_items": ["milk", "eggs", "bread"]
}
}

The response does not include messages, even if the update body contains a messages key.

Example

curl -X POST http://localhost:8090/threads/thread-abc123/state \
-H "Content-Type: application/json" \
-d '{"approved": true}'

Updating Messages

To add a message to the conversation:

curl -X POST http://localhost:8090/threads/thread-abc123/state \
-H "Content-Type: application/json" \
-d '{
"messages": [{"role": "assistant", "content": "Injected message"}]
}'

Note: Messages use the add_messages reducer, so new messages are appended.

State vs Run

ApproachUse Case
Update stateInject data without running agent
Create runExecute agent with new input

For human-in-the-loop, you typically:

  1. Create run with user input
  2. Agent processes and may interrupt()
  3. Get state to see interrupt payload
  4. Create new run with user's response

You don't usually need to update state directly unless you're:

  • Correcting errors
  • Initializing state
  • Testing