Skip to main content

REST API: Threads

Thread endpoints let you read and manage conversation state stored in the checkpointer. A thread is identified by its thread_id.

Base path: /v1/threads

Route summary

MethodPathPermission
GET/v1/threadscheckpointer:read
GET/v1/threads/{thread_id}checkpointer:read
DELETE/v1/threads/{thread_id}checkpointer:delete
GET/v1/threads/{thread_id}/statecheckpointer:read
PUT/v1/threads/{thread_id}/statecheckpointer:write
DELETE/v1/threads/{thread_id}/statecheckpointer:delete
GET/v1/threads/{thread_id}/messagescheckpointer:read
POST/v1/threads/{thread_id}/messagescheckpointer:write
GET/v1/threads/{thread_id}/messages/{message_id}checkpointer:read
DELETE/v1/threads/{thread_id}/messages/{message_id}checkpointer:delete

There is no create-thread endpoint. A thread is created implicitly by the first POST /v1/graph/invoke or POST /v1/graph/stream that uses its thread_id, or by the server generating one when the request omits it.


GET /v1/threads

List threads stored in the checkpointer. With ownership authorization active, only threads owned by the caller are returned.

Query parameters:

ParameterTypeDefaultDescription
searchstringFree-text filter over thread records
offsetintegerNumber of threads to skip. Must be >= 0.
limitinteger100Page size. Clamped server-side to a maximum of 1000 regardless of what the client asks for. Must be > 0.

Response:

{
"success": true,
"data": {
"threads": [
{"thread_id": "t1", "created_at": "2026-04-01T10:00:00Z"},
{"thread_id": "t2", "created_at": "2026-04-02T09:30:00Z"}
]
}
}

GET /v1/threads/{thread_id}

Get a single thread record (metadata, not messages or state).

Path parameters:

ParameterTypeDescription
thread_idstring or integerThread identifier. An empty or whitespace-only string, or an integer below 1, returns 422.

Response:

{
"success": true,
"data": {
"thread_data": {
"thread_id": "t1",
"thread_name": "Weather in Paris",
"user_id": "user-123"
}
}
}

DELETE /v1/threads/{thread_id}

Delete a thread and its checkpointed data.

Request body:

{
"config": {}
}

The body is required; send {} or {"config": {}} when you have no extra config to pass. Keys in config are merged into the checkpointer config alongside thread_id.

Response:

{
"success": true,
"data": {"success": true, "message": "Thread deleted successfully"}
}

Deleting a thread also evicts its cached ownership entry, so the thread_id can be reused by a different user afterwards.


GET /v1/threads/{thread_id}/state

Get the saved state for a thread.

Path parameters:

ParameterTypeDescription
thread_idstring or integerThread identifier

Response:

{
"success": true,
"data": {
"state": {
"context": [
{"role": "user", "content": "Hello"},
{"role": "assistant", "content": "Hi there!"}
]
}
}
}

PUT /v1/threads/{thread_id}/state

Replace or update state fields for a thread.

Request body:

{
"state": {
"user_id": "user-456"
},
"config": {}
}
FieldTypeDescription
stateobjectState fields to merge into the current state
configobjectOptional extra config

Response:

{
"success": true,
"data": {"updated": true}
}

Use this endpoint to inject custom state fields (like user_id) before the first invoke call or to repair inconsistent state.


DELETE /v1/threads/{thread_id}/state

Clear the saved state for a thread. The next invoke call with this thread_id will start fresh.

Response:

{
"success": true,
"data": {"deleted": true}
}

GET /v1/threads/{thread_id}/messages

Get the conversation messages for a thread.

Query parameters:

ParameterTypeDefaultDescription
searchstringFree-text filter over message content
offsetintegerNumber of messages to skip. Must be >= 0.
limitinteger100Page size. Clamped server-side to a maximum of 1000. Must be > 0.

Response:

{
"success": true,
"data": {
"messages": [
{"role": "user", "content": "Hello"},
{"role": "assistant", "content": "Hi there!"}
]
}
}

POST /v1/threads/{thread_id}/messages

Store messages on a thread.

Request body:

{
"messages": [
{"role": "user", "content": "Injected message"}
],
"metadata": {"source": "import"},
"config": {}
}
FieldTypeRequiredDescription
messagesarrayyesMessages to store. An empty array returns 422.
metadataobjectnoArbitrary metadata stored with the write
configobjectnoExtra keys merged into the checkpointer config

Response:

{
"success": true,
"data": {"success": true, "message": "Messages stored"}
}

GET /v1/threads/{thread_id}/messages/{message_id}

Get a single message from a thread.

Path parameters:

ParameterTypeDescription
thread_idstring or integerThread identifier
message_idstring or integerMessage identifier. Empty or whitespace-only returns 422.

Response: the Message object.


DELETE /v1/threads/{thread_id}/messages/{message_id}

Delete a single message from a thread.

Request body:

{
"config": {}
}

Response:

{
"success": true,
"data": {"success": true, "message": "Message deleted successfully"}
}

Authentication and ownership

When auth is configured, all endpoints require a bearer token. Each route declares its own permission, listed in Route summary; the required scope is the "<resource>:<action>" string, for example checkpointer:read.

With the ownership authorization backend active (the default in MODE=production), thread_id is also checked against the thread's owner. A request for a thread owned by another user is rejected with 403 before it reaches the checkpointer. A thread_id that does not exist yet is allowed through, since it represents a new session.

See Authentication.