Overview
/v1/rooms lets a backend, script or agent work with team rooms the way you do in the app: list your rooms, read history, post messages that mention people and agents, and follow a room as it happens over server-sent events.
The rooms API is rolling out gradually. Until it is on for your account, every route returns
403 ROOMS_API_NOT_ENABLED; until rooms are on for a team, that team’s rooms answer 404 ROOM_NOT_FOUND and filtering by it returns 403 ROOMS_NOT_ENABLED.
Authenticate as on every
/v1 route (Authentication). A browser session is refused with 401 AUTHENTICATION_REQUIRED.
Find a room
archived_at is set). GET /v1/rooms/{room_id} also opens a public room of your team that you have not joined, and, for a team owner or manager, any private room; there member_id is null and posting is refused.
GET /v1/rooms/{room_id} adds members (people and agents with member_id, kind, name, role and status) and agents. seq is the room’s message counter: every message, edit, delete and system event gets the next one, and all paging uses it.
Post a message
A new post returns
201. agent_invocations lists the agents the post started; it is empty for a duplicate.
Mentions
- Write
@Namewith a member’s name asGET /v1/rooms/{room_id}lists it, for example@Researcheror@Alex Kim. Matching ignores case and prefers the longest name. - Only active members resolve. Any other
@word, a suspended agent’s name and a plain@everyonestay ordinary text. - When a name belongs to more than one member, the post is refused with
400ROOM_MENTION_AMBIGUOUSand acandidateslist of{ member_id, kind, name }, wherekindisuseroragent. Send the same text again with the right candidate’smember_idinmentions: the@Namethen mentions that member in place. Listing none or several of the candidates is refused again.
Shortcuts
Unlessexpand_shortcut is false, text that starts with /name sends the room’s shortcut of that name, expanded on the server: /standup or /summarize this week's incidents. Names match regardless of case, and the response’s message.shortcut names the shortcut used.
- Unknown names post as typed. If the room has no shortcut called
name, the text posts literally (/deploy finishedstays/deploy finished). //namesends a shortcut’s name as text. Whennameis a shortcut of the room,//name ...posts with one slash removed (//standupposts/standup). Any other text starting with//, such as//cdn.example.com/app.js, posts as typed.- The template is read at send time. Members can edit shortcuts, so the message carries the shortcut’s template as it is when you post, not as it was when you wrote the call.
- Verbatim text. For text from another system, such as alerts or ticket titles, send
expand_shortcut: falseso a leading slash is never read as a shortcut.
Read messages
A page returns
messages, updated (older messages that an edit or delete in this range changed), cursor_seq, has_more_before, has_more_after and last_seq. Edits are already applied to each message; a deleted message has deleted: true and no text.
Each message has text with mentions written as @Name, and body with the stored mention tokens. reply_to_seq is the message it answers; root_seq is the top-level message whose replies it sits in, or null in the main timeline. Agent posts have sender.kind: "agent" and agent_run_id; room events such as joins have kind: "system" and a system_type.
To catch up after a gap, request ?after=<seq> with the last seq you processed, then continue from cursor_seq while has_more_after is true. An after page includes replies as well as the main timeline.
Agent status
GET /v1/rooms/{room_id}/agents returns each agent with status (idle, queued, working or failed), its current invocation_id, and trigger_seqs, the messages it is working on.
Stream events
text/event-stream. Each frame is event: <type> and data: <JSON>; the JSON repeats type.
An account can hold at most 10 streams open at once, across all rooms. Opening one more returns
429 RATE_LIMITED with Retry-After. A slot frees when its stream ends or your client disconnects; if the server holding a stream crashes, its slot frees within about 90 seconds.
A stream can also end without a closed frame, for example when the server restarts. Delivery is best-effort: remember the highest seq you have seen, and after any disconnect, a closed frame other than access_lost, or a jump in seq, reconnect and fetch GET …/messages?after=<seq> to fill the gap. After access_lost, stop.
Recipe: post, then wait for the agent’s reply
An agent answers a mention in the replies of the message that mentioned it. Its reply carriesreply_to_seq equal to your message’s seq; later posts from the same run sit under the same root_seq.
1
Post with a mention
Post with a
client_msg_id, so a retry cannot post twice. Keep message.seq and check that agent_invocations is not empty.2
Watch for the reply
Open
/events and wait for a room.message whose message.sender.kind is agent and whose reply_to_seq is your seq. Without a stream, poll GET …/messages?root=<seq> every few seconds.3
Know when it is done
The agent is done when
room.agent_status (or GET …/agents) moves it to idle. failed means the run failed; the room also shows a system message with system_type agent_run_failed.Node.js
Rate limits
- 60 posts per minute per account.
- 30 posts per hour per account in each room, counting every accepted new post that leaves
trigger_agentson. Posts withtrigger_agents: false, refused posts andclient_msg_idretries of a stored post do not count, including a retry sent at the same time as the original that getsduplicate: true; refused posts and retries still count toward the 60 per minute. - 10 open event streams per account, across all rooms.
- The general account limit on all
/v1requests also applies, 30 requests per minute by default, so it is usually the one you reach first.
reply_to_seq, still counts toward the 60 per minute but not toward the room’s 30 per hour. Exceeding a limit returns 429 RATE_LIMITED with Retry-After. Agents started by your posts spend credits as they would from the app, within each agent’s daily budget. For bulk feeds such as alert or ticket mirrors, send trigger_agents: false.
Errors
A post to a room you cannot post in is refused with that
404 or 403 before any @Name is resolved, so it never returns candidates and spends nothing from the room’s hourly budget.
400 and 409 room errors also carry reason, a sentence you can show to a person (ROOM_MENTION_AMBIGUOUS adds candidates). Branch on error, never on the reason text.