Send a message
POST/chat.post
Send a message to a chat. Messages can be plain markdown text, rich Block Kit layouts, or polls.
Destination (ONE of the following is required):
chatId- Post to an existing chat by its IDgroupId- Post to a group chatuserIds- Post to a DM or Multi-DM with the specified users
You must specify exactly one destination. Specifying multiple destinations (e.g., both chatId and groupId) will return a 400 error.
Mentions use Slack's token syntax with Slack's semantics: <@ID> mentions a principal (a user or bot, e.g. <@7861a4c6-765a-495d-898d-fae3d8fbba2d> — resolvable via user.info), <!subteam^ID> mentions a group or channel, notifying its members (resolvable via group.info), and <!channel> notifies everyone in the chat.
When rendered in the client, the tag will automatically be replaced with the human-readable display name (or "everyone" for <!channel>).
On write, either token form is accepted for any mentionable ID; the legacy <@all> broadcast alias is accepted; and a Slack-style |label suffix (e.g. <@7861a4c6-…|Rob>, <!subteam^59c1a4d2-…|@eng>) is accepted and ignored — the mentioned entity's live display name is always used. Write-side acceptance is identical on every API version. Messages read back always carry bare canonical tokens, and <!channel> for the broadcast — on API versions from 2026-08-07; clients pinned to older versions read the older grammar (<@ID> for every mention, <@all>). Slack forms Roam does not implement are reserved and stay literal text: <#ID> channel links, <!here>, and <!everyone>.
Custom sender (optional): see the Sender Profiles guide.
sender.name/sender.imageUrlare per-message display overrides, stored on the message itself.sender.idauthors the message as a configured bot persona (Roam Administration > Developer > edit your app > Add Bot Persona). Ids that don't match a configured persona are accepted and ignored — the message is authored by the app's root identity. Sending never creates or renames personas.- Personal access tokens: Reject the
senderfield with 400. PATs always post as their personal bot.
Access: Organization tokens can post to chats the bot is a member of,
and to public groups in the workspace without joining. Personal tokens
can post only where the owner is a member (403 not_in_chat for an
unjoined public group).
Required scope: chat:send_message or chat:write
OpenAPI Spec: chat-v1.json
Request
Responses
- 200
- 400
- 401
- 403
- 405
- 413
- 500
Message posted or scheduled successfully. Immediate posts return
chatId (and timestamp when sync is set). Scheduled posts
(sendAt) return chatId, scheduledMessageId, and sendAt.
All success bodies include "ok": true — see
Responses and Errors.
Bad request. Common causes:
- No destination specified (chatId, groupId, or userIds required)
- Multiple destinations specified (only one allowed)
threadTimestampdoes not correspond to an existing message- Both
threadKeyandthreadTimestampwere provided (they are mutually exclusive) threadKeyexceeds 64 charactersreplyTimestampprovided on a non-text message (blocksorpoll)replyTimestampused outside a DM or a channel threadsender.nameexceeds 128 characterssender.imageUrlis not an absolute HTTP(S) URL- Personal access token provided a
senderfield (access_mode_not_supported) - Multiple content types provided (only one of
text/items,blocks, orpollallowed) - An
assetIdwas not found, not owned by your app, or not a file asset - An
assetIdis still processing — retry once its upload completes blocksarray exceeds 10 blocks or 8,000 bytes- Invalid block structure (see Block Kit guide)
- Interactive buttons sent without an Interactivity URL configured
- Invalid
colorvalue - Poll question empty or exceeds 256 characters
- Poll has fewer than 2 options, or an option exceeds 128 characters
sendAtnot on a 15-minute UTC boundary, not in the future, more than 30 days ahead, or combined withsync/poll/threadKey/replyTimestamp
Presented invalid authentication credentials.
Forbidden (not_in_chat / not_in_group). Organization tokens may
still post to a public group in the workspace without joining. Personal
tokens require the owner to be a member.
An unsupported method was requested.
Message content exceeds the maximum allowed size.
An internal error occurred.