Reference
API reference.
Every endpoint, generated from the OpenAPI spec we ship. Set your workspace ID and key in the Credentials panel (top right) and the read-only examples run against your own workspace as you read.
The base is https://app.threa.io, versioned under /api/v1
and scoped to a workspace: /api/v1/workspaces/{workspaceId}/….
Every request authenticates with Authorization: Bearer <key>.
Error shapes, paging, idempotency, and rate limits live in
Operations.
Identity
Confirm who a key belongs to and list the bots you own.
/api/v1/workspaces/{workspaceId}/me Get the authenticated principal
Returns a discriminated union describing the authenticated principal — either the API-key owner (`kind: "user"`) or the bot whose key is in use (`kind: "bot"`). Used by clients (e.g. the OpenClaw channel plugin) to verify their key and discover their identity after pairing.
Scope none — any valid key
Example
curl "/api/v1/workspaces/{workspaceId}/me" \
-H "Authorization: Bearer " Response 200
dataobjectrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource
/api/v1/workspaces/{workspaceId}/me/bots List my personal bots
For user-scoped keys: lists the authenticated user's personal bots, optionally filtered by trait. Used by the frontend to enumerate quick-switcher commands. Bot-scoped keys receive 403.
Scope none — any valid key
Query parameters
traitsstringExample
curl "/api/v1/workspaces/{workspaceId}/me/bots" \
-H "Authorization: Bearer " Response 200
dataobject[]requiredidstringrequiredworkspaceIdstringrequiredtraitsstring[]requiredslugstring | nullrequirednamestringrequireddescriptionstring | nullrequiredavatarEmojistring | nullrequiredavatarUrlstring | nullrequiredarchivedAtstring | nullrequiredcreatedAtstringrequiredupdatedAtstringrequiredtypestringrequiredownerUserIdstringrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource
Streams
List and inspect streams (channels, scratchpads, threads)
/api/v1/workspaces/{workspaceId}/streams List streams
List streams accessible to this API key, with optional type and text filters.
Scope streams:read
Query parameters
typeanyrequiredquerystringafterstringlimitintegerrequiredExample
curl "/api/v1/workspaces/{workspaceId}/streams?type=channel&limit=20" \
-H "Authorization: Bearer " Response 200
dataobject[]requiredidstringrequiredtypestringrequireddisplayNamestringrequiredslugstringdescriptionstringvisibilitystringrequiredparentStreamIdstringrootStreamIdstringparentMessageIdstringcreatedAtstringrequiredarchivedAtstringhasMorebooleanrequiredcursorstring | nullrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource
/api/v1/workspaces/{workspaceId}/streams/{streamId} Get a stream
Scope streams:read
Path parameters
streamIdstringrequiredStream ID (prefixed ULID)
Example
curl "/api/v1/workspaces//streams/STREAM_ID" \
-H "Authorization: Bearer " Response 200
dataobjectrequiredidstringrequiredtypestringrequireddisplayNamestringrequiredslugstringdescriptionstringvisibilitystringrequiredparentStreamIdstringrootStreamIdstringparentMessageIdstringcreatedAtstringrequiredarchivedAtstringStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource404Resource not found
/api/v1/workspaces/{workspaceId}/streams/{streamId}/members List stream members
Scope streams:read
Path parameters
streamIdstringrequiredStream ID (prefixed ULID)
Query parameters
afterstringlimitintegerrequiredExample
curl "/api/v1/workspaces//streams/STREAM_ID/members?limit=50" \
-H "Authorization: Bearer " Response 200
dataobject[]requireduserIdstringrequirednamestringrequiredslugstringrequiredavatarUrlstringjoinedAtstringrequiredhasMorebooleanrequiredcursorstring | nullrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource
Messages
Read, send, update, and delete messages
/api/v1/workspaces/{workspaceId}/messages/search Search messages
Full-text and optional semantic search across accessible streams.
Scope messages:search
Request body
querystringrequiredsemanticbooleanrequiredexactbooleanrequiredstreamsstring[]fromstringtypestring[]beforestringafterstringlimitintegerrequiredExample
curl -X POST /api/v1/workspaces/{workspaceId}/messages/search \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"query": "auth",
"semantic": true,
"exact": false,
"limit": 5
}' Response 200
dataobject[]requiredidstringrequiredstreamIdstringrequiredsequencestringrequiredNumeric sequence as string
contentstringrequiredauthorIdstringrequiredauthorTypestringrequiredauthorDisplayNamestringreplyCountintegerrequiredmetadataobjectrequiredExternal references attached by the sender. Always present; empty when unset.
editedAtstringcreatedAtstringrequiredranknumberrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource
/api/v1/workspaces/{workspaceId}/streams/{streamId}/messages List messages in a stream
Cursor-paginated message list. Use `before` or `after` sequence numbers.
Scope messages:read
Path parameters
streamIdstringrequiredStream ID (prefixed ULID)
Query parameters
beforestringafterstringlimitintegerrequiredExample
curl "/api/v1/workspaces//streams/STREAM_ID/messages?limit=50" \
-H "Authorization: Bearer " Response 200
dataobject[]requiredidstringrequiredstreamIdstringrequiredsequencestringrequiredNumeric sequence as string
authorIdstringrequiredauthorTypestringrequiredauthorDisplayNamestringcontentstringrequiredreplyCountintegerrequiredthreadStreamIdstringclientMessageIdstringsentViastringPresent when message was sent via API on behalf of a user
metadataobjectrequiredExternal references attached by the sender. Always present; empty when unset.
attachmentsobject[]idstringrequiredfilenamestringrequiredmimeTypestringrequiredsizeBytesintegerrequiredprocessingStatusstringwidthintegerheightintegereditedAtstringcreatedAtstringrequiredhasMorebooleanrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource
/api/v1/workspaces/{workspaceId}/streams/{streamId}/messages Send a message
Send a message. Workspace-scoped keys send as a bot; user-scoped keys send on behalf of the key owner.
Scope messages:write
Path parameters
streamIdstringrequiredStream ID (prefixed ULID)
Request body
contentstringrequiredclientMessageIdstringmetadataobjectExample
curl -X POST /api/v1/workspaces//streams/STREAM_ID/messages \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"content": "string"
}' Response 201
dataobjectrequiredidstringrequiredstreamIdstringrequiredsequencestringrequiredNumeric sequence as string
authorIdstringrequiredauthorTypestringrequiredauthorDisplayNamestringcontentstringrequiredreplyCountintegerrequiredthreadStreamIdstringclientMessageIdstringsentViastringPresent when message was sent via API on behalf of a user
metadataobjectrequiredExternal references attached by the sender. Always present; empty when unset.
attachmentsobject[]idstringrequiredfilenamestringrequiredmimeTypestringrequiredsizeBytesintegerrequiredprocessingStatusstringwidthintegerheightintegereditedAtstringcreatedAtstringrequiredStatus codes
201Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource
/api/v1/workspaces/{workspaceId}/messages/find-by-metadata Find messages by metadata
Find non-deleted messages whose `metadata` contains all the given key/value pairs (AND-containment). Useful for dedup flows — e.g. 'has a message already been posted for this GitHub PR event?'.
Scope messages:read
Request body
metadataobjectrequiredstreamIdstringlimitintegerrequiredExample
curl -X POST /api/v1/workspaces//messages/find-by-metadata \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"metadata": {},
"limit": 20
}' Response 200
dataobject[]requiredidstringrequiredstreamIdstringrequiredsequencestringrequiredNumeric sequence as string
authorIdstringrequiredauthorTypestringrequiredauthorDisplayNamestringcontentstringrequiredreplyCountintegerrequiredthreadStreamIdstringclientMessageIdstringsentViastringPresent when message was sent via API on behalf of a user
metadataobjectrequiredExternal references attached by the sender. Always present; empty when unset.
attachmentsobject[]idstringrequiredfilenamestringrequiredmimeTypestringrequiredsizeBytesintegerrequiredprocessingStatusstringwidthintegerheightintegereditedAtstringcreatedAtstringrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource
/api/v1/workspaces/{workspaceId}/messages/{messageId} Update a message
Update a message you previously sent via API.
Scope messages:write
Path parameters
messageIdstringrequiredMessage ID (prefixed ULID)
Request body
contentstringrequiredExample
curl -X PATCH /api/v1/workspaces//messages/MESSAGE_ID \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"content": "string"
}' Response 200
dataobjectrequiredidstringrequiredstreamIdstringrequiredsequencestringrequiredNumeric sequence as string
authorIdstringrequiredauthorTypestringrequiredauthorDisplayNamestringcontentstringrequiredreplyCountintegerrequiredthreadStreamIdstringclientMessageIdstringsentViastringPresent when message was sent via API on behalf of a user
metadataobjectrequiredExternal references attached by the sender. Always present; empty when unset.
attachmentsobject[]idstringrequiredfilenamestringrequiredmimeTypestringrequiredsizeBytesintegerrequiredprocessingStatusstringwidthintegerheightintegereditedAtstringcreatedAtstringrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource404Resource not found
/api/v1/workspaces/{workspaceId}/messages/{messageId} Delete a message
Delete a message you previously sent via API.
Scope messages:write
Path parameters
messageIdstringrequiredMessage ID (prefixed ULID)
Example
curl -X DELETE /api/v1/workspaces//messages/MESSAGE_ID \
-H "Authorization: Bearer " Status codes
204No content400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource404Resource not found
Memos
Search preserved workspace knowledge and inspect memo provenance
/api/v1/workspaces/{workspaceId}/memos/search Search memos
Search preserved workspace memos with semantic, exact, or recent-first retrieval.
Scope memos:read
Request body
querystringrequiredexactbooleanstreamsstring[]memoTypestring[]knowledgeTypestring[]tagsstring[]beforestringafterstringlimitintegerrequiredExample
curl -X POST /api/v1/workspaces/{workspaceId}/memos/search \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"query": "auth",
"limit": 5
}' Response 200
dataobject[]requiredmemoobjectrequiredidstringrequiredworkspaceIdstringrequiredmemoTypestringrequiredsourceMessageIdstring | nullrequiredsourceConversationIdstring | nullrequiredtitlestringrequiredabstractstringrequiredkeyPointsstring[]requiredsourceMessageIdsstring[]requiredparticipantIdsstring[]requiredknowledgeTypestringrequiredtagsstring[]requiredparentMemoIdstring | nullrequiredstatusstringrequiredversionintegerrequiredrevisionReasonstring | nullrequiredcreatedAtstringrequiredupdatedAtstringrequiredarchivedAtstring | nullrequireddistancenumberrequiredsourceStreamobject | nullrequiredrootStreamobject | nullrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource
/api/v1/workspaces/{workspaceId}/memos/{memoId} Get a memo
Retrieve a memo together with source stream and source message provenance.
Scope memos:read
Path parameters
memoIdstringrequiredMemo ID (prefixed ULID)
Example
curl "/api/v1/workspaces//memos/MEMO_ID" \
-H "Authorization: Bearer " Response 200
dataobjectrequiredmemoobjectrequiredidstringrequiredworkspaceIdstringrequiredmemoTypestringrequiredsourceMessageIdstring | nullrequiredsourceConversationIdstring | nullrequiredtitlestringrequiredabstractstringrequiredkeyPointsstring[]requiredsourceMessageIdsstring[]requiredparticipantIdsstring[]requiredknowledgeTypestringrequiredtagsstring[]requiredparentMemoIdstring | nullrequiredstatusstringrequiredversionintegerrequiredrevisionReasonstring | nullrequiredcreatedAtstringrequiredupdatedAtstringrequiredarchivedAtstring | nullrequireddistancenumberrequiredsourceStreamobject | nullrequiredrootStreamobject | nullrequiredsourceMessagesobject[]requiredidstringrequiredstreamIdstringrequiredstreamNamestringrequiredauthorIdstringrequiredauthorTypestringrequiredauthorNamestringrequiredcontentstringrequiredcreatedAtstringrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource404Resource not found
Attachments
Search attachments, inspect extracted content, and fetch download URLs
/api/v1/workspaces/{workspaceId}/attachments Upload an attachment
Upload a file as multipart/form-data using field `file`. Include the returned attachment id in message markdown as `attachment:<id>` to attach it to a message.
Scope attachments:write
Example
curl -X POST /api/v1/workspaces//attachments \
-H "Authorization: Bearer " Response 201
dataobjectrequiredidstringrequiredfilenamestringrequiredmimeTypestringrequiredsizeBytesintegerrequiredprocessingStatusstringrequiredcreatedAtstringrequiredStatus codes
201Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource
/api/v1/workspaces/{workspaceId}/attachments/search Search attachments
Search accessible attachments by filename or extracted content.
Scope attachments:read
Request body
querystringrequiredstreamsstring[]contentTypesstring[]limitintegerrequiredExample
curl -X POST /api/v1/workspaces/{workspaceId}/attachments/search \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"query": "auth",
"limit": 5
}' Response 200
dataobject[]requiredidstringrequiredfilenamestringrequiredmimeTypestringrequiredcontentTypestring | nullrequiredsummarystring | nullrequiredstreamIdstringmessageIdstringcreatedAtstringrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource
/api/v1/workspaces/{workspaceId}/attachments/{attachmentId} Get an attachment
Retrieve attachment metadata and extracted content for an accessible attachment.
Scope attachments:read
Path parameters
attachmentIdstringrequiredAttachment ID (prefixed ULID)
Example
curl "/api/v1/workspaces//attachments/ATTACHMENT_ID" \
-H "Authorization: Bearer " Response 200
dataobjectrequiredidstringrequiredfilenamestringrequiredmimeTypestringrequiredsizeBytesintegerrequiredprocessingStatusstringrequiredcreatedAtstringrequiredextractionobject | nullrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource404Resource not found
/api/v1/workspaces/{workspaceId}/attachments/{attachmentId}/url Get an attachment download URL
Create a short-lived signed URL for an accessible attachment.
Scope attachments:read
Path parameters
attachmentIdstringrequiredAttachment ID (prefixed ULID)
Example
curl "/api/v1/workspaces//attachments/ATTACHMENT_ID/url" \
-H "Authorization: Bearer " Response 200
dataobjectrequiredurlstringrequiredexpiresInintegerrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource404Resource not found
Users
List workspace users
/api/v1/workspaces/{workspaceId}/users List workspace users
List users in the workspace with optional text search and cursor pagination.
Scope users:read
Query parameters
querystringafterstringlimitintegerrequiredExample
curl "/api/v1/workspaces/{workspaceId}/users?limit=20" \
-H "Authorization: Bearer " Response 200
dataobject[]requiredidstringrequirednamestringrequiredslugstringrequiredemailstringrequiredavatarUrlstringrolestringrequiredhasMorebooleanrequiredcursorstring | nullrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource
Bot runtimes
Register a runtime and keep its presence alive so it can be assigned work.
/api/v1/workspaces/{workspaceId}/bot-runtime/presence Heartbeat bot runtime presence
Scope bot-runtime:write
Request body
runtimeKindstringrequiredinstanceIdstringrequiredruntimeSessionIdstringdisplayNamestringstatusstringrequiredacceptingInvocationsbooleanrequiredcapabilitiesobjectrequiredstatusTextstringpublicKeystringpublicKeyIdstringExample
curl -X POST /api/v1/workspaces//bot-runtime/presence \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"runtimeKind": "pi-local",
"instanceId": "string",
"status": "available",
"acceptingInvocations": false,
"capabilities": {}
}' Response 200
dataobjectrequiredidstringrequiredworkspaceIdstringrequiredbotIdstringrequiredruntimeKindstringrequiredinstanceIdstringrequireddisplayNamestring | nullrequiredstatusstringrequiredacceptingInvocationsbooleanrequiredcapabilitiesobjectrequiredstatusTextstring | nullrequiredlastSeenAtstringrequiredcreatedAtstringrequiredupdatedAtstringrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource
/api/v1/workspaces/{workspaceId}/bot-runtime/sessions Create or link a bot runtime session
Scope bot-runtime:write
Request body
runtimeKindstringrequiredinstanceIdstringrequiredruntimeSessionIdstringrequireddisplayNamestringrequiredlocalCwdstringExample
curl -X POST /api/v1/workspaces//bot-runtime/sessions \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"runtimeKind": "string",
"instanceId": "string",
"runtimeSessionId": "string",
"displayName": "string"
}' Response 200
dataobjectrequiredlinkIdstringrequiredrootStreamIdstringrequiredactiveStreamIdstringrequiredruntimeSessionIdstringrequiredstreamUrlPathstringrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource
/api/v1/workspaces/{workspaceId}/bot-runtime/sessions/rename Rename the scratchpad linked to a bot runtime session
Scope bot-runtime:write
Request body
instanceIdstringrequiredruntimeSessionIdstringrequireddisplayNamestringrequiredExample
curl -X POST /api/v1/workspaces//bot-runtime/sessions/rename \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"instanceId": "string",
"runtimeSessionId": "string",
"displayName": "string"
}' Response 200
dataobjectrequiredlinkIdstringrequiredrootStreamIdstringrequiredactiveStreamIdstringrequiredruntimeSessionIdstringrequiredstreamUrlPathstringrequireddisplayNamestringrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource404Resource not found
Bot invocations
Claim, renew, step through, complete, or fail the work a bot is summoned to do.
/api/v1/workspaces/{workspaceId}/bot-invocations/claim Claim one pending bot invocation
Scope bot-invocations:write
Request body
runtimeKindstringrequiredinstanceIdstringrequiredruntimeSessionIdstringsupportedCapabilitiesstring[]requiredclaimTtlSecondsintegerrequiredExample
curl -X POST /api/v1/workspaces//bot-invocations/claim \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"runtimeKind": "pi-local",
"instanceId": "string",
"supportedCapabilities": [
"mentionable"
],
"claimTtlSeconds": 60
}' Response 200
dataobject | nullrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource
/api/v1/workspaces/{workspaceId}/bot-invocations/{invocationId}/renew Renew a claimed bot invocation
Scope bot-invocations:write
Path parameters
invocationIdstringrequiredInvocation ID
Request body
instanceIdstringrequiredclaimTokenstringrequiredclaimTtlSecondsintegerrequiredExample
curl -X POST /api/v1/workspaces//bot-invocations/INVOCATION_ID/renew \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"instanceId": "string",
"claimToken": "string",
"claimTtlSeconds": 60
}' Response 200
dataobjectrequiredinvocationIdstringrequiredstatusstringrequiredclaimExpiresAtstring | nullrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource404Resource not found
/api/v1/workspaces/{workspaceId}/bot-invocations/{invocationId}/steps Record a bot invocation trace step
Scope bot-invocations:write
Path parameters
invocationIdstringrequiredInvocation ID
Request body
instanceIdstringrequiredclaimTokenstringrequiredstepTypestringrequiredcontentstringrequiredstatusTextstringExample
curl -X POST /api/v1/workspaces//bot-invocations/INVOCATION_ID/steps \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"instanceId": "string",
"claimToken": "string",
"stepType": "context_received",
"content": "string"
}' Response 200
dataobjectrequiredinvocationIdstringrequiredsessionIdstringrequiredstepIdstringrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource404Resource not found
/api/v1/workspaces/{workspaceId}/bot-invocations/{invocationId}/complete Complete a claimed bot invocation
Scope bot-invocations:write
Path parameters
invocationIdstringrequiredInvocation ID
Request body
instanceIdstringrequiredclaimTokenstringrequiredfinalMessageMarkdownstringnoResponsebooleanmetadataobjectExample
curl -X POST /api/v1/workspaces//bot-invocations/INVOCATION_ID/complete \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"instanceId": "string",
"claimToken": "string"
}' Response 200
dataobjectrequiredinvocationIdstringrequiredmessageobject | nullrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource404Resource not found
/api/v1/workspaces/{workspaceId}/bot-invocations/{invocationId}/fail Fail a claimed bot invocation
Scope bot-invocations:write
Path parameters
invocationIdstringrequiredInvocation ID
Request body
instanceIdstringrequiredclaimTokenstringrequirederrorMessagestringrequiredExample
curl -X POST /api/v1/workspaces//bot-invocations/INVOCATION_ID/fail \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"instanceId": "string",
"claimToken": "string",
"errorMessage": "string"
}' Response 200
dataobjectrequiredinvocationIdstringrequiredstatusstringrequiredStatus codes
200Successful response400Validation error401Missing or invalid API key403Insufficient permissions or inaccessible resource404Resource not found