Skip to main content

The channel and message resources

TeamChat is internal, workspace-to-workspace messaging: a channel is one thread of conversation scoped to your organization, and a message is one post inside it. A channel carries its id, name, and (optional) description; each message carries a body plus two fields your own integrations write — mentions (resolved user ids) and attachments. Membership is the authorization boundary: you must be a member of a channel to read its history or post to it, and only owner/admin roles manage members. When you create a channel, you join it automatically as owner. Every response on this page follows the same { data, meta } envelope — a data payload plus a meta block carrying request_id and timestamp — so the examples below hold for every endpoint here. Errors follow Devotel Orbit’s { error, meta } envelope, shown once under Error envelope.

Worked sequence: create, post, read back

The run-through every integration does first: create the channel, post a message that mentions a teammate, then read the history back with sender identity hydrated.

1. Create the channel

POST /api/v1/team-chat/channels
Only name is required (1–120 characters). description is optional (up to 500 characters). The caller is inserted as the channel’s owner member. Request
The response carries my_role: "owner" as a convenience — the channels list endpoint returns the same field on every row you are a member of.

2. Post a message with @-mentions

POST /api/v1/team-chat/channels/{channelId}/messages
A message needs a body or at least one attachment — an empty post is rejected with 422. body accepts plain text or Slack-style markdown (**bold**, _italic_, ~strike~, back-tick code blocks, and lists) and is capped at 8000 characters. mentions is an array of resolved user ids — write @name in the body text for the reader and pass the same person’s user id in mentions so the notification fan-out knows whom to ping without re-parsing your text. Request
edited_at is null until somebody edits the message. Posting a message also pushes a realtime refresh hint to every open dashboard and creates a persistent notification for each other channel member, so an empty client poll is never required to learn about a new post.

3. Read the channel history back

GET /api/v1/team-chat/channels/{channelId}/messages
Returns thread roots (top-level posts) newest-first, bounded by the limit query parameter — default 50, maximum 200. Replies live inside their parent’s thread so a busy thread never floods the channel feed; each root carries reply_count and last_reply_at when it has replies. The GET hydrates each row with the sender’s human-readable identity from your user directory — sender_name and sender_avatar_url — so a UI renders a person, not the internal sender_user_id. When the sender row was deleted (or was a system sender) both fields come back null and the id stays.

4. React to a message

POST /api/v1/team-chat/channels/{channelId}/messages/{messageId}/reactions
One reaction per user per emoji — repeating the POST with the same emoji is a no-op. The reaction feeds the per-message summary your UI polls with GET …/messages/reactions?message_ids=…. Request

Error envelope

Schema failures return 422 with a VALIDATION_ERROR code and the offending fields listed under details. The classic case is an empty message — no body, no attachments:
422
Reading or posting to a channel you do not belong to returns 403 NOT_A_MEMBER — handle it before you code a retry loop. On the creation path a 500 means the insert itself failed; channel names and descriptions are server-scrubbed only for length, so a narrower client-side validation never shadows a server accept.