Post an ephemeral message
POST/chat.postEphemeral
Post an ephemeral message — visible to a single member of a chat, with an
"Only you can see this" header — without posting anything the other members can
see. This is the standard way for a bot to respond privately in a shared
channel (the Roam equivalent of Slack's chat.postEphemeral).
The target userId must be a member of the chat (for channels: a member of the
backing group), otherwise the request fails with user_not_in_chat.
text is always rendered as GitHub-flavored markdown. Mention markup
(<@USER_ID>) is not supported in ephemeral messages. Block Kit blocks
are not currently supported.
Delivery semantics — read before using:
- Desktop and web only. Mobile clients do not display ephemeral messages, and no mobile push notification is sent. A recipient who only uses Roam on mobile will never see the message.
- Best-effort, at-most-once. The message is delivered in real time to the recipient's connected clients, and to recently-active offline clients when they reconnect. A recipient who has been offline for several days (or has never signed in on that device) silently misses it. There are no retries and no delivery receipt.
- Transient. The message is never stored server-side. It disappears when
the recipient restarts their app, and it never appears in
/chat.historyor/chat.search. - Not addressable. It cannot be edited or deleted:
/chat.updateand/chat.deleteagainst its(chatId, timestamp)returnmessage_not_found. - No webhooks. Posting an ephemeral message never triggers a
chat.messageevent, so it cannot leak to org-wide webhook consumers.
Do not use ephemeral messages for anything the recipient must durably receive —
use a DM (/chat.post with userIds) for that.
Custom sender (optional): same semantics as /chat.post —
sender.name / sender.imageUrl apply a per-message display override, and
sender.id authors the message as a configured bot persona (unknown ids
are accepted and ignored). Personal access tokens reject the sender
field. See the Sender Profiles guide.
Required scope: chat:send_message or chat:write
Access: Organization and Personal. The organization bot or
personal-token owner must be a member of the chat (403 not_in_chat
otherwise) — unlike /chat.post, there is no
public-group carveout. Personal tokens send as the user's personal bot
and reject the sender field.
OpenAPI Spec: chat-v1.json
Request
Responses
- 200
- 400
- 401
- 403
- 404
- 405
- 413
- 500
Ephemeral message accepted for delivery. The (chatId, timestamp) pair is
the identity the recipient's client renders the message under; it is not
addressable by any other endpoint. All success bodies include "ok": true —
see Responses and Errors.
Bad request. Common causes:
textmissing (missing_parameter)chatIdoruserIdis not a UUID (invalid_parameter)- The target user is not a member of the chat (
user_not_in_chat) threadTimestampused in a DM or Multi-DM (invalid_parameter)- The channel's group is archived (
is_archived) sender.nameexceeds 128 characters, orsender.imageUrlis not an absolute HTTP(S) URL (invalid_parameter)- Personal access token provided a
senderfield (access_mode_not_supported)
Presented invalid authentication credentials.
Forbidden. The token lacks the required scope, or the bot lacks access to
the target chat (not_in_chat / not_in_group); a public channel in a
different Roam responds group_not_found, indistinguishable from a
missing group.
Not found: the chat does not exist (chat_not_found), or userId does
not refer to a user or visitor address (user_not_found).
An unsupported method was requested.
Message text exceeds the maximum allowed size (msg_too_long).
An internal error occurred.