Everything here is the API the app itself uses. Every call is a plain HTTP request with a bearer token, there is no SDK to install, and anything that can send JSON can be an integration.
The API is pre-1.0. It is in use every day, but paths, fields and shapes still change when they need to - so pin the version you build against, and read the reference below as what the API answers today.
A key is how a script, a bot or an assistant acts as you. Mint one in the app - your profile, then the keys tab - and give it a name you will recognise later. It is the whole of the authentication on every call below: send it as Authorization: Bearer <key>.
# From the app: your profile -> keys. It is shown once.
export GUILD_KEY="gk_live_..."
# Worth knowing before you start:
# it acts as you - the same channels, and nothing more
# keep it out of a repository; revoke it if it leaks
# a key belongs to one workspace, so two means two keys
Every example calls the API's own address, so there is nothing to configure there. The four ids below are what the later calls fill in: the walkthrough hands each one back, so read it in order and they will be right.
# https://api.useguild.dev is the API's address - no variable to set.
# Filled in as you go, so the examples run as written:
export CHANNEL_ID="" # from GET /api/channels
export MESSAGE_ID="" # from the task you create
export CURSOR="" # from the X-Next-Cursor header
Seven calls, in the order they make sense. Read them in order and you have a working integration; jump to the one you need and the example runs on its own.
Everything in Guild lives in a channel. This is the list the caller is a member of - the tokens are scoped to one workspace, so there is nothing to pass but the token itself.
Oldest first, and paged: the answer carries the next cursor in the X-Next-Cursor header. Pass it back as since to keep reading forwards, or as before to read backwards. A task is in this same list - a task is a message, printed in a structured way.
curl -s "https://api.useguild.dev/api/channels/$CHANNEL_ID/messages?limit=50" \
-H "Authorization: Bearer $GUILD_KEY" \
-D - # the X-Next-Cursor header comes out here
[{"_id":"6aaff2e8359000011551b361","text":"the queue is draining again","author_id":"6aaff2e8359000011551b362","written_at":"2026-09-28T09:00:00Z"}]
One call writes either a plain message or a task, because they are the same thing stored two ways: type says which. The answer is the stored message, so you have its id without asking again.
This is the thing Guild is for. Pick the messages that contain the decision, and name the work: the task is created from them and stays joined to the conversation it came from, so whoever picks it up can read why it exists.
curl -s https://api.useguild.dev/api/channels/$CHANNEL_ID/tasks \
-X POST \
-H "Authorization: Bearer $GUILD_KEY" \
-H "Content-Type: application/json" \
-d '{"message_ids":["6aaff2e8359000011551b361"],
"title":"Ship the queue fix",
"description":"before the standup"}'
{"_id":"6aaff2e8359000011551b363","type":"task","title":"Ship the queue fix","status":"todo","key":"INC-1"}
A task is in exactly one of five statuses, and moving it is its own call: todo, backlog, in_progress, done, discarded. This is what a board, a bot or a person at a keyboard all do the same way.
One query looks across messages, tasks, commits, pull requests, runs and channels, by meaning as well as by word. What you may find is decided by your membership, never by the query - there is no way to search your way into a channel you are not in.
The other direction: Guild tells you. Add a hook to a channel or to the whole workspace, give it a URL and a secret, choose what it should hear, and Guild POSTs one JSON body per event.
The headers first: the secret is the whole of the authentication (a bearer credential, not a signature), and the two X-Guild-* headers are there so you can route without parsing the body.
POST /your/endpoint HTTP/1.1
authorization: Bearer <the secret you set on the hook>
content-type: application/json
x-guild-event: task.status_changed
x-guild-delivery: 6aaff2e8359000011551b370
user-agent: guild/0.20.1
{"id":"6aaff2e8359000011551b370","event":"task.status_changed","workspace_id":"6aaff2e8359000011551b35e","at":"2026-09-28T09:00:00Z","actor_id":"6aaff2e8359000011551b362","channel_id":"6aaff2e8359000011551b360","payload":{"_id":"6aaff2e8359000011551b363","type":"task","title":"Ship the queue fix","status":"in_progress"}}
A 2xx is the whole of "delivered". Anything else is a rejected attempt, with the status and the first part of what you said recorded against it; no answer at all is unreachable.
Guild does not retry a delivery. A receiver that wants one keeps the X-Guild-Delivery id and asks again in its own time - or re-reads the API, which is the same data and the only thing that heals a gap.
Every attempt is a row in the workspace's delivery log with its outcome (delivered, rejected or unreachable), the status and the reason - so an endpoint that has been refusing everything for a week looks different from one that is working.
# Add one: the URL to POST to, the secret to sign it with, the events to hear.
# Leave `channel_id` out and the hook hears from the whole workspace; give it a
# channel's id and it hears from that channel alone. No `events` at all means every
# kind - which is the API's way of saying it, not a hook that hears nothing.
curl -s https://api.useguild.dev/api/workspace/hooks \
-X POST \
-H "Authorization: Bearer $GUILD_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/guild",
"secret":"a long random string",
"channel_id":"6aaff2e8359000011551b360",
"events":["task.status_changed","message.created"]}'
# What it has been saying: one row per attempt, newest first.
curl -s https://api.useguild.dev/api/workspace/hooks/$HOOK_ID/deliveries \
-H "Authorization: Bearer $GUILD_KEY"
Guild API 0.19.0, as this site was built against it. Grouped the way an integrator meets it: the areas the walkthrough above moves through, and what else there is to call. The sidebar lists them, so nothing here is reachable only by scrolling.
Administrative, and admin-only, and only for a channel that is already deleted - a live one has to be deleted first, so a stray call cannot destroy a conversation. The channel's messages are removed with it, for good; nothing is recoverable afterwards.
Every public channel, oldest first. Private channels are never listed. An external member is refused: they enter a channel by invitation, so the list of what exists here is not theirs to read.
A moderator's act: a mod or admin of this channel, or of the workspace. The channel disappears from every listing and refuses reads and writes, but its rows are kept: an administrator may list the deleted channels and purge one for good. A direct conversation cannot be deleted. Deleting an already-deleted channel is a no-op.
Administrative: an admin of this channel, or any workspace admin. The name, the system prompt and the two triggers. A channel with no agent answers 404 - which is the normal case, not an error to recover from.
{"allow_external":true,"channel_id":"6aaff2e8359000011551b360","description_prompt":"string","id":"6aaff2e8359000011551b35f","name":"ada-bot","on_mention":true,"on_new_messages":true,"plugins":[{"all_agents":true,"kind":"guild","name":"incidents","plugin_id":"6aaff2e8359000011551b35f","settings":{}}],"prefill_tasks":true,"system_prompt":"You answer questions about the ingest pipeline. Be brief.","title_prompt":"string"}
PUT/api/channels/{channel_id}/agent
Configure a channel's agent
Administrative: an admin of this channel, or any workspace admin. A full replacement of the configuration. name is also the mention token (@name), so it has no spaces. on_new_messages answers a new top-level message and never one inside a thread; on_mention answers @name anywhere.
curl -s https://api.useguild.dev/api/channels/6aaff2e8359000011551b360/agent \
-X PUT \
-H "Authorization: Bearer $GUILD_KEY" \
-H "Content-Type: application/json" \
-d '{"allow_external":true,"description_prompt":"Describe what has to happen, and who it affects. Use Gherkin (Given/When/Then) when it fits. Keep it under 200 words.","name":"ada-bot","on_mention":true,"on_new_messages":true,"plugins":[{"plugin_id":"507f1f77bcf86cd799439011","settings":{"channels":"devprod"}}],"prefill_tasks":true,"system_prompt":"You answer questions about the ingest pipeline. Be brief.","title_prompt":"Name the work the messages describe: a short, imperative title of at most 20 words, with no trailing period."}'
{"allow_external":true,"description_prompt":"Describe what has to happen, and who it affects. Use Gherkin (Given/When/Then) when it fits. Keep it under 200 words.","name":"ada-bot","on_mention":true,"on_new_messages":true,"plugins":[{"plugin_id":"507f1f77bcf86cd799439011","settings":{"channels":"devprod"}}],"prefill_tasks":true,"system_prompt":"You answer questions about the ingest pipeline. Be brief.","title_prompt":"Name the work the messages describe: a short, imperative title of at most 20 words, with no trailing period."}
{"allow_external":true,"channel_id":"6aaff2e8359000011551b360","description_prompt":"string","id":"6aaff2e8359000011551b35f","name":"ada-bot","on_mention":true,"on_new_messages":true,"plugins":[{"all_agents":true,"kind":"guild","name":"incidents","plugin_id":"6aaff2e8359000011551b35f","settings":{}}],"prefill_tasks":true,"system_prompt":"You answer questions about the ingest pipeline. Be brief.","title_prompt":"string"}
DELETE/api/channels/{channel_id}/agent
Remove a channel's agent
Administrative: an admin of this channel, or any workspace admin. The agent stops answering immediately. The messages it already wrote keep their author, so the user it spoke as is kept - only the agent is removed.
Administrative: an admin of this channel, or any workspace admin. Every plugin configured in the workspace that this channel may bind, and whether its agent already does - the list the form is built from.
Any member of the channel. The body is the file itself with its Content-Type; the name is in ?filename=. An image is scaled to fit 1920x1080 and served inline; anything else is stored zip-wrapped under that name and served as a download. At most 25 MB.
The invitee answers it through GET /api/invites and POST /api/invites/{invite_id}/accept. This is how a private channel is entered, and the only way a member whose workspace role is external enters any channel.
A private channel cannot be joined this way - it is entered by accepting an invitation (see POST /api/invites/{invite_id}/accept). A member whose workspace role is external cannot join this way either: an external person enters a channel by invitation only, private or public.
Removes the caller from the channel's members. The channel and its messages are untouched, and the caller can join again if it is public. me is the authenticated user, so nobody else can be removed this way.
Allowed to an admin of that channel, or to any workspace admin. The target must already be a member (404 otherwise), the caller may not remove themselves (409 - leaving is a member's own act, DELETE /api/channels/{channel_id}/members/me), and a direct conversation cannot be administered at all (422).
List the top level messages and tasks of a channel
Returns at most limit messages, oldest first. The next cursor is in the X-Next-Cursor response header: pass it back as since to keep reading forwards, or as before to read the page of history above what you have. Replies are not listed here: read a message's replies through GET /api/messages/{message_id}/thread.
[{"_id":"6aaff2e8359000011551b35f","attachments":[{"_id":"6aaff2e8359000011551b35f","bytes":0,"content_type":"string","created_at":"2026-09-28T09:00:00Z","filename":"string","height":0,"kind":"image","uncompressed_bytes":0,"url":"/api/attachments/507f1f77bcf86cd799439012","width":0}],"author_id":"6aaff2e8359000011551b35f","channel_id":"6aaff2e8359000011551b360","context_of":null,"created_at":"2026-09-28T09:00:00Z","depth":0,"from_agent":true,"parent":null,"reactions":[{"emoji":"string","user_id":"6aaff2e8359000011551b362"}],"root_id":null,"updated_at":"2026-09-28T09:00:00Z","workspace_id":"6aaff2e8359000011551b35e","text":"Deployed 4f21c0a - the queue is draining again.","type":"message","permalink":"https://app.example.com/c/507f1f77bcf86cd799439011/m/507f1f77bcf86cd799439012"}]
POST/api/channels/{channel_id}/messages
Post a message, or a task, into a channel
A plain message, or a task. A task is offered the workspace's tag catalog as it is created: it comes back carrying the tags Jev was at least 90% confident about, applied by the server, and it is created the same way when that judgement does not arrive.
{"attachments":["507f1f77bcf86cd799439012"],"text":"we should ship it","type":"message","parent":null}
{"text":"Deployed 4f21c0a - the queue is draining again.","_id":"6aaff2e8359000011551b35f","attachments":[{"_id":"6aaff2e8359000011551b35f","bytes":0,"content_type":"string","created_at":"2026-09-28T09:00:00Z","filename":"string","height":0,"kind":"image","uncompressed_bytes":0,"url":"/api/attachments/507f1f77bcf86cd799439012","width":0}],"author_id":"6aaff2e8359000011551b35f","channel_id":"6aaff2e8359000011551b360","context_of":null,"created_at":"2026-09-28T09:00:00Z","depth":0,"from_agent":true,"parent":null,"reactions":[{"emoji":"string","user_id":"6aaff2e8359000011551b362"}],"root_id":null,"updated_at":"2026-09-28T09:00:00Z","workspace_id":"6aaff2e8359000011551b35e","permalink":"https://app.example.com/c/507f1f77bcf86cd799439011/m/507f1f77bcf86cd799439012"}
PUT/api/channels/{channel_id}/quarantine
Quarantine a channel for a while
A moderator's act: a mod or admin of this channel, or of the workspace. While the quarantine is in force, only moderators may write in it - every other member keeps reading. It lasts minutes, at most a year. DELETE the same path to open it again.
The raw messages are linked through source_messages; they are never deleted or modified, so the conversation stays readable. Only top level plain messages of this channel can be grouped, each at most once, and the task may name attachments - ids already uploaded into this channel - which come back on every listing exactly as a message's do.
{"attachments":["507f1f77bcf86cd799439012"],"description":"before the standup","message_ids":["string"],"tags":["string"],"title":"Ship it"}
{"text":"Deployed 4f21c0a - the queue is draining again.","_id":"6aaff2e8359000011551b35f","attachments":[{"_id":"6aaff2e8359000011551b35f","bytes":0,"content_type":"string","created_at":"2026-09-28T09:00:00Z","filename":"string","height":0,"kind":"image","uncompressed_bytes":0,"url":"/api/attachments/507f1f77bcf86cd799439012","width":0}],"author_id":"6aaff2e8359000011551b35f","channel_id":"6aaff2e8359000011551b360","context_of":null,"created_at":"2026-09-28T09:00:00Z","depth":0,"from_agent":true,"parent":null,"reactions":[{"emoji":"string","user_id":"6aaff2e8359000011551b362"}],"root_id":null,"updated_at":"2026-09-28T09:00:00Z","workspace_id":"6aaff2e8359000011551b35e","permalink":"https://app.example.com/c/507f1f77bcf86cd799439011/m/507f1f77bcf86cd799439012"}
POST/api/channels/{channel_id}/tasks/prefill
Draft a task from selected messages
Asks the channel's agent for a title and a description for a task being created from the given messages. The agent reads the messages being grouped, the channel's recent traffic, the related messages it may see and the open tasks - and, when the workspace allows it, uses its own tools to look things up.
Reading and writing what people say - and the tasks a message can become.
GET/api/messages/threads
Read several messages' threads at once
The replies under each named message, nested to the same three levels as GET /api/messages/{message_id}/thread. The order the ids were named is kept, and a message the caller may not read (or that does not exist) is left out rather than refused. At most 200 ids.
[{"insights":{"estimate":{"p50":"2026-09-28T09:00:00Z","p75":"2026-09-28T09:00:00Z","p90":"2026-09-28T09:00:00Z","p99":"2026-09-28T09:00:00Z","remaining_days":0},"generated_at":"2026-09-28T09:00:00Z","milestones":[{"action":"string","at":"2026-09-28T09:00:00Z","kind":"pull_request_linked","number":0,"repo":"string","sha":"string","summary":"string","url":"https://example.com/acme/pull/12"}],"milestones_omitted":0,"time_in_status":[{"millis":0,"status":"backlog"}],"work":{"commits":0,"messages":0,"pull_requests":0,"reactions":0}},"replies":[{"_id":"6aaff2e8359000011551b35f","attachments":[{"_id":"6aaff2e8359000011551b35f","bytes":0,"content_type":"string","created_at":"2026-09-28T09:00:00Z","filename":"string","height":0,"kind":"image","uncompressed_bytes":0,"url":"/api/attachments/507f1f77bcf86cd799439012","width":0}],"author_id":"6aaff2e8359000011551b35f","channel_id":"6aaff2e8359000011551b360","context_of":null,"created_at":"2026-09-28T09:00:00Z","depth":0,"from_agent":true,"parent":null,"reactions":[{"emoji":"string","user_id":"6aaff2e8359000011551b362"}],"root_id":null,"updated_at":"2026-09-28T09:00:00Z","workspace_id":"6aaff2e8359000011551b35e"}],"text":"Deployed 4f21c0a - the queue is draining again.","type":"message"}]
GET/api/messages/{message_id}
One message: a plain message or a task - and, for a task, its per-task facts
when they are asked for.
{"_id":"6aaff2e8359000011551b35f","attachments":[{"_id":"6aaff2e8359000011551b35f","bytes":0,"content_type":"string","created_at":"2026-09-28T09:00:00Z","filename":"string","height":0,"kind":"image","uncompressed_bytes":0,"url":"/api/attachments/507f1f77bcf86cd799439012","width":0}],"author_id":"6aaff2e8359000011551b35f","channel_id":"6aaff2e8359000011551b360","context_of":null,"created_at":"2026-09-28T09:00:00Z","depth":0,"from_agent":true,"parent":null,"reactions":[{"emoji":"string","user_id":"6aaff2e8359000011551b362"}],"root_id":null,"updated_at":"2026-09-28T09:00:00Z","workspace_id":"6aaff2e8359000011551b35e","permalink":"https://app.example.com/c/507f1f77bcf86cd799439011/m/507f1f77bcf86cd799439012","text":"Deployed 4f21c0a - the queue is draining again.","type":"message","insights":{"estimate":{"p50":"2026-09-28T09:00:00Z","p75":"2026-09-28T09:00:00Z","p90":"2026-09-28T09:00:00Z","p99":"2026-09-28T09:00:00Z","remaining_days":0},"generated_at":"2026-09-28T09:00:00Z","milestones":[{"action":"string","at":"2026-09-28T09:00:00Z","kind":"pull_request_linked","number":0,"repo":"string","sha":"string","summary":"string","url":"https://example.com/acme/pull/12"}],"milestones_omitted":0,"time_in_status":[{"millis":0,"status":"backlog"}],"work":{"commits":0,"messages":0,"pull_requests":0,"reactions":0}}}
PATCH/api/messages/{message_id}
Edit a task's title and description
Rewrites what a task says, which is the one thing no other write path reaches: a status and an assignee are decisions about the work, and this is the work's own words.
curl -s https://api.useguild.dev/api/messages/6aaff2e8359000011551b361 \
-X PATCH \
-H "Authorization: Bearer $GUILD_KEY" \
-H "Content-Type: application/json" \
-d '{"description":"It fails about one run in twenty.","title":"Fix the flaky sync test"}'
{"description":"It fails about one run in twenty.","title":"Fix the flaky sync test"}
{"text":"Deployed 4f21c0a - the queue is draining again.","_id":"6aaff2e8359000011551b35f","attachments":[{"_id":"6aaff2e8359000011551b35f","bytes":0,"content_type":"string","created_at":"2026-09-28T09:00:00Z","filename":"string","height":0,"kind":"image","uncompressed_bytes":0,"url":"/api/attachments/507f1f77bcf86cd799439012","width":0}],"author_id":"6aaff2e8359000011551b35f","channel_id":"6aaff2e8359000011551b360","context_of":null,"created_at":"2026-09-28T09:00:00Z","depth":0,"from_agent":true,"parent":null,"reactions":[{"emoji":"string","user_id":"6aaff2e8359000011551b362"}],"root_id":null,"updated_at":"2026-09-28T09:00:00Z","workspace_id":"6aaff2e8359000011551b35e","permalink":"https://app.example.com/c/507f1f77bcf86cd799439011/m/507f1f77bcf86cd799439012"}
DELETE/api/messages/{message_id}
Delete a message or a task
Removes the message from the database: nothing is kept, nothing is hidden, and there is no way back - the counterpart of a task's status, which is how work is called off without losing what was said.
A task always has somebody: it is owned by whoever created it from the moment it exists, so this moves it from one member of the channel to another and never leaves it unowned. Any member of the task's channel may do it - handing it to yourself is the same call, and is what taking a task on looks like.
{"text":"Deployed 4f21c0a - the queue is draining again.","_id":"6aaff2e8359000011551b35f","attachments":[{"_id":"6aaff2e8359000011551b35f","bytes":0,"content_type":"string","created_at":"2026-09-28T09:00:00Z","filename":"string","height":0,"kind":"image","uncompressed_bytes":0,"url":"/api/attachments/507f1f77bcf86cd799439012","width":0}],"author_id":"6aaff2e8359000011551b35f","channel_id":"6aaff2e8359000011551b360","context_of":null,"created_at":"2026-09-28T09:00:00Z","depth":0,"from_agent":true,"parent":null,"reactions":[{"emoji":"string","user_id":"6aaff2e8359000011551b362"}],"root_id":null,"updated_at":"2026-09-28T09:00:00Z","workspace_id":"6aaff2e8359000011551b35e","permalink":"https://app.example.com/c/507f1f77bcf86cd799439011/m/507f1f77bcf86cd799439012"}
GET/api/messages/{message_id}/context
The context of a task
The messages grouped into this task, in the order they were given when it was grouped. They are not replies and not a thread: they are the material the task was made from, hidden from the channel listing by default and never deleted. Each one carries context_of set to the task id.
{"context":[{"_id":"6aaff2e8359000011551b35f","attachments":[{"_id":"6aaff2e8359000011551b35f","bytes":0,"content_type":"string","created_at":"2026-09-28T09:00:00Z","filename":"string","height":0,"kind":"image","uncompressed_bytes":0,"url":"/api/attachments/507f1f77bcf86cd799439012","width":0}],"author_id":"6aaff2e8359000011551b35f","channel_id":"6aaff2e8359000011551b360","context_of":null,"created_at":"2026-09-28T09:00:00Z","depth":0,"from_agent":true,"parent":null,"reactions":[{"emoji":"string","user_id":"6aaff2e8359000011551b362"}],"root_id":null,"updated_at":"2026-09-28T09:00:00Z","workspace_id":"6aaff2e8359000011551b35e","permalink":"https://app.example.com/c/507f1f77bcf86cd799439011/m/507f1f77bcf86cd799439012","text":"Deployed 4f21c0a - the queue is draining again.","type":"message"}],"task":{"_id":"6aaff2e8359000011551b35f","attachments":[{"_id":"6aaff2e8359000011551b35f","bytes":0,"content_type":"string","created_at":"2026-09-28T09:00:00Z","filename":"string","height":0,"kind":"image","uncompressed_bytes":0,"url":"/api/attachments/507f1f77bcf86cd799439012","width":0}],"author_id":"6aaff2e8359000011551b35f","channel_id":"6aaff2e8359000011551b360","context_of":null,"created_at":"2026-09-28T09:00:00Z","depth":0,"from_agent":true,"parent":null,"reactions":[{"emoji":"string","user_id":"6aaff2e8359000011551b362"}],"root_id":null,"updated_at":"2026-09-28T09:00:00Z","workspace_id":"6aaff2e8359000011551b35e","text":"Deployed 4f21c0a - the queue is draining again.","type":"message","permalink":"https://app.example.com/c/507f1f77bcf86cd799439011/m/507f1f77bcf86cd799439012"}}
POST/api/messages/{message_id}/dependencies
Make a task depend on another task
Records a dependency: this task requires, blocks or duplicates the task named by target (its id or its public key). A dependency is a decision somebody makes, so any member of the task's channel may make it, to themselves or on somebody else's behalf - the rule a status and an assignee already follow.
{"text":"Deployed 4f21c0a - the queue is draining again.","_id":"6aaff2e8359000011551b35f","attachments":[{"_id":"6aaff2e8359000011551b35f","bytes":0,"content_type":"string","created_at":"2026-09-28T09:00:00Z","filename":"string","height":0,"kind":"image","uncompressed_bytes":0,"url":"/api/attachments/507f1f77bcf86cd799439012","width":0}],"author_id":"6aaff2e8359000011551b35f","channel_id":"6aaff2e8359000011551b360","context_of":null,"created_at":"2026-09-28T09:00:00Z","depth":0,"from_agent":true,"parent":null,"reactions":[{"emoji":"string","user_id":"6aaff2e8359000011551b362"}],"root_id":null,"updated_at":"2026-09-28T09:00:00Z","workspace_id":"6aaff2e8359000011551b35e","permalink":"https://app.example.com/c/507f1f77bcf86cd799439011/m/507f1f77bcf86cd799439012"}
Removes the dependency on the task named by target (its id or its public key), whoever added it. Any member of the task's channel may do it. Removing a dependency that is not there changes nothing and announces nothing, so a retry is not an error; the task's references are untouched.
{"text":"Deployed 4f21c0a - the queue is draining again.","_id":"6aaff2e8359000011551b35f","attachments":[{"_id":"6aaff2e8359000011551b35f","bytes":0,"content_type":"string","created_at":"2026-09-28T09:00:00Z","filename":"string","height":0,"kind":"image","uncompressed_bytes":0,"url":"/api/attachments/507f1f77bcf86cd799439012","width":0}],"author_id":"6aaff2e8359000011551b35f","channel_id":"6aaff2e8359000011551b360","context_of":null,"created_at":"2026-09-28T09:00:00Z","depth":0,"from_agent":true,"parent":null,"reactions":[{"emoji":"string","user_id":"6aaff2e8359000011551b362"}],"root_id":null,"updated_at":"2026-09-28T09:00:00Z","workspace_id":"6aaff2e8359000011551b35e","permalink":"https://app.example.com/c/507f1f77bcf86cd799439011/m/507f1f77bcf86cd799439012"}
GET/api/messages/{message_id}/plugins
The plugins an agent's answer used
Which plugin instances an agent called while producing an answer - the disclosure a reader may open on an agent's message. Empty for a message a person wrote, and for an agent that called no tool. 403 for an external member, 404 when there is no such message.
{"text":"Deployed 4f21c0a - the queue is draining again.","_id":"6aaff2e8359000011551b35f","attachments":[{"_id":"6aaff2e8359000011551b35f","bytes":0,"content_type":"string","created_at":"2026-09-28T09:00:00Z","filename":"string","height":0,"kind":"image","uncompressed_bytes":0,"url":"/api/attachments/507f1f77bcf86cd799439012","width":0}],"author_id":"6aaff2e8359000011551b35f","channel_id":"6aaff2e8359000011551b360","context_of":null,"created_at":"2026-09-28T09:00:00Z","depth":0,"from_agent":true,"parent":null,"reactions":[{"emoji":"string","user_id":"6aaff2e8359000011551b362"}],"root_id":null,"updated_at":"2026-09-28T09:00:00Z","workspace_id":"6aaff2e8359000011551b35e","permalink":"https://app.example.com/c/507f1f77bcf86cd799439011/m/507f1f77bcf86cd799439012"}
{"text":"Deployed 4f21c0a - the queue is draining again.","_id":"6aaff2e8359000011551b35f","attachments":[{"_id":"6aaff2e8359000011551b35f","bytes":0,"content_type":"string","created_at":"2026-09-28T09:00:00Z","filename":"string","height":0,"kind":"image","uncompressed_bytes":0,"url":"/api/attachments/507f1f77bcf86cd799439012","width":0}],"author_id":"6aaff2e8359000011551b35f","channel_id":"6aaff2e8359000011551b360","context_of":null,"created_at":"2026-09-28T09:00:00Z","depth":0,"from_agent":true,"parent":null,"reactions":[{"emoji":"string","user_id":"6aaff2e8359000011551b362"}],"root_id":null,"updated_at":"2026-09-28T09:00:00Z","workspace_id":"6aaff2e8359000011551b35e","permalink":"https://app.example.com/c/507f1f77bcf86cd799439011/m/507f1f77bcf86cd799439012"}
Removes the reference to the task named by target (its id or its public key) - a mention somebody decided is not a link. Any member of the task's channel may do it, and removing one that is not there changes nothing.
{"text":"Deployed 4f21c0a - the queue is draining again.","_id":"6aaff2e8359000011551b35f","attachments":[{"_id":"6aaff2e8359000011551b35f","bytes":0,"content_type":"string","created_at":"2026-09-28T09:00:00Z","filename":"string","height":0,"kind":"image","uncompressed_bytes":0,"url":"/api/attachments/507f1f77bcf86cd799439012","width":0}],"author_id":"6aaff2e8359000011551b35f","channel_id":"6aaff2e8359000011551b360","context_of":null,"created_at":"2026-09-28T09:00:00Z","depth":0,"from_agent":true,"parent":null,"reactions":[{"emoji":"string","user_id":"6aaff2e8359000011551b362"}],"root_id":null,"updated_at":"2026-09-28T09:00:00Z","workspace_id":"6aaff2e8359000011551b35e","permalink":"https://app.example.com/c/507f1f77bcf86cd799439011/m/507f1f77bcf86cd799439012"}
POST/api/messages/{message_id}/replies
Reply to a message. Replies nest at most three levels below the root.
{"text":"Deployed 4f21c0a - the queue is draining again.","_id":"6aaff2e8359000011551b35f","attachments":[{"_id":"6aaff2e8359000011551b35f","bytes":0,"content_type":"string","created_at":"2026-09-28T09:00:00Z","filename":"string","height":0,"kind":"image","uncompressed_bytes":0,"url":"/api/attachments/507f1f77bcf86cd799439012","width":0}],"author_id":"6aaff2e8359000011551b35f","channel_id":"6aaff2e8359000011551b360","context_of":null,"created_at":"2026-09-28T09:00:00Z","depth":0,"from_agent":true,"parent":null,"reactions":[{"emoji":"string","user_id":"6aaff2e8359000011551b362"}],"root_id":null,"updated_at":"2026-09-28T09:00:00Z","workspace_id":"6aaff2e8359000011551b35e","permalink":"https://app.example.com/c/507f1f77bcf86cd799439011/m/507f1f77bcf86cd799439012"}
POST/api/messages/{message_id}/status
Move a task to another status
A task's status is a fixed set of five values: backlog (agreed, and deliberately not being worked on yet), todo (where every task starts), in_progress, done and discarded (dropped rather than finished). Any status may follow any other - a task closed by mistake has to be reopenable - so there are no transition rules.
{"text":"Deployed 4f21c0a - the queue is draining again.","_id":"6aaff2e8359000011551b35f","attachments":[{"_id":"6aaff2e8359000011551b35f","bytes":0,"content_type":"string","created_at":"2026-09-28T09:00:00Z","filename":"string","height":0,"kind":"image","uncompressed_bytes":0,"url":"/api/attachments/507f1f77bcf86cd799439012","width":0}],"author_id":"6aaff2e8359000011551b35f","channel_id":"6aaff2e8359000011551b360","context_of":null,"created_at":"2026-09-28T09:00:00Z","depth":0,"from_agent":true,"parent":null,"reactions":[{"emoji":"string","user_id":"6aaff2e8359000011551b362"}],"root_id":null,"updated_at":"2026-09-28T09:00:00Z","workspace_id":"6aaff2e8359000011551b35e","permalink":"https://app.example.com/c/507f1f77bcf86cd799439011/m/507f1f77bcf86cd799439012"}
POST/api/messages/{message_id}/summary
Summarise a task, and judge its references
Reads the task, the messages it was grouped from, its whole thread (with the pull requests, commits, CI results and comments in it), the pull-request comments and failed CI logs fetched from GitHub, and the tasks it depends on (each with its own status and activity) - but never a diff. It asks two services at once.
{"comments":0,"dependencies":0,"events":0,"logs":0,"messages":0,"model":"string","provider":"openai","referenced":0,"standup":{"blockers":"string","done":"string","left":"string"},"suggestion_model":"string","suggestion_threshold":0,"suggestions":[{"confidence":0,"key":"GEN-1","kind":null,"reason":"The message names somebody outside the channel.","suggested":true,"task_id":"6aaff2e8359000011551b363"}],"summary":"string","truncated":true}
POST/api/messages/{message_id}/tags
Set a task's tags
The tags a task carries, as the whole list: one call both adds and removes, and {"tags": []} clears every tag - so a client never has to say which of the two it meant, and re-sending what it just read changes nothing. Any member of the task's channel may do it, the rule the status and the assignee follow.
{"text":"Deployed 4f21c0a - the queue is draining again.","_id":"6aaff2e8359000011551b35f","attachments":[{"_id":"6aaff2e8359000011551b35f","bytes":0,"content_type":"string","created_at":"2026-09-28T09:00:00Z","filename":"string","height":0,"kind":"image","uncompressed_bytes":0,"url":"/api/attachments/507f1f77bcf86cd799439012","width":0}],"author_id":"6aaff2e8359000011551b35f","channel_id":"6aaff2e8359000011551b360","context_of":null,"created_at":"2026-09-28T09:00:00Z","depth":0,"from_agent":true,"parent":null,"reactions":[{"emoji":"string","user_id":"6aaff2e8359000011551b362"}],"root_id":null,"updated_at":"2026-09-28T09:00:00Z","workspace_id":"6aaff2e8359000011551b35e","permalink":"https://app.example.com/c/507f1f77bcf86cd799439011/m/507f1f77bcf86cd799439012"}
{"_id":"6aaff2e8359000011551b35f","attachments":[{"_id":"6aaff2e8359000011551b35f","bytes":0,"content_type":"string","created_at":"2026-09-28T09:00:00Z","filename":"string","height":0,"kind":"image","uncompressed_bytes":0,"url":"/api/attachments/507f1f77bcf86cd799439012","width":0}],"author_id":"6aaff2e8359000011551b35f","channel_id":"6aaff2e8359000011551b360","context_of":null,"created_at":"2026-09-28T09:00:00Z","depth":0,"from_agent":true,"parent":null,"reactions":[{"emoji":"string","user_id":"6aaff2e8359000011551b362"}],"root_id":null,"updated_at":"2026-09-28T09:00:00Z","workspace_id":"6aaff2e8359000011551b35e","permalink":"https://app.example.com/c/507f1f77bcf86cd799439011/m/507f1f77bcf86cd799439012","text":"Deployed 4f21c0a - the queue is draining again.","type":"message","insights":{"estimate":{"p50":"2026-09-28T09:00:00Z","p75":"2026-09-28T09:00:00Z","p90":"2026-09-28T09:00:00Z","p99":"2026-09-28T09:00:00Z","remaining_days":0},"generated_at":"2026-09-28T09:00:00Z","milestones":[{"action":"string","at":"2026-09-28T09:00:00Z","kind":"pull_request_linked","number":0,"repo":"string","sha":"string","summary":"string","url":"https://example.com/acme/pull/12"}],"milestones_omitted":0,"time_in_status":[{"millis":0,"status":"backlog"}],"work":{"commits":0,"messages":0,"pull_requests":0,"reactions":0}},"replies":[{"text":"Deployed 4f21c0a - the queue is draining again."}]}
One query across messages, tasks, commits, pull requests, runs and channels.
GET/api/search
Search the workspace
A hybrid search - vector and lexical at once - over what happened in the workspace. q is the whole search bar: free text, plus in:<channel>, from:<person>, since:<YYYY-MM-DD>, until:<YYYY-MM-DD>, type:<message|task|commit|pull_request|run|channel> and "quoted phrases"; anything the syntax does not know is searched as words.
What happened while a reader was away, in the threads they are part of.
GET/api/activity
Read your activity
Everything that happened in the threads you are part of, newest first: messages, replies and integration events (a commit, a pull request, a run) in a thread you wrote in or a task you are on, and the changes to those tasks (a status, a hand-over, an edit, a dependency). Your own acts are left out.
{"items":[{"actor_id":null,"at":"string","channel_id":"6aaff2e8359000011551b360","data":"string","kind":"message.created","message":{"text":"Deployed 4f21c0a - the queue is draining again."},"target_id":null}],"next":"string","seen_at":"string"}
POST/api/activity/seen
Mark your activity as seen
Moves your activity cursor to at, or to now when it is absent, so the feed answers only what happens next. Called when the reader opens the Activity section, with the newest item it showed - so an item that arrived between the read and this call is still shown next time.
The console's API is not documented here, because it is not yours to call: it is a different credential, for the people who run the platform, and it only ever answers to theirs.