Threa Developers

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, under the stable /api/v1 prefix and scoped to a workspace: /api/v1/workspaces/{workspaceId}/…. The v1 segment does not move as the API evolves; versions are dated and selected with the Threa-Version header, described in Versioning. 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.

GET /api/v1/workspaces/{workspaceId}/me

Get the authenticated principal

Returns a discriminated union describing the authenticated principal: the API-key owner (`kind: "user"`) or the bot whose key is in use (`kind: "bot"`). Also reports the key's API version pin, the version this request resolved to, and the supported versions. Used by clients (e.g. the OpenClaw channel plugin) to verify their key and discover their identity after pairing.

Scope none · any valid key

Response 200

dataobjectrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
GET /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

traitsstring
one of: mentionable, active-scratchpad

Response 200

dataobject[]required
idstringrequired
workspaceIdstringrequired
traitsstring[]required
slugstring | nullrequired
namestringrequired
descriptionstring | nullrequired
avatarEmojistring | nullrequired
avatarUrlstring | nullrequired
archivedAtstring | nullrequired
createdAtstringrequired
date-time
updatedAtstringrequired
date-time
typestringrequired
ownerUserIdstringrequired
readsAsOwnerbooleanrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource

Streams

List and inspect accessible streams

Recipes Notify a stream from CI →

GET /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

typeanyrequired
querystring
afterstring
limitinteger
1–200 · default 50
includeArchivedstring
one of: true, false

Response 200

dataobject[]required
idstringrequired
typestringrequired
one of: scratchpad, channel, dm, thread, system, aside
displayNamestringrequired
slugstring
descriptionstring
visibilitystringrequired
memoryModestringrequired
one of: auto, off

GAM memory automation gate: 'auto' extracts memos, 'off' disables it

parentStreamIdstring
rootStreamIdstring
anchorIdstring

Canonical id of the timeline item a thread anchors on. The prefix is the kind: 'msg_…' for a message, 'event_…' for a card. Present on threads only.

createdAtstringrequired
date-time
archivedAtstring
date-time
hasMorebooleanrequired
cursorstring | nullrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
GET /api/v1/workspaces/{workspaceId}/streams/{streamId}

Get a stream

Scope streams:read

Path parameters

streamIdstringrequired

Stream ID (prefixed ULID)

Response 200

dataobjectrequired
idstringrequired
typestringrequired
one of: scratchpad, channel, dm, thread, system, aside
displayNamestringrequired
slugstring
descriptionstring
visibilitystringrequired
memoryModestringrequired
one of: auto, off

GAM memory automation gate: 'auto' extracts memos, 'off' disables it

parentStreamIdstring
rootStreamIdstring
anchorIdstring

Canonical id of the timeline item a thread anchors on. The prefix is the kind: 'msg_…' for a message, 'event_…' for a card. Present on threads only.

createdAtstringrequired
date-time
archivedAtstring
date-time

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
PATCH /api/v1/workspaces/{workspaceId}/streams/{streamId}

Set a stream's description

Set the stream's description from markdown (parsed to rich text, same as message content). Send an empty string to clear it. User-scoped keys attribute the change to the key owner; workspace-scoped keys to the bot.

Scope streams:write

Path parameters

streamIdstringrequired

Stream ID (prefixed ULID)

Request body

descriptionstringrequired
max length 10000

Response 200

dataobjectrequired
idstringrequired
typestringrequired
one of: scratchpad, channel, dm, thread, system, aside
displayNamestringrequired
slugstring
descriptionstring
visibilitystringrequired
memoryModestringrequired
one of: auto, off

GAM memory automation gate: 'auto' extracts memos, 'off' disables it

parentStreamIdstring
rootStreamIdstring
anchorIdstring

Canonical id of the timeline item a thread anchors on. The prefix is the kind: 'msg_…' for a message, 'event_…' for a card. Present on threads only.

createdAtstringrequired
date-time
archivedAtstring
date-time

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
GET /api/v1/workspaces/{workspaceId}/streams/{streamId}/members

List stream members

Scope streams:read

Path parameters

streamIdstringrequired

Stream ID (prefixed ULID)

Query parameters

afterstring
limitinteger
1–200 · default 50

Response 200

dataobject[]required
userIdstringrequired
namestringrequired
slugstringrequired
avatarUrlstring
joinedAtstringrequired
date-time
hasMorebooleanrequired
cursorstring | nullrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource

Messages

Read, send, update, and delete messages

Recipes Notify a stream from CI →Mirror a search into your own tool →

POST /api/v1/workspaces/{workspaceId}/messages/search

Search messages

Full-text and optional semantic search across accessible streams.

Scope messages:search

Request body

querystringrequired
min length 1
semanticbooleanrequired
default false
exactbooleanrequired
default false
streamsstring[]
fromstring
typestring[]
beforestring
date-time
afterstring
date-time
limitintegerrequired
1–50 · default 20

Response 200

dataobject[]required
idstringrequired
streamIdstringrequired
sequencestringrequired

Numeric sequence as string

contentstringrequired
authorIdstringrequired
authorTypestringrequired
one of: user, persona, system, bot
authorDisplayNamestring
replyCountintegerrequired
-9007199254740991–9007199254740991
metadataobjectrequired

External references attached by the sender. Always present; empty when unset.

editedAtstring
date-time
createdAtstringrequired
date-time
ranknumberrequired
slotsobjectrequired

Hydration for shared-message pointers in the returned messages, keyed by the pointer's reference: `shared:<messageId>` (legacy, current revision), `shared:<messageId>@<version>` (pinned revision) or `shared:<messageId>@<version>:<from>-<to>` (pinned span). Always present; empty when no message references a shared source.

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
GET /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

streamIdstringrequired

Stream ID (prefixed ULID)

Query parameters

beforestring
afterstring
limitinteger
1–100 · default 50

Response 200

dataobject[]required
idstringrequired
streamIdstringrequired
sequencestringrequired

Numeric sequence as string

authorIdstringrequired
authorTypestringrequired
one of: user, persona, system, bot
authorDisplayNamestring
contentstringrequired
replyCountintegerrequired
-9007199254740991–9007199254740991
threadStreamIdstring
clientMessageIdstring
sentViastring

Present when message was sent via API on behalf of a user

metadataobjectrequired

External references attached by the sender. Always present; empty when unset.

attachmentsobject[]
idstringrequired
filenamestringrequired
mimeTypestringrequired
sizeBytesintegerrequired
-9007199254740991–9007199254740991
processingStatusstring
one of: pending, processing, completed, failed, skipped
widthinteger
-9007199254740991–9007199254740991
heightinteger
-9007199254740991–9007199254740991
revisionintegerrequired
≤ 9007199254740991

1 for the original body, +1 per edit

editedAtstring
date-time
createdAtstringrequired
date-time
hasMorebooleanrequired
slotsobjectrequired

Hydration for shared-message pointers in the returned messages, keyed by the pointer's reference: `shared:<messageId>` (legacy, current revision), `shared:<messageId>@<version>` (pinned revision) or `shared:<messageId>@<version>:<from>-<to>` (pinned span). Always present; empty when no message references a shared source.

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
POST /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. Optionally declare the message's conversation via `conversation`: `{intent: "new"}` starts a fresh conversation, `{intent: "existing", conversationId}` posts into one under the same root stream.

Scope messages:write

Path parameters

streamIdstringrequired

Stream ID (prefixed ULID)

Request body

contentstringrequired
min length 1
clientMessageIdstring
max length 128
metadataobject
conversationobject

Response 201

dataobjectrequired
idstringrequired
streamIdstringrequired
sequencestringrequired

Numeric sequence as string

authorIdstringrequired
authorTypestringrequired
one of: user, persona, system, bot
authorDisplayNamestring
contentstringrequired
replyCountintegerrequired
-9007199254740991–9007199254740991
threadStreamIdstring
clientMessageIdstring
sentViastring

Present when message was sent via API on behalf of a user

metadataobjectrequired

External references attached by the sender. Always present; empty when unset.

attachmentsobject[]
idstringrequired
filenamestringrequired
mimeTypestringrequired
sizeBytesintegerrequired
-9007199254740991–9007199254740991
processingStatusstring
one of: pending, processing, completed, failed, skipped
widthinteger
-9007199254740991–9007199254740991
heightinteger
-9007199254740991–9007199254740991
revisionintegerrequired
≤ 9007199254740991

1 for the original body, +1 per edit

editedAtstring
date-time
createdAtstringrequired
date-time
conversationIdstring

The conversation the message was assigned to; present when the request declared a `conversation`.

slotsobjectrequired

Hydration for shared-message pointers in the returned messages, keyed by the pointer's reference: `shared:<messageId>` (legacy, current revision), `shared:<messageId>@<version>` (pinned revision) or `shared:<messageId>@<version>:<from>-<to>` (pinned span). Always present; empty when no message references a shared source.

Status codes

  • 201 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
POST /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

metadataobjectrequired
streamIdstring
min length 1
limitintegerrequired
1–100 · default 20

Response 200

dataobject[]required
idstringrequired
streamIdstringrequired
sequencestringrequired

Numeric sequence as string

authorIdstringrequired
authorTypestringrequired
one of: user, persona, system, bot
authorDisplayNamestring
contentstringrequired
replyCountintegerrequired
-9007199254740991–9007199254740991
threadStreamIdstring
clientMessageIdstring
sentViastring

Present when message was sent via API on behalf of a user

metadataobjectrequired

External references attached by the sender. Always present; empty when unset.

attachmentsobject[]
idstringrequired
filenamestringrequired
mimeTypestringrequired
sizeBytesintegerrequired
-9007199254740991–9007199254740991
processingStatusstring
one of: pending, processing, completed, failed, skipped
widthinteger
-9007199254740991–9007199254740991
heightinteger
-9007199254740991–9007199254740991
revisionintegerrequired
≤ 9007199254740991

1 for the original body, +1 per edit

editedAtstring
date-time
createdAtstringrequired
date-time
slotsobjectrequired

Hydration for shared-message pointers in the returned messages, keyed by the pointer's reference: `shared:<messageId>` (legacy, current revision), `shared:<messageId>@<version>` (pinned revision) or `shared:<messageId>@<version>:<from>-<to>` (pinned span). Always present; empty when no message references a shared source.

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
PATCH /api/v1/workspaces/{workspaceId}/messages/{messageId}

Update a message

Update a message you previously sent via API.

Scope messages:write

Path parameters

messageIdstringrequired

Message ID (prefixed ULID)

Request body

contentstringrequired
min length 1

Response 200

dataobjectrequired
idstringrequired
streamIdstringrequired
sequencestringrequired

Numeric sequence as string

authorIdstringrequired
authorTypestringrequired
one of: user, persona, system, bot
authorDisplayNamestring
contentstringrequired
replyCountintegerrequired
-9007199254740991–9007199254740991
threadStreamIdstring
clientMessageIdstring
sentViastring

Present when message was sent via API on behalf of a user

metadataobjectrequired

External references attached by the sender. Always present; empty when unset.

attachmentsobject[]
idstringrequired
filenamestringrequired
mimeTypestringrequired
sizeBytesintegerrequired
-9007199254740991–9007199254740991
processingStatusstring
one of: pending, processing, completed, failed, skipped
widthinteger
-9007199254740991–9007199254740991
heightinteger
-9007199254740991–9007199254740991
revisionintegerrequired
≤ 9007199254740991

1 for the original body, +1 per edit

editedAtstring
date-time
createdAtstringrequired
date-time
slotsobjectrequired

Hydration for shared-message pointers in the returned messages, keyed by the pointer's reference: `shared:<messageId>` (legacy, current revision), `shared:<messageId>@<version>` (pinned revision) or `shared:<messageId>@<version>:<from>-<to>` (pinned span). Always present; empty when no message references a shared source.

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
DELETE /api/v1/workspaces/{workspaceId}/messages/{messageId}

Delete a message

Delete a message you previously sent via API.

Scope messages:write

Path parameters

messageIdstringrequired

Message ID (prefixed ULID)

Status codes

  • 204 No content
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found

Conversations

Browse first-class conversations — topic-level groupings of messages under a root stream and its threads

GET /api/v1/workspaces/{workspaceId}/conversations

List conversations

Cursor-paginated conversation feed across accessible streams, newest activity first. Filter with `streamId` (scopes to that stream's root and its threads) and `status`.

Scope messages:read

Query parameters

streamIdstring
statusstring
one of: active, stalled, resolved
afterstring
limitinteger
1–100 · default 50

Response 200

dataobject[]required
idstringrequired
streamIdstringrequired

Anchor stream the conversation lives in (may be a thread)

rootStreamIdstringrequired

Effective root of the anchor — the stream whose access governs the conversation

topicSummarystring | nullrequired
summarystring | nullrequired
statusstringrequired
one of: active, stalled, resolved
messageCountintegerrequired
-9007199254740991–9007199254740991

Number of primary member messages

participantIdsstring[]required

Distinct author ids of the member messages

lastActivityAtstringrequired
date-time
createdAtstringrequired
date-time
updatedAtstringrequired
date-time
hasMorebooleanrequired
cursorstring | nullrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
GET /api/v1/workspaces/{workspaceId}/conversations/{conversationId}

Get a conversation

Scope messages:read

Path parameters

conversationIdstringrequired

Conversation ID (prefixed ULID)

Response 200

dataobjectrequired
idstringrequired
streamIdstringrequired

Anchor stream the conversation lives in (may be a thread)

rootStreamIdstringrequired

Effective root of the anchor — the stream whose access governs the conversation

topicSummarystring | nullrequired
summarystring | nullrequired
statusstringrequired
one of: active, stalled, resolved
messageCountintegerrequired
-9007199254740991–9007199254740991

Number of primary member messages

participantIdsstring[]required

Distinct author ids of the member messages

lastActivityAtstringrequired
date-time
createdAtstringrequired
date-time
updatedAtstringrequired
date-time

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
GET /api/v1/workspaces/{workspaceId}/conversations/{conversationId}/messages

List a conversation's messages

The conversation's member messages in chronological order, cursor-paginated. Messages can span the conversation's root stream and its threads; each message carries its own `streamId`.

Scope messages:read

Path parameters

conversationIdstringrequired

Conversation ID (prefixed ULID)

Query parameters

afterstring
limitinteger
1–100 · default 50

Response 200

dataobject[]required
idstringrequired
streamIdstringrequired
sequencestringrequired

Numeric sequence as string

authorIdstringrequired
authorTypestringrequired
one of: user, persona, system, bot
authorDisplayNamestring
contentstringrequired
replyCountintegerrequired
-9007199254740991–9007199254740991
threadStreamIdstring
clientMessageIdstring
sentViastring

Present when message was sent via API on behalf of a user

metadataobjectrequired

External references attached by the sender. Always present; empty when unset.

attachmentsobject[]
idstringrequired
filenamestringrequired
mimeTypestringrequired
sizeBytesintegerrequired
-9007199254740991–9007199254740991
processingStatusstring
one of: pending, processing, completed, failed, skipped
widthinteger
-9007199254740991–9007199254740991
heightinteger
-9007199254740991–9007199254740991
revisionintegerrequired
≤ 9007199254740991

1 for the original body, +1 per edit

editedAtstring
date-time
createdAtstringrequired
date-time
hasMorebooleanrequired
cursorstring | nullrequired
slotsobjectrequired

Hydration for shared-message pointers in the returned messages, keyed by the pointer's reference: `shared:<messageId>` (legacy, current revision), `shared:<messageId>@<version>` (pinned revision) or `shared:<messageId>@<version>:<from>-<to>` (pinned span). Always present; empty when no message references a shared source.

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found

Memos

Search preserved workspace knowledge and inspect memo provenance

Recipes Find out what was decided, and why →

POST /api/v1/workspaces/{workspaceId}/memos/search

Search memos

Search preserved workspace memos with semantic, exact, or recent-first retrieval.

Scope memos:read

Request body

querystringrequired
default ""
exactboolean
streamsstring[]
memoTypestring[]
knowledgeTypestring[]
tagsstring[]
scopestring
one of: user, stream, workspace
beforestring
date-time
afterstring
date-time
limitintegerrequired
1–100 · default 20

Response 200

dataobject[]required
memoobjectrequired
idstringrequired
workspaceIdstringrequired
memoTypestringrequired
one of: message, conversation
sourceMessageIdstring | nullrequired
sourceConversationIdstring | nullrequired
titlestringrequired
abstractstringrequired
keyPointsstring[]required
sourceMessageIdsstring[]required
participantIdsstring[]required
knowledgeTypestringrequired
one of: decision, learning, procedure, context, reference
tagsstring[]required
parentMemoIdstring | nullrequired
statusstringrequired
versionintegerrequired
-9007199254740991–9007199254740991
revisionReasonstring | nullrequired
authoredByKindstringrequired
one of: pipeline, agent
sourceSessionIdstring | nullrequired
scopestringrequired
one of: user, stream, workspace
scopeUserIdstring | nullrequired
createdAtstringrequired
date-time
updatedAtstringrequired
date-time
archivedAtstring | nullrequired
distancenumberrequired
sourceStreamobject | nullrequired
rootStreamobject | nullrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
GET /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

memoIdstringrequired

Memo ID (prefixed ULID)

Response 200

dataobjectrequired
memoobjectrequired
idstringrequired
workspaceIdstringrequired
memoTypestringrequired
one of: message, conversation
sourceMessageIdstring | nullrequired
sourceConversationIdstring | nullrequired
titlestringrequired
abstractstringrequired
keyPointsstring[]required
sourceMessageIdsstring[]required
participantIdsstring[]required
knowledgeTypestringrequired
one of: decision, learning, procedure, context, reference
tagsstring[]required
parentMemoIdstring | nullrequired
statusstringrequired
versionintegerrequired
-9007199254740991–9007199254740991
revisionReasonstring | nullrequired
authoredByKindstringrequired
one of: pipeline, agent
sourceSessionIdstring | nullrequired
scopestringrequired
one of: user, stream, workspace
scopeUserIdstring | nullrequired
createdAtstringrequired
date-time
updatedAtstringrequired
date-time
archivedAtstring | nullrequired
distancenumberrequired
sourceStreamobject | nullrequired
rootStreamobject | nullrequired
sourceMessagesobject[]required
idstringrequired
streamIdstringrequired
streamNamestringrequired
authorIdstringrequired
authorTypestringrequired
one of: user, persona, system, bot
authorNamestringrequired
contentstringrequired
createdAtstringrequired
date-time
successorMemoIdstring | nullrequired
capturedByPersonaNamestring | nullrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found

Attachments

Search attachments, inspect extracted content, and fetch download URLs

POST /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

Response 201

dataobjectrequired
idstringrequired
filenamestringrequired
mimeTypestringrequired
sizeBytesintegerrequired
-9007199254740991–9007199254740991
processingStatusstringrequired
one of: pending, processing, completed, failed, skipped
createdAtstringrequired
date-time

Status codes

  • 201 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
POST /api/v1/workspaces/{workspaceId}/attachments/search

Search attachments

Search accessible attachments by filename or extracted content. Omit query to browse the most recent attachments.

Scope attachments:read

Request body

querystring
min length 1
streamsstring[]
contentTypesstring[]
limitintegerrequired
1–50 · default 20

Response 200

dataobject[]required
idstringrequired
filenamestringrequired
mimeTypestringrequired
contentTypestring | nullrequired
summarystring | nullrequired
streamIdstring
messageIdstring
createdAtstringrequired
date-time

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
GET /api/v1/workspaces/{workspaceId}/attachments/{attachmentId}

Get an attachment

Retrieve attachment metadata and extracted content for an accessible attachment.

Scope attachments:read

Path parameters

attachmentIdstringrequired

Attachment ID (prefixed ULID)

Response 200

dataobjectrequired
idstringrequired
filenamestringrequired
mimeTypestringrequired
sizeBytesintegerrequired
-9007199254740991–9007199254740991
processingStatusstringrequired
one of: pending, processing, completed, failed, skipped
createdAtstringrequired
date-time
extractionobject | nullrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
GET /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

attachmentIdstringrequired

Attachment ID (prefixed ULID)

Response 200

dataobjectrequired
urlstringrequired
uri
expiresInintegerrequired
-9007199254740991–9007199254740991

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found

Users

List workspace users

GET /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

querystring
afterstring
limitinteger
1–200 · default 50

Response 200

dataobject[]required
idstringrequired
namestringrequired
slugstringrequired
emailstringrequired
avatarUrlstring
rolestringrequired
hasMorebooleanrequired
cursorstring | nullrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource

Labels

List, create, edit, archive, join, and apply workspace labels

GET /api/v1/workspaces/{workspaceId}/labels

List labels

The key actor's labels (every label is private to its owner) and their resource assignments.

Scope labels:read

Response 200

dataobjectrequired
labelsobject[]required
idstringrequired
workspaceIdstringrequired
creatorActorTypestringrequired
one of: user, bot
creatorActorIdstringrequired
namestringrequired
slugstringrequired
colorstringrequired
emojistring | nullrequired
descriptionstring | nullrequired
createdAtstringrequired
date-time
updatedAtstringrequired
date-time
archivedAtstring | nullrequired
assignmentsobject[]required
labelIdstringrequired
resourceTypestringrequired
one of: stream, message
resourceIdstringrequired
actorTypestringrequired
one of: user, bot
actorIdstringrequired
workspaceIdstringrequired
assignedAtstringrequired
date-time

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
POST /api/v1/workspaces/{workspaceId}/labels

Create or update a label by name

Find-or-create a label owned by the key actor (a user or a bot), keyed by its name. Posting an existing name returns that label and applies any appearance fields supplied; labels are identified by their text, so this is idempotent.

Scope labels:write

Request body

namestringrequired
min length 1 · max length 100
colorstring
emojistring | null
descriptionstring | null

Response 201

dataobjectrequired
idstringrequired
workspaceIdstringrequired
creatorActorTypestringrequired
one of: user, bot
creatorActorIdstringrequired
namestringrequired
slugstringrequired
colorstringrequired
emojistring | nullrequired
descriptionstring | nullrequired
createdAtstringrequired
date-time
updatedAtstringrequired
date-time
archivedAtstring | nullrequired

Status codes

  • 201 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
POST /api/v1/workspaces/{workspaceId}/labels/assignments

Apply a label to a resource by name

Attach a label to a resource the key actor can reach, identifying the label by its text: the label is found-or-created for the actor, then assigned. `resourceType` is the polymorphic target (`stream` today) so the same endpoint labels any future resource without a wire change.

Scope labels:write

Request body

namestringrequired
min length 1 · max length 100
colorstring
emojistring | null
descriptionstring | null
resourceTypestringrequired
one of: stream, message
resourceIdstringrequired
min length 1 · max length 64

Response 201

dataobjectrequired
labelobjectrequired
idstringrequired
workspaceIdstringrequired
creatorActorTypestringrequired
one of: user, bot
creatorActorIdstringrequired
namestringrequired
slugstringrequired
colorstringrequired
emojistring | nullrequired
descriptionstring | nullrequired
createdAtstringrequired
date-time
updatedAtstringrequired
date-time
archivedAtstring | nullrequired
assignmentobjectrequired
labelIdstringrequired
resourceTypestringrequired
one of: stream, message
resourceIdstringrequired
actorTypestringrequired
one of: user, bot
actorIdstringrequired
workspaceIdstringrequired
assignedAtstringrequired
date-time

Status codes

  • 201 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
DELETE /api/v1/workspaces/{workspaceId}/labels/assignments

Remove a label from a resource by name

Remove the key actor's assignment of a label (identified by its text) from a resource.

Scope labels:write

Query parameters

namestringrequired
min length 1 · max length 100
resourceTypestringrequired
one of: stream, message
resourceIdstringrequired
min length 1 · max length 64

Status codes

  • 204 No content
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
PATCH /api/v1/workspaces/{workspaceId}/labels/{labelId}

Update a label

Update a label the key actor created.

Scope labels:write

Path parameters

labelIdstringrequired

Label ID (prefixed ULID)

Request body

namestring
min length 1 · max length 100
colorstring
emojistring | null
descriptionstring | null

Response 200

dataobjectrequired
idstringrequired
workspaceIdstringrequired
creatorActorTypestringrequired
one of: user, bot
creatorActorIdstringrequired
namestringrequired
slugstringrequired
colorstringrequired
emojistring | nullrequired
descriptionstring | nullrequired
createdAtstringrequired
date-time
updatedAtstringrequired
date-time
archivedAtstring | nullrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
DELETE /api/v1/workspaces/{workspaceId}/labels/{labelId}

Delete a label

Archive a label the key actor created and remove its assignments.

Scope labels:write

Path parameters

labelIdstringrequired

Label ID (prefixed ULID)

Status codes

  • 204 No content
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found

Bot runtimes

Register a runtime and keep its presence alive so it can be assigned work.

Recipes Connect your local agent →

POST /api/v1/workspaces/{workspaceId}/bot-runtime/presence

Heartbeat bot runtime presence

Scope bot-runtime:write

Request body

runtimeKindstringrequired
one of: pi-local, hermes, openclaw, claude-code-channel, custom
instanceIdstringrequired
min length 1 · max length 128
runtimeSessionIdstring
min length 1 · max length 256
displayNamestring
max length 100
statusstringrequired
one of: available, busy, offline, error
acceptingInvocationsbooleanrequired
capabilitiesobjectrequired
default {}
manifestobject | null
statusTextstring
max length 200
publicKeystring
min length 44 · max length 44
publicKeyIdstring
min length 1 · max length 128

Response 200

dataobjectrequired
idstringrequired
workspaceIdstringrequired
botIdstringrequired
runtimeKindstringrequired
one of: pi-local, hermes, openclaw, claude-code-channel, custom
instanceIdstringrequired
displayNamestring | nullrequired
statusstringrequired
one of: available, busy, offline, error
acceptingInvocationsbooleanrequired
capabilitiesobjectrequired
statusTextstring | nullrequired
lastSeenAtstringrequired
date-time
createdAtstringrequired
date-time
updatedAtstringrequired
date-time

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
POST /api/v1/workspaces/{workspaceId}/bot-runtime/sessions

Create or link a bot runtime session

Creates a fresh scratchpad session by default. Pass `attachTo` to link the session to a new thread under an existing scratchpad the bot already has access to instead. An identity that is already linked resumes its existing link in either mode; compare the returned `rootStreamId` and `activeStreamId` with the request to tell the two apart.

Scope bot-runtime:write

Request body

runtimeKindstringrequired
one of: pi-local, claude-code-channel, custom
instanceIdstringrequired
min length 1 · max length 128
runtimeSessionIdstringrequired
min length 1 · max length 256
displayNamestringrequired
min length 1 · max length 100
localCwdstring
max length 1000
memoryModestring
one of: auto, off
labelNamestring
min length 1 · max length 100
descriptionstring
max length 10000
e2eobject
ownerKeyIdstringrequired
min length 1 · max length 128
ifArchivedstring
one of: wait, replace
ifMissingstring
one of: create, error
attachToobject
rootStreamIdstringrequired
min length 1
anchorIdstringrequired
min length 1

Response 200

dataobjectrequired
linkIdstringrequired
rootStreamIdstringrequired
activeStreamIdstringrequired
runtimeSessionIdstringrequired
streamUrlPathstringrequired
e2eEnabledboolean

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
  • 409 Conflict: the resource is not in the state the operation requires
GET /api/v1/workspaces/{workspaceId}/bot-runtime/owner-e2e-key

Get the bot owner's active encryption public key

The bot owner's active UIK (key id + base64 X25519 public key). A sealed harness fetches this before creating an end-to-end-encrypted session so it can wrap the generation-0 stream key to the owner. 404 when the owner has not set up encryption.

Scope bot-runtime:write

Response 200

dataobjectrequired
keyIdstringrequired
publicKeystringrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
POST /api/v1/workspaces/{workspaceId}/streams/{streamId}/e2e/key-wraps

Provision the generation-0 key wraps for a harness-created encrypted scratchpad

Phase two of harness-created E2E scratchpads: stores the stream-key wraps (owner UIK + the harness's own BIK) minted against the stream id returned by session create. Bot-actor-only, current generation only, and only while the generation has no wraps. Slots are immutable, so a replay cannot splice keys.

Scope bot-runtime:write

Path parameters

streamIdstringrequired

Stream ID

Request body

keyGenerationintegerrequired
0–9007199254740991
wrapsobject[]required
recipientKindstringrequired
one of: user, bot
recipientKeyIdstringrequired
min length 1 · max length 128
wrapEncstringrequired
base64 · min length 1
wrapCtstringrequired
base64 · min length 1

Response 200

dataobjectrequired
storedintegerrequired
-9007199254740991–9007199254740991

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
POST /api/v1/workspaces/{workspaceId}/bot-runtime/sessions/rename

Rename the scratchpad linked to a bot runtime session

Scope bot-runtime:write

Request body

instanceIdstringrequired
min length 1 · max length 128
runtimeSessionIdstringrequired
min length 1 · max length 256
displayNamestringrequired
min length 1 · max length 100

Response 200

dataobjectrequired
linkIdstringrequired
rootStreamIdstringrequired
activeStreamIdstringrequired
runtimeSessionIdstringrequired
streamUrlPathstringrequired
e2eEnabledboolean
displayNamestringrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
POST /api/v1/workspaces/{workspaceId}/bot-runtime/sessions/rebind

Move an existing bot runtime session link to a new runtime instance id

Scope bot-runtime:write

Request body

linkIdstringrequired
min length 1 · max length 128
instanceIdstringrequired
min length 1 · max length 128
runtimeSessionIdstringrequired
min length 1 · max length 256
newInstanceIdstringrequired
min length 1 · max length 128

Response 200

dataobjectrequired
linkIdstringrequired
rootStreamIdstringrequired
activeStreamIdstringrequired
runtimeSessionIdstringrequired
streamUrlPathstringrequired
e2eEnabledboolean

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
POST /api/v1/workspaces/{workspaceId}/bot-runtime/sessions/end

End the runtime session link on purpose

Ends the link without archiving any stream and frees the runtime identity for reuse. Cancels any pending invocation still routed at this runtime session, except the one named by `exceptInvocationId` so the command that ends the link can finish reporting on it.

Scope bot-runtime:write

Request body

instanceIdstringrequired
min length 1 · max length 128
runtimeSessionIdstringrequired
min length 1 · max length 256
exceptInvocationIdstring
min length 1 · max length 128

Response 200

dataobjectrequired
linkIdstringrequired
rootStreamIdstringrequired
activeStreamIdstringrequired
statusstringrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
POST /api/v1/workspaces/{workspaceId}/bot-runtime/sessions/brief

Brief the linked runtime session with a prompt

Creates the invocation that delivers a prompt as a turn to the linked runtime session. The turn is sourced from the thread's anchor message and answered into the thread, so the brief writes no message of its own. That anchor is the brief's identity: repeating a brief with the same prompt returns the invocation already in flight, and a different prompt for the same anchor is refused with 409.

Scope bot-runtime:write

Request body

instanceIdstringrequired
min length 1 · max length 128
runtimeSessionIdstringrequired
min length 1 · max length 256
contentstringrequired
min length 1 · max length 50000

Response 201

dataobjectrequired
invocationIdstringrequired
streamIdstringrequired

Status codes

  • 201 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found

Bot invocations

Claim, renew, step through, complete, or fail the work a bot is summoned to do.

Recipes Connect your local agent →

POST /api/v1/workspaces/{workspaceId}/bot-invocations/claim

Claim one pending bot invocation

Scope bot-invocations:write

Request body

runtimeKindstringrequired
one of: pi-local, hermes, openclaw, claude-code-channel, custom
instanceIdstringrequired
min length 1 · max length 128
runtimeSessionIdstring
min length 1 · max length 256
supportedCapabilitiesstring[]required
claimTtlSecondsintegerrequired
15–300 · default 60
responseStreamIdstring
min length 1 · max length 64

Response 200

dataobject | nullrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
POST /api/v1/workspaces/{workspaceId}/bot-invocations/{invocationId}/renew

Renew a claimed bot invocation

Scope bot-invocations:write

Path parameters

invocationIdstringrequired

Invocation ID

Request body

instanceIdstringrequired
min length 1 · max length 128
claimTokenstringrequired
min length 1 · max length 256
claimTtlSecondsintegerrequired
15–300 · default 60
knownSourceRevisioninteger
0–9007199254740991
restartRequiredRevisioninteger
0–9007199254740991

Response 200

dataobjectrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
  • 409 Conflict: the resource is not in the state the operation requires
POST /api/v1/workspaces/{workspaceId}/bot-invocations/{invocationId}/progress

Report a step of a claimed slash command

Appends a progress step to the slash command the claimed invocation carries. The step renders inside the command's timeline entry while it runs; it is not a message. Refused with 409 when the invocation is not a slash command.

Scope bot-invocations:write

Path parameters

invocationIdstringrequired

Invocation ID

Request body

instanceIdstringrequired
min length 1 · max length 128
claimTokenstringrequired
min length 1 · max length 256
stepstringrequired
min length 1 · max length 200

Response 200

dataobjectrequired
invocationIdstringrequired
statusstringrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
  • 409 Conflict: the resource is not in the state the operation requires
POST /api/v1/workspaces/{workspaceId}/bot-invocations/{invocationId}/steps

Record a bot invocation trace step

Scope bot-invocations:write

Path parameters

invocationIdstringrequired

Invocation ID

Request body

instanceIdstringrequired
min length 1 · max length 128
claimTokenstringrequired
min length 1 · max length 256
stepTypestringrequired
one of: context_received, thinking, reconsidering, steer, web_search, visit_page, workspace_search, research, github_access, linear_access, message_sent, message_edited, response, tool_call, tool_error, rate_limited, rate_limit_retry, turn_digest, model_escalated
contentstringrequired
min length 1 · max length 10000
statusTextstring
max length 200
clientStepIdstring
min length 1 · max length 128

Response 200

dataobjectrequired
invocationIdstringrequired
sessionIdstringrequired
stepIdstringrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
POST /api/v1/workspaces/{workspaceId}/bot-invocations/{invocationId}/messages

Post a message from a bot invocation

Posts one plaintext message into the invocation's own response stream — progress notes and permission prompts mid-turn, follow-ups after the turn completed (a claim-bound completed invocation may still post; completion itself stays terminal and is never reopened). The claim decides the stream and the session the message is attributed to, so a harness never has to name either. Rejects end-to-end encrypted streams (use sealed-messages). Authenticated with the bot API key plus the claim's instanceId and claimToken; clientMessageId dedupes a retried post.

Scope bot-invocations:write

Path parameters

invocationIdstringrequired

Invocation ID

Request body

instanceIdstringrequired
min length 1 · max length 128
claimTokenstringrequired
min length 1 · max length 256
contentstringrequired
min length 1 · max length 50000
clientMessageIdstring
min length 1 · max length 128
metadataobject

Response 200

dataobjectrequired
invocationIdstringrequired
sessionIdstringrequired
messageIdstringrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
  • 409 Conflict: the resource is not in the state the operation requires
POST /api/v1/workspaces/{workspaceId}/bot-invocations/{invocationId}/sealed-steps/started

Open an in-flight sealed bot invocation trace step

Sealed variant of the trace-step start, for an owner-granted E2E bot harness: the content is ciphertext the server never decrypts. Authenticated with the per-claim callback token in the X-Threa-Callback-Token header.

Scope bot-invocations:write

Path parameters

invocationIdstringrequired

Invocation ID

Request body

stepIdstringrequired
min length 1 · max length 128
stepTypestringrequired
one of: context_received, thinking, reconsidering, steer, web_search, visit_page, workspace_search, research, github_access, linear_access, message_sent, message_edited, response, tool_call, tool_error, rate_limited, rate_limit_retry, turn_digest, model_escalated
messageIdstring
min length 1 · max length 128
ciphertextstring
base64 · min length 1
envelopeobject
vnumberrequired
keyGenerationintegerrequired
0–9007199254740991
ivstringrequired
base64 · min length 1
aadstringrequired
base64 · min length 1

Response 200

dataobjectrequired
invocationIdstringrequired
sessionIdstringrequired
stepIdstringrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
POST /api/v1/workspaces/{workspaceId}/bot-invocations/{invocationId}/sealed-steps

Finalize a sealed bot invocation trace step

Sealed variant of the trace-step finalize, for an owner-granted E2E bot harness: sets the sealed content + completion on the step opened at sealed-steps/started (or inserts a completed row if the start was dropped). Authenticated with the per-claim callback token in the X-Threa-Callback-Token header.

Scope bot-invocations:write

Path parameters

invocationIdstringrequired

Invocation ID

Request body

stepIdstringrequired
min length 1 · max length 128
stepTypestringrequired
one of: context_received, thinking, reconsidering, steer, web_search, visit_page, workspace_search, research, github_access, linear_access, message_sent, message_edited, response, tool_call, tool_error, rate_limited, rate_limit_retry, turn_digest, model_escalated
messageIdstring
min length 1 · max length 128
ciphertextstringrequired
base64 · min length 1
envelopeobjectrequired
vnumberrequired
keyGenerationintegerrequired
0–9007199254740991
ivstringrequired
base64 · min length 1
aadstringrequired
base64 · min length 1
durationMsinteger
0–9007199254740991

Response 200

dataobjectrequired
invocationIdstringrequired
sessionIdstringrequired
stepIdstringrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
POST /api/v1/workspaces/{workspaceId}/bot-invocations/{invocationId}/sealed-messages

Post a sealed message from a sealed bot invocation

Sealed variant of a bot message, for an owner-granted E2E bot harness: posts one sealed message (ciphertext the server never decrypts) into the claim's stream. Mid-turn that is a progress note, permission prompt, or early ack; after the turn completed it is a follow-up, which a callback-token-bound completed invocation may still post — completion itself stays terminal and is never reopened. The client-minted messageId binds the seal AAD and dedupes retries. Authenticated with the per-claim callback token in the X-Threa-Callback-Token header.

Scope bot-invocations:write

Path parameters

invocationIdstringrequired

Invocation ID

Request body

messageIdstringrequired
min length 1 · max length 128
ciphertextstringrequired
base64 · min length 1
envelopeobjectrequired
vnumberrequired
keyGenerationintegerrequired
0–9007199254740991
ivstringrequired
base64 · min length 1
aadstringrequired
base64 · min length 1
attachmentIdsstring[]

Response 200

dataobjectrequired
messageIdstringrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
  • 409 Conflict: the resource is not in the state the operation requires
POST /api/v1/workspaces/{workspaceId}/bot-invocations/{invocationId}/sealed-complete

Complete a sealed bot invocation

Sealed variant of the completion, for an owner-granted E2E bot harness: persists the turn's final sealed reply (ciphertext the server never decrypts) or noResponse, flips the claim, and finalizes the agent session. A still-active claim may recover a session marked failed by orphan cleanup. Authenticated with the per-claim callback token in the X-Threa-Callback-Token header.

Scope bot-invocations:write

Path parameters

invocationIdstringrequired

Invocation ID

Request body

sourceRevisioninteger
0–9007199254740991
replyobject
messageIdstringrequired
min length 1 · max length 128
ciphertextstringrequired
base64 · min length 1
envelopeobjectrequired
vnumberrequired
keyGenerationintegerrequired
0–9007199254740991
ivstringrequired
base64 · min length 1
aadstringrequired
base64 · min length 1
attachmentIdsstring[]
noResponseboolean

Response 200

dataobjectrequired
invocationIdstringrequired
sessionIdstringrequired
messageIdstring | nullrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
  • 409 Conflict: the resource is not in the state the operation requires
POST /api/v1/workspaces/{workspaceId}/bot-invocations/{invocationId}/complete

Complete a claimed bot invocation

Closes the claim, posting the reply the body carries. Completing the `/done` command also archives the thread it ran in, when that thread is one the bot opened.

Scope bot-invocations:write

Path parameters

invocationIdstringrequired

Invocation ID

Request body

instanceIdstringrequired
min length 1 · max length 128
claimTokenstringrequired
min length 1 · max length 256
sourceRevisioninteger
0–9007199254740991
finalMessageMarkdownstring
min length 1 · max length 50000
noResponseboolean
summarystring
min length 1 · max length 500
sourcesobject[]
typestring
one of: web, workspace, github
titlestringrequired
min length 1 · max length 500
urlstringrequired
min length 1 · max length 2000
snippetstring
max length 2000
metadataobject
sealedReplyobject
messageIdstringrequired
min length 1 · max length 128
ciphertextstringrequired
base64 · min length 1
envelopeobjectrequired
vnumberrequired
keyGenerationintegerrequired
0–9007199254740991
ivstringrequired
base64 · min length 1
aadstringrequired
base64 · min length 1

Response 200

dataobjectrequired
invocationIdstringrequired
messageobject | nullrequired
slotsobjectrequired

Hydration for shared-message pointers in the returned messages, keyed by the pointer's reference: `shared:<messageId>` (legacy, current revision), `shared:<messageId>@<version>` (pinned revision) or `shared:<messageId>@<version>:<from>-<to>` (pinned span). Always present; empty when no message references a shared source.

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
  • 409 Conflict: the resource is not in the state the operation requires
POST /api/v1/workspaces/{workspaceId}/bot-invocations/{invocationId}/fail

Fail a claimed bot invocation

Scope bot-invocations:write

Path parameters

invocationIdstringrequired

Invocation ID

Request body

instanceIdstringrequired
min length 1 · max length 128
claimTokenstringrequired
min length 1 · max length 256
errorMessagestringrequired
min length 1 · max length 1000

Response 200

dataobjectrequired
invocationIdstringrequired
statusstringrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found

Delegations

Inspect delegated work, claim accepted tasks, report progress or renew the lease, then complete, fail, or release the claim.

GET /api/v1/workspaces/{workspaceId}/delegations

List open delegations

List accessible delegations with status `open`. When `since` is provided, filters by `statusChangedAt`; returns newly created and reopened tasks whose availability changed after that instant.

Scope delegations:read

Query parameters

statusstring
default "open"
sincestring
date-time

Response 200

dataobject[]required
idstringrequired
streamIdstringrequired
titlestringrequired
statusstringrequired
one of: open, claimed, running, completed, failed, cancelled, expired
claimedByLabelstring
statusNotestring
resultMessageIdstring
sourceConversationIdstring
createdAtstringrequired
date-time
statusChangedAtstringrequired
date-time

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
GET /api/v1/workspaces/{workspaceId}/delegations/{delegationId}

Get a delegation

Inspect an accessible delegation before claiming it. Returns its brief, context references, status, and current claim expiry, but never a claim token or other claim secret.

Scope delegations:read

Path parameters

delegationIdstringrequired

Delegation ID (prefixed ULID)

Response 200

dataobjectrequired
idstringrequired
streamIdstringrequired
titlestringrequired
statusstringrequired
one of: open, claimed, running, completed, failed, cancelled, expired
claimedByLabelstring
statusNotestring
resultMessageIdstring
sourceConversationIdstring
createdAtstringrequired
date-time
statusChangedAtstringrequired
date-time
briefstringrequired
contextRefsstring[]required
claimExpiresAtstring
date-time

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
POST /api/v1/workspaces/{workspaceId}/delegations/{delegationId}/claim

Claim an open or expired delegation

Atomically claim an open delegation or a historical delegation still stored as `expired`. A matching idempotency key can re-key only the current unexpired claimed or running claim. Returns the brief, context references, and a cleartext claim token exactly once; send the token as X-Threa-Callback-Token on later lifecycle calls. Other non-open states return 409; a missing or inaccessible delegation returns 404.

Scope delegations:write

Path parameters

delegationIdstringrequired

Delegation ID (prefixed ULID)

Request body

claimedByLabelstringrequired
min length 1 · max length 200
idempotencyKeystring
min length 8 · max length 128

Response 200

dataobjectrequired
idstringrequired
streamIdstringrequired
titlestringrequired
statusstringrequired
one of: open, claimed, running, completed, failed, cancelled, expired
claimedByLabelstring
statusNotestring
resultMessageIdstring
sourceConversationIdstring
createdAtstringrequired
date-time
statusChangedAtstringrequired
date-time
briefstringrequired
contextRefsstring[]required
claimTokenstringrequired
claimExpiresAtstringrequired
date-time

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
  • 409 Conflict: the resource is not in the state the operation requires
POST /api/v1/workspaces/{workspaceId}/delegations/{delegationId}/release

Release a delegation claim

Release an unexpired claimed or running delegation back to the open queue. Requires the per-claim token in X-Threa-Callback-Token. A missing, inaccessible, lapsed, stale, or replaced claim returns 404 without revealing which condition applied.

Scope delegations:write

Path parameters

delegationIdstringrequired

Delegation ID (prefixed ULID)

Response 200

dataobjectrequired
idstringrequired
streamIdstringrequired
titlestringrequired
statusstringrequired
one of: open, claimed, running, completed, failed, cancelled, expired
claimedByLabelstring
statusNotestring
resultMessageIdstring
sourceConversationIdstring
createdAtstringrequired
date-time
statusChangedAtstringrequired
date-time

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
POST /api/v1/workspaces/{workspaceId}/delegations/{delegationId}/heartbeat

Renew a delegation claim

Renew a live claim without changing the card status. Any HTTP client can call this endpoint directly while working; send the per-claim token in X-Threa-Callback-Token.

Scope delegations:write

Path parameters

delegationIdstringrequired

Delegation ID (prefixed ULID)

Response 200

dataobjectrequired
claimExpiresAtstringrequired
date-time

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
POST /api/v1/workspaces/{workspaceId}/delegations/{delegationId}/status

Report delegation progress

Mark the delegation running and put a free-text progress note on its card (each report replaces the previous note; the claim TTL renews). Authenticated with the per-claim token in the X-Threa-Callback-Token header.

Scope delegations:write

Path parameters

delegationIdstringrequired

Delegation ID (prefixed ULID)

Request body

statusNotestring
min length 1 · max length 2000

Response 200

dataobjectrequired
idstringrequired
streamIdstringrequired
titlestringrequired
statusstringrequired
one of: open, claimed, running, completed, failed, cancelled, expired
claimedByLabelstring
statusNotestring
resultMessageIdstring
sourceConversationIdstring
createdAtstringrequired
date-time
statusChangedAtstringrequired
date-time

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
POST /api/v1/workspaces/{workspaceId}/delegations/{delegationId}/complete

Complete a delegation

Complete the claimed delegation. When `resultMarkdown` is provided, the result is posted as a reply in the thread anchored on the delegation card, in the same transaction as the completion; it is authored as the key's user (with via-API provenance) for a user-scoped key, or as the bot for a workspace key. The response then includes `resultMessageId` and `resultThreadId`, and the result enters the normal message pipeline so workspace memory can capture it. Without `resultMarkdown`, the response contains only the delegation summary and no result ids. Send the per-claim token in X-Threa-Callback-Token.

Scope delegations:write

Path parameters

delegationIdstringrequired

Delegation ID (prefixed ULID)

Request body

resultMarkdownstring
min length 1 · max length 50000
metadataobject

Response 200

dataobjectrequired
idstringrequired
streamIdstringrequired
titlestringrequired
statusstringrequired
one of: open, claimed, running, completed, failed, cancelled, expired
claimedByLabelstring
statusNotestring
resultMessageIdstring
sourceConversationIdstring
createdAtstringrequired
date-time
statusChangedAtstringrequired
date-time
resultThreadIdstring

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
POST /api/v1/workspaces/{workspaceId}/delegations/{delegationId}/fail

Fail a delegation

Mark the claimed delegation failed, recording why on its card. Authenticated with the per-claim token in the X-Threa-Callback-Token header.

Scope delegations:write

Path parameters

delegationIdstringrequired

Delegation ID (prefixed ULID)

Request body

errorMessagestringrequired
min length 1 · max length 2000

Response 200

dataobjectrequired
idstringrequired
streamIdstringrequired
titlestringrequired
statusstringrequired
one of: open, claimed, running, completed, failed, cancelled, expired
claimedByLabelstring
statusNotestring
resultMessageIdstring
sourceConversationIdstring
createdAtstringrequired
date-time
statusChangedAtstringrequired
date-time

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found
POST /api/v1/workspaces/{workspaceId}/delegations/{delegationId}/request-access

Request access to a delegation's stream

For a workspace (bot) key that received the delegation:available nudge but cannot claim it (no channel grant): file an access request that renders as a card in the delegation's stream for a member to approve or deny. Returns already_granted (no card) when the bot already has access; otherwise the request is idempotent per (bot, stream). 404 for an unknown delegation id — the existence-hiding carve-out is scoped to ids the workspace bot plane already saw on the nudge. A user-scoped key gets 400 (USER_KEY_CANNOT_REQUEST_ACCESS): a user key's access follows its user, who should join the stream directly.

Scope delegations:write

Path parameters

delegationIdstringrequired

Delegation ID (prefixed ULID)

Request body

requestedByLabelstring
max length 200

Response 200

dataobjectrequired
requestIdstring
statusstringrequired

Status codes

  • 200 Successful response
  • 400 Validation error
  • 401 Missing or invalid API key
  • 403 Insufficient permissions or inaccessible resource
  • 404 Resource not found