# Post an ephemeral message

`POST /chat.postEphemeral`

## Description

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.history`](/docs/api/chat-history) or [`/chat.search`](/docs/api/chat-search).
- **Not addressable.** It cannot be edited or deleted:
  [`/chat.update`](/docs/api/chat-update) and [`/chat.delete`](/docs/api/chat-delete)
  against its `(chatId, timestamp)` return `message_not_found`.
- **No webhooks.** Posting an ephemeral message never triggers a
  [`chat.message`](/docs/webhooks/chat-message) event, 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`](/docs/api/chat-post) with `userIds`) for that.

**Custom sender (optional):** same semantics as [`/chat.post`](/docs/api/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](/docs/guides/sender-profiles).

**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`](/docs/api/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](https://developer.ro.am/chat-v1.json)

## Authentication

```
Authorization: Bearer YOUR_API_KEY
```

## Request Body

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `chatId` | string | Yes | The chat to post into. Use [`/chat.list`](/docs/api/chat-list) or a `chat.message` webhook payload to obtain chat IDs. |
| `userId` | string | Yes | The user who should see the message. Must be a member of the chat. |
| `threadTimestamp` | integer | No | Show the ephemeral message inside an existing thread instead of the main channel view. Channels only — returns 400 in DMs and Multi-DMs. The value is not validated against an existing thread: pass a r |
| `text` | string | Yes | Message text in GitHub-flavored markdown (always rendered as markdown; there is no plain-text mode). Maximum 8,000 bytes. Mention markup is not supported.  |
| `sender` | Sender | No |  |

**Sender**:

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `id` | string | No | Code of a configured bot persona to author the message as (trimmed, case-insensitive). Omitted, empty, or `_` posts as the app's root identity. Unconfigured ids are accepted and ignored — supplying an |
| `name` | string | No | Display name override for this message only (max 128 characters). Does not rename the app or persona.  |
| `imageUrl` | string | No | Avatar URL override for this message only. Must be an absolute HTTP(S) URL.  |

## Responses

### 200 - 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](/docs/guides/responses-and-errors).


| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `ok` | "true" | No |  |
| `chatId` | string | No | ID of the chat the message was delivered into |
| `timestamp` | integer | No | Message timestamp in microseconds |
| `threadTimestamp` | integer | No | Echoed thread timestamp when the message was posted into a thread |

### 400 - Bad request. Common causes:
- `text` missing (`missing_parameter`)
- `chatId` or `userId` is not a UUID (`invalid_parameter`)
- The target user is not a member of the chat (`user_not_in_chat`)
- `threadTimestamp` used in a DM or Multi-DM (`invalid_parameter`)
- The channel's group is archived (`is_archived`)
- `sender.name` exceeds 128 characters, or `sender.imageUrl` is not an absolute HTTP(S) URL (`invalid_parameter`)
- Personal access token provided a `sender` field (`access_mode_not_supported`)


### 401 - Presented invalid authentication credentials.

### 403 - 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.


### 404 - Not found: the chat does not exist (`chat_not_found`), or `userId` does
not refer to a user or visitor address (`user_not_found`).


### 405 - An unsupported method was requested.

### 413 - Message text exceeds the maximum allowed size (`msg_too_long`).

### 500 - An internal error occurred.

---

*Machine-readable API documentation.*
*Full documentation: https://developer.ro.am/docs/api/chat-post-ephemeral*
