Skip to main content
Threads are the durable conversation context behind multi-turn agent runs. Each thread is scoped to a single computer and stores the full message history so the AI can build on what it has already done. Pass a thread’s ID into Create chat completion to continue a session.
Threads are created implicitly. Every call to POST /v1/chat/completions with a computer_id either continues an existing thread (when thread_id is passed) or creates a new one and returns its ID. Use these endpoints to list, inspect, rename, archive, or delete threads - you rarely need to create them by hand.

Endpoints

MethodPathPurpose
GET/api/chat/threads?desktopId={id}List threads for a computer.
POST/api/chat/threadsCreate an empty thread.
GET/api/chat/threads/{id}Get a thread with full message history.
PATCH/api/chat/threads/{id}Update title, replace messages, archive, or unarchive.
DELETE/api/chat/threads/{id}Delete a thread.
POST/api/chat/threads/{id}/titleAuto-generate a title with Claude Haiku.
Base URL: https://www.orgo.ai Auth: Authorization: Bearer sk_live_... on every request.

List threads

GET /api/chat/threads?desktopId={desktop_uuid}
Returns every thread the authenticated user owns for the given computer, including full message history.

Query parameters

desktopId
string
required
UUID of the computer (the id field from Create computer). Threads are scoped per-computer; to list across computers, call this endpoint once per computer.

Response

threads
array
Array of thread objects, most recent first.

Example

import requests

r = requests.get(
    "https://www.orgo.ai/api/chat/threads",
    params={"desktopId": "a3bb189e-8bf9-3888-9912-ace4e6543002"},
    headers={"Authorization": "Bearer sk_live_a3bb189e8bf93888"},
)
for t in r.json()["threads"]:
    print(t["id"], t.get("title", "(untitled)"))
{
  "threads": [
    {
      "id": "thr_01HX8W3Z6PN2Q1MRT7YV9K4BFA",
      "remoteId": "thr_01HX8W3Z6PN2Q1MRT7YV9K4BFA",
      "status": "active",
      "title": "GitHub search",
      "messages": [
        { "role": "user", "content": "Open github.com" },
        { "role": "assistant", "content": "Done - GitHub is open." }
      ],
      "updated_at": "2026-04-20T14:22:05.123Z"
    }
  ]
}

Create thread

POST /api/chat/threads
Creates an empty thread bound to a computer. Only needed when you want a thread ID before making the first completion - otherwise, omit this call and let POST /v1/chat/completions create one for you.

Request

desktopId
string
required
UUID of the computer to attach the thread to.
localId
string
Optional client-side identifier. Echoed back as externalId in the response so clients can reconcile local and remote threads.

Response

Returns 201 Created.
remoteId
string
The new thread’s UUID. Use this as thread_id in subsequent chat completions.
externalId
string
Mirror of localId from the request. Omitted if not supplied.

Example

import requests

r = requests.post(
    "https://www.orgo.ai/api/chat/threads",
    headers={"Authorization": "Bearer sk_live_a3bb189e8bf93888"},
    json={"desktopId": "a3bb189e-8bf9-3888-9912-ace4e6543002"},
)
thread_id = r.json()["remoteId"]

Get thread

GET /api/chat/threads/{id}
Fetches a single thread with its full message history. 403 if the thread belongs to another user; 404 if it does not exist.

Response

remoteId
string
Thread UUID.
status
string
active or archived.
title
string
Title if set, otherwise omitted.
messages
array
Full message history in chronological order.

Update thread

PATCH /api/chat/threads/{id}
Updates title, replaces messages, or toggles archive state. Archive takes precedence over other fields when both are present.

Request

title
string
New human-readable title.
messages
array
Replaces the entire stored message history with this array. Use with care - this is a full overwrite, not an append.
archive
boolean
Set to true to archive. Archived threads are hidden from default list views but remain fetchable by ID.
unarchive
boolean
Set to true to restore an archived thread.

Response

remoteId
string
Thread UUID.
status
string
Updated status (active or archived).
title
string
Updated title if set.

Example

curl -X PATCH https://www.orgo.ai/api/chat/threads/thr_01HX8W3Z6PN2Q1MRT7YV9K4BFA \
  -H "Authorization: Bearer sk_live_a3bb189e8bf93888" \
  -H "Content-Type: application/json" \
  -d '{"title": "GitHub research session"}'

Delete thread

DELETE /api/chat/threads/{id}
Permanently deletes the thread and its message history. Prefer PATCH with archive: true if you might need the conversation back.

Response

{ "success": true }

Generate title

POST /api/chat/threads/{id}/title
Generates a short (3–6 word) title from the first few messages using Claude Haiku and saves it to the thread. Returns an assistant-ui-compatible text stream rather than JSON.

Request

messages
array
required
The messages to summarize. Only the first three are considered. Each message is { role, content } where content may be a string or an array of { type: "text", text } blocks.

Response

text/plain stream in the assistant-ui format:
0:"GitHub search session"
The generated title is also persisted to the thread, so a subsequent GET will include it in the title field.

Using threads with completions

Threads compose with chat completions - you almost never manage them directly in production code:
# First turn - server creates the thread, returns its ID
first = client.chat.completions.create(
    model="claude-sonnet-4.6",
    messages=[{"role": "user", "content": "Open Chrome and go to github.com"}],
    extra_body={"computer_id": computer_id},
)
thread_id = first.orgo["thread_id"]

# Later turn - agent picks up where it left off
client.chat.completions.create(
    model="claude-sonnet-4.6",
    messages=[{"role": "user", "content": "Search for 'orgo'"}],
    extra_body={"computer_id": computer_id, "thread_id": thread_id},
)

Errors

StatusMeaning
400desktopId or messages missing from the request body.
401Missing or invalid Authorization header.
403The thread exists but belongs to a different user.
404Thread does not exist.
500Unexpected server error. Retry with backoff.
Error responses are JSON with a single error field:
{ "error": "desktopId is required" }