
## Read messages

`GET /v1/chats/:dialogId/messages`

Returns dialog messages with cursor-based pagination.

## Parameters

| Parameter | Type | Required | Default | Description |
|----------|-----|:-----:|-----------|---------|
| `dialogId` (path) | string | yes | — | Dialog ID: numeric user ID for personal messages, `chatXXX` for group chats — the `dialogId` field of a [recent dialogs](/docs/chats/discovery/recent) row; a user ID comes from the [user list](/docs/entities/users/list). The special `me` alias — the current user's personal dialog |
| `limit` (query) | number | no | 20, in the v2 mode — 50 | Number of messages per page; values up to 200 are accepted. In the regular mode, a cloud portal returns at most 50 records per call, and in the v2 mode up to 200 — see "Known specifics" |
| `lastId` (query) | number | no | — | Cursor to load older messages: returns messages with an ID lower than the given one |
| `firstId` (query) | number | no | — | Cursor to load newer messages: returns messages with an ID higher than the given one. Rejected in the v2 mode |
| `format` (query) | string | no | — | `v2` turns on the v2 mode — see "The v2 mode" below. Any other value is ignored and the response is unchanged. A repeated `format` is not rejected: with `format=v1&format=v2` the last value wins, so the v2 mode is on, while `format=v2&format=v1` returns the regular-mode response. The bracket form `format[]=v2` turns on the v2 mode when all its values are `v2` |
| `order` (query) | string | no | `desc` | The v2 mode only: `desc` — messages older than `lastId`, `asc` — newer than `lastId` |

**The v2 mode.** `format=v2` reads the message history. `data` carries the v2 object — `messages`, `users`, `files`, `reactions` and other collections — and `hasNextPage`, with `camelCase` keys. Reactions arrive on the same page. The mode accepts only `format`, `limit` (from 1 to 200: an out-of-range value is clamped, and the passed and applied values are in `meta`), `lastId` and `order`. `firstId` is rejected with `400 INVALID_PARAMS` — page forward with `order=asc` and `lastId`. The `lastId` bound is exclusive: the page after 52523 starts at 52522. `hasNextPage: false` marks the end of the history. A request in this mode differs from the examples below only in the query string: `GET /v1/chats/chat42/messages?format=v2&limit=50&lastId=1002` returns 50 messages older than 1002. The authorization headers are the same. **The v2 mode may make you a member:** when the chat allows auto-join — a comment chat, a collab or a task chat, for example — the call adds the current user as a member, as [loading a chat](/docs/chats/messages/load) does. A `READONLY` key may use the v2 mode as a targeted exception. The user's access rights and the `im` scope are still checked. The read updates the employee's presence. Reading the history does not itself mark messages as read. These effects and their exact method boundaries are listed under [access rights](/docs/access-rights).

**Read-only key.** In `READONLY` mode, this request reads an existing chat both with and without `format=v2`: use its `chatN`, or a numeric peer ID confirmed in the same employee's recent dialogs. For `me`, the check requires an existing personal dialog. The check scans at most 4000 raw rows of recent dialogs. If the binding is not confirmed, the request returns `403 WRITE_BLOCKED_READONLY_KEY` without calling a method that could create a chat. The check's limits and the permitted read effects are described in [access rights](/docs/access-rights).

For `format=v2`, this exception for named reads applies only to an ordinary employee personal key, whether it uses a webhook or previously configured OAuth tokens. An application key with a user Bearer session, a management key, or a key whose owner type is unconfirmed receives `403 WRITE_BLOCKED_READONLY_KEY` in `READONLY` mode before the existing-chat binding check and before the named messenger method is called. Ordinary reads without these effects remain available to application keys. Read/write mode is unaffected. In regular mode without `format=v2`, the named-method exception does not apply: ordinary scope and user access checks remain, and the existing-binding check is still required for a read-only key.

## Examples

### curl — personal key

```bash
curl "https://vibecode.bitrix24.com/v1/chats/chat42/messages?limit=20" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### curl — OAuth application

```bash
curl "https://vibecode.bitrix24.com/v1/chats/chat42/messages?limit=20" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN"
```

### JavaScript — personal key

```javascript
const res = await fetch(
  'https://vibecode.bitrix24.com/v1/chats/chat42/messages?limit=20',
  {
    headers: {
      'X-Api-Key': 'YOUR_API_KEY',
    },
  }
)

const { success, data } = await res.json()
console.log('Messages:', data.messages)
console.log('Participants:', data.users)
```

### JavaScript — OAuth application

```javascript
const res = await fetch(
  'https://vibecode.bitrix24.com/v1/chats/chat42/messages?limit=20',
  {
    headers: {
      'X-Api-Key': 'YOUR_APP_KEY',
      'Authorization': 'Bearer USER_SESSION_TOKEN',
    },
  }
)

const { success, data } = await res.json()
```

## Response fields

The regular mode — without `format=v2`:

| Field | Type | Description |
|------|-----|---------|
| `success` | boolean | Always `true` on success |
| `data.chatId` | number | Chat ID |
| `data.messages` | array | Array of messages |
| `data.messages[].id` | number | Message ID |
| `data.messages[].chatId` | number | ID of the chat the message belongs to |
| `data.messages[].authorId` | number | Message author ID. `0` — system message |
| `data.messages[].date` | string | Sent date (ISO 8601) |
| `data.messages[].text` | string | Message text |
| `data.messages[].unread` | boolean | Not read by the current user |
| `data.messages[].uuid` | string\|null | Unique message identifier (set by the sender) |
| `data.messages[].replaces` | array | List of replaced message IDs |
| `data.messages[].params` | array\|object | Additional message parameters (formatting, system markers) |
| `data.messages[].disappearingDate` | string\|null | Message auto-deletion date (ISO 8601) or `null` |
| `data.users` | array | Dialog participants with profiles |
| `data.users[].id` | number | User ID |
| `data.users[].name` | string | Display name |
| `data.users[].active` | boolean | Whether the user is active in the Bitrix24 account |
| `data.files` | array | Files attached to messages |
| `meta.requestedLimit` | number | The passed `limit` before clamping. Present together with `appliedLimit` only when the passed value falls outside the range from 1 to 200 |
| `meta.appliedLimit` | number | The `limit` value applied — only when it was clamped to a bound |

The v2 mode — `format=v2`. This mode has no `data.chatId` field: every message carries the chat ID.

| Field | Type | Description |
|------|-----|---------|
| `success` | boolean | Always `true` on success |
| `data.messages` | array | Messages on the page |
| `data.messages[].id` | number | Message ID — the `lastId` cursor for the next page and the `messageId` for [messages around a message](/docs/chats/messages/context) |
| `data.messages[].chatId` | number | Chat ID — equals `data.chat.id` in the [chat loading](/docs/chats/messages/load) response |
| `data.messages[].authorId` | number | Author ID — the profile is in `data.users` of the same response or can be fetched [by user ID](/docs/entities/users/get). `0` — a system message |
| `data.messages[].date` | string | Sent date (ISO 8601) |
| `data.messages[].text` | string | Message text |
| `data.messages[].params` | object | Additional message parameters, keys in `camelCase` |
| `data.users` | array | Authors of the messages on the page |
| `data.files` | array | Files attached to the messages on the page |
| `data.reactions` | array | Reactions to the messages on the page — one item per message with reactions |
| `data.reactions[].messageId` | number | ID of a message in `data.messages` of the same response |
| `data.reactions[].reactionCounters` | object | Number of reactions per reaction code, for example `{"like": 2}` |
| `data.reactions[].reactionUsers` | object | IDs of the users who reacted, per reaction code. Only the first few are listed, not all of them. The profile is available [by user ID](/docs/entities/users/get) |
| `data.reactions[].ownReactions` | array | Reaction codes of the current user |
| `data.hasNextPage` | boolean | `false` — no more pages in the chosen direction |
| `meta.requestedLimit` | number | The passed `limit` before clamping. Present together with `appliedLimit` only when the passed value falls outside the range from 1 to 200 |
| `meta.appliedLimit` | number | The `limit` value applied — only when it was clamped to a bound |

Besides the ones listed, the v2 object may carry other messenger collections — for example, `additionalMessages` with the messages that the page's messages reply to, and `copilot`. Which collections are included depends on the Bitrix24 account.

## Response example

The regular mode:

```json
{
  "success": true,
  "data": {
    "chatId": 42,
    "messages": [
      {
        "id": 1001,
        "chatId": 42,
        "authorId": 5,
        "date": "2026-06-05T10:00:00+00:00",
        "text": "Good afternoon! How are you?",
        "unread": false,
        "uuid": null,
        "replaces": [],
        "params": [],
        "disappearingDate": null
      },
      {
        "id": 1002,
        "chatId": 42,
        "authorId": 7,
        "date": "2026-06-05T10:01:00+00:00",
        "text": "All good, thanks.",
        "unread": true,
        "uuid": null,
        "replaces": [],
        "params": [],
        "disappearingDate": null
      }
    ],
    "users": [
      {
        "id": 5,
        "active": true,
        "name": "John Brown",
        "firstName": "John",
        "lastName": "Brown",
        "workPosition": "Manager",
        "color": "#3bc8f5",
        "gender": "M",
        "bot": false,
        "type": "user"
      }
    ],
    "files": []
  }
}
```

### Response example — the v2 mode

```json
{
  "success": true,
  "data": {
    "messages": [
      {
        "id": 1002,
        "chatId": 42,
        "authorId": 7,
        "date": "2026-09-20T10:01:00+00:00",
        "text": "All done",
        "params": {}
      },
      {
        "id": 1001,
        "chatId": 42,
        "authorId": 5,
        "date": "2026-09-20T10:00:00+00:00",
        "text": "Please review the mockup",
        "params": {}
      }
    ],
    "users": [],
    "files": [],
    "reactions": [
      {
        "messageId": 1001,
        "reactionCounters": { "like": 1 },
        "reactionUsers": { "like": [7] },
        "ownReactions": []
      }
    ],
    "hasNextPage": true
  }
}
```

## Error response example

422 — no access to the dialog:

```json
{
  "success": false,
  "error": {
    "code": "BITRIX_ERROR",
    "message": "You do not have access to the specified dialog",
    "b24Code": "ACCESS_ERROR"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|---------|
| 422 | `BITRIX_ERROR` | Bitrix24 returned an error (text in the `message` field). In the regular mode both "dialog not found" and "no access" arrive this way. The portal code is in `error.b24Code` |
| 403 | `BITRIX_ACCESS_DENIED` | The v2 mode: the user has no access to the chat — Bitrix24 refused with the `ACCESS_DENIED` code |
| 404 | `ENTITY_NOT_FOUND` | Bitrix24 returned a NOT_FOUND error. In the v2 mode, the portal error code arrives in `error.b24Code` |
| 400 | `INVALID_PARAMS` | The v2 mode: `firstId`, a parameter outside the allowed set (`format`, `limit`, `lastId`, `order`), a repeated parameter other than `format`, or a non-numeric value. Checked before any call to Bitrix24 |
| 502 | `BITRIX_UNAVAILABLE` | Bitrix24 is unavailable or returned a server error |
| 502 | `ME_ALIAS_RESOLUTION_FAILED` | Failed to resolve the user when using the `me` alias |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | Read-only mode: the bounded window of recent dialogs did not confirm an existing chat, or the supplied alias could create a chat or is ambiguous. The method that would create the chat is not sent to the portal |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | The v2 mode: the named read in `READONLY` mode is unavailable to an application key, a management key, or a key whose owner type is unconfirmed |
| 403 | `SCOPE_DENIED` | The key is missing the `im` scope |
| 401 | `TOKEN_MISSING` | The API key has no Bitrix24 tokens configured |

The full list of common API errors — [Errors](/docs/errors).

## Known specifics

**The page ceiling in the regular mode is 50 messages per call.** The `limit` parameter accepts values up to 200, but in the regular mode a cloud portal returns at most 50 records per call regardless of the value passed. On-premise portals may return more. Read history beyond the latest 50 messages with the `lastId` cursor — page by page. The v2 mode has no such ceiling: a call returns as many messages as passed in `limit`, up to 200.

**Message order.** Messages are returned in descending order — from newest to oldest. In the v2 mode with `order=asc` the order is reversed: from oldest to newest, starting with the one after `lastId`.

**The v2 mode and response keys.** Keys that name fields are converted to `camelCase`: the `FILE_ID` message parameter arrives as `fileId`. Keys that are data stay as they are: the keys of the `data.copilot.roles` dictionary are role codes, and `copilot_assistant` arrives as `copilot_assistant` — the same value that `role` carries on a chat and a message in `data.copilot`, so a role is found directly as `roles[role]`. Fields inside a role are converted to `camelCase`. A request for a chat that does not exist in the Bitrix24 account returns `422 BITRIX_ERROR` with `error.b24Code` set to `CHAT_NOT_FOUND`.

## See also

- [Messages](/docs/chats/messages)
- [Send a message](/docs/chats/messages/send)
- [Chat discovery](/docs/chats/discovery)
