# Get chat messages

`GET /chat.history`

## Description

List messages in a chat, filtered by date range (after/before).

Messages with `contentType` of `text`, `voice`, or `poll` are returned. System messages and other content types are excluded.

**Specify ONE of the following:**
- `chatId` - Fetch from an existing chat by its ID
- `groupId` - Fetch from a group chat
- `userIds` - Fetch from a DM or Multi-DM with the specified users

You must specify exactly one destination. Specifying multiple (e.g., both `chatId` and `groupId`) will return a 400 error.

The ordering of results depends on the filter specified:

- When no parameters are provided, the most recent messages are returned,
  sorted in reverse chronological order. This is equivalent to specifying `before`
  as NOW and leaving `after` unspecified.

- If `after` is specified, the results are sorted in forward chronological order.

Either dates or datetimes may be specified. Date-only inputs (`YYYY-MM-DD`)
are interpreted in the caller's timezone (see
[Timezone handling](/docs/guides/migration-v0-to-v1#timezone-handling)).

**Access:** Organization tokens need to be a **member** of the chat
(`403` `not_in_chat` otherwise). Personal tokens can read any chat the
owner can, including public groups in their roam they have not joined.

**Required scope:** `chat:history`

Every returned sender includes `userId` plus `userType`. The ID resolves
through [`user.info`](/docs/api/user-info) with the same credentials.

---

**OpenAPI Spec:** [chat-v1.json](https://developer.ro.am/chat-v1.json)

## Authentication

```
Authorization: Bearer YOUR_API_KEY
```

## Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `chatId` | query | string | No | The chat ID to fetch messages from. Either chatId, groupId, or userIds must be specified. |
| `groupId` | query | string | No | Group chat ID to fetch messages from. Either chatId, groupId, or userIds must be specified. |
| `userIds` | query | string[] | No | User IDs to fetch DM/Multi-DM messages with. Either chatId, groupId, or userIds must be specified. |
| `threadTimestamp` | query | number | No | Read replies of the message with this timestamp. Specified in microseconds. |
| `after` | query | string | No | The datetime to begin listing messages (YYYY-MM-DD or RFC-3339). Date-only values are interpreted in the caller's timezone. Sub-millisecond precision on datetimes is truncated. Defaults to "no filter" |
| `before` | query | string | No | The datetime until which to list messages (YYYY-MM-DD or RFC-3339). Date-only values are interpreted in the caller's timezone. Sub-millisecond precision on datetimes is truncated. Defaults to "now".  |
| `cursor` | query | string | No | Opaque pagination cursor from a previous response's `nextCursor`. |
| `limit` | query | integer | No | Number of messages to return (default 10, max 200). |
| `expand` | query | string | No | Comma-separated fields to expand. Supported: `addresses` — include an `addresses` map resolving the sender (`userId`) and mentioned IDs on each message to their display info.  |

## Responses

### 200 - Messages retrieved successfully

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `chatId` | string | Yes | The chat ID |
| `nextCursor` | string | No | A cursor to fetch the next page of results |
| `threadTimestamp` | integer | No | The thread timestamp being read, echoed from the request (present only when reading a thread). |
| `messages` | ChatMessage[] | Yes |  |
| `addresses` | object | No | Resolved address objects keyed by ID, for the senders and mentioned entities in this response. Included only when `expand=addresses` is requested.  |

**ChatMessage** (array item):

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `type` | "message" | No | Message type identifier |
| `userId` | string | Yes | Principal ID of the message sender. Resolve with `user.info`. |
| `userType` | "user" | "bot" | Yes | Principal type of `userId`; always equals `user.info.type` for the same credentials. Use it to prevent bot loops without another lookup. |
| `chatId` | string | No | ID of the chat the message belongs to |
| `timestamp` | integer | Yes | Message key as Unix microseconds |
| `threadTimestamp` | integer | No | Unix microseconds timestamp of the parent message (for thread replies) |
| `replyTimestamp` | integer | No | Timestamp of the message this one quotes — a DM or channel-thread quoted reply, set via the `replyTimestamp` request field on chat.post. Omitted otherwise. |
| `ephemeral` | boolean | No | Whether the message is ephemeral. Always omitted (false) in chat.history, chat.search, and chat.link.resolve responses — ephemeral messages (posted via chat.postEphemeral) are never persisted, so they |
| `text` | string | No | Text of the message, formatted as github-flavored markdown. Mention tokens use Slack's syntax: `<@ID>` is a principal (user or bot — resolve with `user.info`), `<!subteam^ID>` is a group or channel (r |
| `contentType` | "text" | "voice" | "block" | "poll" | Yes | Type of message content: `text` (markdown body, optionally with items), `voice` (voice note), `block` (rich block layout), or `poll`. |
| `items` | ChatItem[] | No | Items attached to this message |
| `poll` | object | No | Poll content, present when contentType is `poll`. |
| `voice` | object | No | Voice-note content, present when contentType is `voice`. |
| `blocks` | object[] | No | Rich block layout, present when contentType is `block`. |
| `color` | string | No | Accent color for a block message, present when contentType is `block`. Omitted otherwise. |
| `replyCount` | integer | No | Number of replies in this message's thread. Omitted when zero. |
| `sender` | object | No | Per-message sender display override supplied at send time via the request's `sender` field. Present only when the stored message carries one. Additive: `userId` remains the authoring identity — render |
| `mentions` | string[] | No | Flat, order-preserving, de-duplicated list of everything referenced by mention tokens in `text`: bare address UUIDs (from both `<@ID>` principal and `<!subteam^ID>` group tokens), plus the literal `al |

**ChatItem** (array item):

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `id` | string | Yes | UUID identifying this item |
| `type` | "photo" | "blob" | Yes | Type of item:  - **photo**: Images with inline preview and thumbnail   - image/jpeg, image/png, image/gif, image/webp  - **blob**: Any other file type (download only, no preview)   - application/octet |
| `mime` | string | No | MIME type of the file (e.g., "application/octet-stream"). May be omitted for photo items where the type is inferred from the image format.  |
| `created` | string | Yes | Timestamp when the item was created |
| `name` | string | Yes | Name of the item (typically the filename). |
| `url` | string | Yes | URL for the uploaded item. |
| `thumbnail` | string | No | URL for a thumbnail of the uploaded item (photo type only). This may be equal to the item's main URL if it is suitable to use as a thumbnail.  |
| `size` | integer | No | Size of the item in bytes |
| `width` | integer | No | Width in pixels (images only) |
| `height` | integer | No | Height in pixels (images only) |

**poll**:

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `question` | string | No | The poll question. |
| `options` | object[] | No | The poll answer options. |
| `allowMultipleAnswers` | boolean | No | Whether voters can select multiple options. |
| `closesAt` | string | No | When the poll closes (RFC-3339). Omitted if no close time is set. |

**voice**:

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `audioUrl` | string | Yes | URL of the voice-note audio (m4a). |
| `duration` | integer | Yes | Duration of the voice note in milliseconds. |
| `transcript` | string | No | Text transcript of the voice note. Omitted if not yet available. |

**sender**:

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `name` | string | No | Display name override for this message. |
| `imageUrl` | string | No | Avatar URL override for this message. |


#### Example Response

```json
{
  "chatId": "295155ae-7df5-4ed5-9ebc-89a170559c81",
  "messages": [
    {
      "type": "message",
      "contentType": "text",
      "userId": "ad1e9cc0-0ffd-47e5-895c-2630a73327b4",
      "userType": "user",
      "timestamp": 1765602474760032,
      "text": "Hey team, can we sync up on the roadmap this afternoon?"
    },
    {
      "type": "message",
      "contentType": "text",
      "userId": "709b8a57-70bc-427a-b6f0-b16ba5297f8c",
      "userType": "bot",
      "timestamp": 1765602480123456,
      "text": "Sure, I'm free at 3pm!"
    }
  ],
  "nextCursor": "YzE6MTc2NTYwMjQ3NDc2MDAzMg"
}
```

### 400 - Bad request.

### 401 - Presented invalid authentication credentials.

### 403 - Organization token is not a member of this chat (`not_in_chat`).
Personal tokens receive this only when the owner cannot read the chat
(private / DM they are not in).


### 405 - An unsupported method was requested.

### 500 - An internal error occurred, including a stored sender that cannot resolve as a visible principal. The endpoint does not return a partial page.

---

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