
## Recent dialogs

`GET /v1/chats/recent`

Returns the list of the current user's recent dialogs: pinned dialogs first, then by the time of the last message.

The generic `filter` parameter is not applied by this endpoint. By default, the API preserves the previous HTTP 200 response and data, adds `Warning: 299 - "filter is not supported on this endpoint and was ignored"`, and records unsupported-filter use in the server log.
Parameter validation for `format=v2` remains unchanged.

## Parameters

| Parameter | Type | Required | Default | Description |
|----------|-----|:-----:|-----------|---------|
| `format` (query) | string | no | — | `v2` turns on the v2 mode — see "The v2 mode" below. Any other value is ignored, and the legacy response is returned. A repeated `format` is not rejected: with `format=v1&format=v2` the last value is used, so the v2 mode is on, while `format=v2&format=v1` returns the legacy response. The bracket form `format[]=v2` turns on the v2 mode when all its values are `v2` |
| `skipOpenLines` (query) | string | no | — | Exclude Open Channels conversations. Accepts `true`, `Y`, or `y` |
| `skipChat` (query) | string | no | — | Exclude group chats. Accepts `true`, `Y`, or `y` |
| `skipDialog` (query) | string | no | — | Exclude private dialogs. Accepts `true`, `Y`, or `y` |
| `unreadOnly` (query) | string | no | — | Only dialogs with unread messages. Accepts `true`, `Y`, or `y` |
| `onlyOpenLines` (query) | string | no | — | Only Open Channels conversations. Accepts `true`, `Y`, or `y` |
| `onlyCopilot` (query) | string | no | — | Only chats with the AI assistant. Accepts `true`, `Y`, or `y` |
| `onlyChannel` (query) | string | no | — | Only channels. Accepts `true`, `Y`, or `y` |
| `lastMessageDate` (query) | string | no | — | Date of the last message for pagination (ISO 8601). In the v2 mode, it is the cursor defined by the rule below, in the `YYYY-MM-DDTHH:MM:SS` format with an offset or `Z` and without fractional seconds |
| `updatedAfter` (query) | string | no | — | Delta mode: return dialogs changed at or after the given instant. Date in ISO 8601 with an explicit offset or `Z`. Not compatible with `offset` or `lastMessageDate` |
| `limit` (query) | number | no | 50 | Number of items in the response. Maximum 200. In the v2 mode, the range is 50 to 200: a smaller value is raised to 50 and a larger one is clamped to 200, and in both cases the passed and applied values are returned in `meta` |
| `offset` (query) | number | no | `0` | Offset for paging |

**Delta mode.** The `updatedAfter` parameter switches the endpoint into a "what changed since the given instant" mode: `GET /v1/chats/recent?updatedAfter=2026-06-28T00:00:00Z`. The response contains an array of dialogs whose `dateUpdate` is not earlier than the given instant. The boundary is inclusive — a dialog whose `dateUpdate` equals the given instant is included, so if you advance the cursor to the highest value you have seen, that boundary record arrives again on the next call.

The page size in this mode is controlled by the server: one page of up to 200 dialogs is read. The `limit` you pass does not affect it and is returned in `meta.requestedLimit` together with the applied `meta.appliedLimit`.

When `meta` contains `truncated` with the value `true`, the delta may be incomplete: more dialogs exist beyond the returned page, or the response shape could not be parsed. In that case do not move `updatedAfter` — read the full list using the paged mode with the `lastMessageDate` cursor. **The number of rows returned is not a completeness signal** — a short response also arrives when more data exists beyond it.

A date without an explicit offset — for example `2026-06-29 10:00:00` — is rejected with `400 INVALID_PARAMS`: such a value is interpreted differently depending on the server time zone, so the instant has to be stated unambiguously.

**The v2 mode.** `format=v2` switches the endpoint to the v2 mode: the first page is requested without `lastMessageDate`, subsequent pages with it. `data` carries the v2 object — `recentItems` and the `chats`, `users`, `messages`, `files` collections — with `camelCase` keys. The mode accepts only `format`, `limit`, `lastMessageDate` and `unreadOnly` (`true` or `false`). Any other parameter, including the parameters of the legacy mode and their upper-case forms (`OFFSET`, `LIMIT`, `LAST_UPDATE`), is refused with `400 INVALID_PARAMS` naming the parameter. All dialogs are returned in a single list, without nested sections. The mode has its own rate-limit bucket, separate from that of the legacy mode.

The cursor of the next page is the smallest non-empty `dateLastActivity` among the **unpinned** rows of the current page; compare instants, not strings: dates may carry different offsets. The bound is inclusive, and pinned chats dated no later than the cursor appear at the top of every page — deduplicate by `chatId`. The list ends only at `hasNextPage: false`. If `hasNextPage` is `true` but there is no cursor or it equals the previous one, repeat the request with a larger `limit`, up to 200; `true` without progress even at 200 is a failure, not the end of the list. Do not discard rows from a response even if you display fewer: the cursor is computed over the whole response, and discarded rows are not returned again. Pagination does not give a snapshot: a chat lifted above the cursor by new activity during pagination does not appear in it, so re-read the first page after pagination. To read the whole list, use `limit=200`: pinned rows repeat on every page, and small pages waste requests on repeats.

## Examples

### curl — personal key

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

### curl — OAuth application

```bash
curl "https://vibecode.bitrix24.com/v1/chats/recent?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/recent?limit=20', {
  headers: { 'X-Api-Key': 'YOUR_API_KEY' },
})
const { data } = await res.json()
console.log('Recent dialogs:', data.items)
```

### JavaScript — OAuth application

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

## Response fields

| Field | Type | Description |
|------|-----|---------|
| `success` | boolean | Always `true` on success |
| `data` | object\|array | Result object. In delta mode — an array of dialogs, see the paragraph below the table |
| `data.items` | array | Array of recent dialogs |
| `data.items[].id` | string | Dialog identifier (`chatXXX` for a group chat or a numeric ID for a private dialog) |
| `data.items[].chatId` | number | Numeric chat ID |
| `data.items[].type` | string | Dialog type: `chat`, `openlines`, `copilot`, `channel` |
| `data.items[].title` | string | Dialog title |
| `data.items[].avatar` | object | Dialog avatar |
| `data.items[].avatar.url` | string | Avatar image URL. Empty string if the avatar is not set |
| `data.items[].avatar.color` | string | Avatar color in hexadecimal format, for example `#4ba984` |
| `data.items[].message` | object | Last message in the dialog |
| `data.items[].message.id` | number | Message ID |
| `data.items[].message.text` | string | Message text |
| `data.items[].message.authorId` | number | Message author ID |
| `data.items[].message.date` | string | Message date (ISO 8601) |
| `data.items[].message.file` | boolean | Whether the message contains a file |
| `data.items[].message.attach` | boolean | Whether the message contains an attachment |
| `data.items[].message.sticker` | string \| null | Message sticker or `null` |
| `data.items[].message.status` | string | Message status (for example, `received`) |
| `data.items[].lastId` | number | ID of the last read message |
| `data.items[].unread` | boolean | Whether there are unread messages |
| `data.items[].counter` | number | Number of unread messages |
| `data.items[].pinned` | boolean | Whether the dialog is pinned |
| `data.items[].dateUpdate` | string | Date of the last dialog update (ISO 8601) |
| `data.items[].dateLastActivity` | string | Date of the last activity (ISO 8601) |
| `data.items[].chat` | object | Extended chat information |
| `data.items[].chat.id` | number | Numeric chat ID |
| `data.items[].chat.name` | string | System chat name |
| `data.items[].chat.type` | string | Chat type: `chat`, `general`, `openlines`, `copilot`, `channel`, `mail`, `crm`, and others |
| `data.items[].chat.owner` | number | Chat owner ID |
| `data.items[].chat.userCounter` | number | Number of members |
| `data.items[].chat.role` | string | Role of the current user: `OWNER`, `MANAGER`, `MEMBER` |
| `data.items[].chat.entityType` | string | Type of the linked entity (`CRM`, `TASKS`, `MAIL`, `GENERAL`, and others) |
| `data.items[].chat.entityId` | string | ID of the linked entity |
| `data.hasMorePages` | boolean | `true` if there is a next page |
| `data.hasMore` | boolean | Duplicates `hasMorePages`. Kept for backward compatibility |
| `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, when `limit` is passed in delta mode, or when in the v2 mode it is below 50 or above 200 |
| `meta.appliedLimit` | number | The applied `limit` value — only when it was clamped to a bound. In delta mode it comes with any `limit` passed and equals the server page size — 200 |
| `meta.mode` | string | The value `delta`. Present only in delta mode |
| `meta.returned` | number | Number of dialogs in the response. Present only in delta mode |
| `meta.truncated` | boolean | Returned with the value `true` when the delta could not be confirmed complete: more dialogs exist beyond the returned page, or the response shape could not be parsed |
| `data.recentItems` | array | The v2 mode: list rows in the order Bitrix24 returns them — pinned first, then by activity |
| `data.recentItems[].dialogId` | string | The v2 mode: dialog ID (`chatXXX` or a user ID) — [chat loading](/docs/chats/messages/load) and the [message feed](/docs/chats/messages/list) take it |
| `data.recentItems[].chatId` | number | The v2 mode: chat ID — the deduplication key; equals `data.chat.id` in the [chat loading](/docs/chats/messages/load) response |
| `data.recentItems[].pinned` | boolean | The v2 mode: whether the chat is pinned |
| `data.recentItems[].dateLastActivity` | string\|null | The v2 mode: the instant of the last activity; the smallest value among unpinned rows is the cursor of the next page |
| `data.hasNextPage` | boolean | The v2 mode: `false` marks the end of the list |

In delta mode `data` is an array of dialogs with the same item fields listed above for `data.items`. The `hasMorePages` and `hasMore` fields are not returned in this mode — `meta.truncated` serves that role instead.

## Response example

Paged mode:

```json
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "chat456",
        "chatId": 456,
        "type": "chat",
        "title": "Development team",
        "avatar": {
          "url": "",
          "color": "#4ba984"
        },
        "message": {
          "id": 1201,
          "text": "Task update is ready",
          "authorId": 1,
          "date": "2026-06-03T16:51:12+00:00",
          "file": false,
          "attach": false,
          "sticker": null,
          "status": "received"
        },
        "lastId": 1195,
        "pinned": false,
        "unread": false,
        "counter": 0,
        "dateUpdate": "2026-06-03T16:51:12+00:00",
        "dateLastActivity": "2026-06-03T16:51:12+00:00",
        "chat": {
          "id": 456,
          "name": "Development team",
          "type": "chat",
          "owner": 1,
          "userCounter": 5,
          "role": "OWNER",
          "entityType": "",
          "entityId": ""
        }
      },
      {
        "id": "chat123",
        "chatId": 123,
        "type": "crm",
        "title": "Deal chat",
        "avatar": {
          "url": "",
          "color": "#f76187"
        },
        "message": {
          "id": 980,
          "text": "Contract approved",
          "authorId": 7,
          "date": "2026-06-02T14:08:10+00:00",
          "file": false,
          "attach": false,
          "sticker": null,
          "status": "received"
        },
        "lastId": 0,
        "pinned": false,
        "unread": false,
        "counter": 0,
        "dateUpdate": "2026-06-02T14:08:10+00:00",
        "dateLastActivity": "2026-06-02T14:08:10+00:00",
        "chat": {
          "id": 123,
          "name": "Deal chat",
          "type": "crm",
          "owner": 1,
          "userCounter": 2,
          "role": "MEMBER",
          "entityType": "CRM",
          "entityId": "DEAL|42"
        }
      }
    ],
    "hasMorePages": true,
    "hasMore": true
  }
}
```

Delta mode. The main item fields are shown; the full set is in the table above:

```json
{
  "success": true,
  "data": [
    {
      "id": "chat456",
      "chatId": 456,
      "type": "chat",
      "title": "Development team",
      "pinned": false,
      "unread": false,
      "counter": 0,
      "dateUpdate": "2026-06-29T10:12:44+00:00",
      "dateLastActivity": "2026-06-29T10:12:44+00:00"
    }
  ],
  "meta": {
    "mode": "delta",
    "returned": 1
  }
}
```

## Error response example

403 — no `im` scope:

```json
{
  "success": false,
  "error": {
    "code": "SCOPE_DENIED",
    "message": "This endpoint requires 'im' scope"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|---------|
| 200 | `Warning: 299` | The filter is ignored by default; previous data is preserved and the warning is sent in the `Warning` header. |
| 400 (strict mode only) | `UNSUPPORTED_FILTER` | The server mode is disabled by default; until it is enabled, the endpoint preserves HTTP 200 and the previous data. |
| 400 | `INVALID_PARAMS` | `updatedAfter` was passed without an explicit offset or `Z`, or is not a date |
| 400 | `INVALID_PARAMS` | `updatedAfter` was passed together with `offset` or `lastMessageDate` |
| 400 | `INVALID_PARAMS` | The v2 mode: a parameter other than `format`, `limit`, `lastMessageDate` or `unreadOnly`; a repeated parameter other than `format`; a non-numeric `limit`; an `unreadOnly` other than `true` or `false`; or a `lastMessageDate` that is not in the `YYYY-MM-DDTHH:MM:SS` format with an offset or `Z`, or that names a date or time that does not exist (`2026-02-30`, `24:00`) |
| 403 | `SCOPE_DENIED` | The API key does not have the `im` scope |
| 401 | `TOKEN_MISSING` | The API key has no Bitrix24 tokens configured |
| 422 | `BITRIX_ERROR` | Bitrix24 returned an error (details in `message`) |
| 502 | `BITRIX_UNAVAILABLE` | Bitrix24 is unavailable or returned a server error |

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

## Known specifics

- The `skipOpenLines` and `onlyOpenLines` flags are mutually exclusive: enabling both yields a result in which no Open Channels chat appears in the list.
- In legacy mode a deep `offset` is forwarded to Bitrix24. A small window is read from one buffered page, and an incomplete boundary row may require one additional request. New activity between requests can reorder the list. In legacy mode the date bound is inclusive: pass the last unpinned item's `dateLastActivity`, omit `offset` and deduplicate by `chatId`. Pinned rows and rows with the same boundary date may repeat. If the cursor does not advance, increase `limit` up to 200.
- The response contains a `data.copilot` field with the configuration of the Bitrix24 account's AI assistant. It is not related to the list of dialogs.
- In delta mode a dialog whose `dateUpdate` cannot be read stays in the response: an extra record is safer than a lost change.
- The `skip*` and `only*` filter families and `unreadOnly` apply in delta mode as well, narrowing the page that is scanned.
- In the v2 mode, keys that name fields are converted to camelCase, while 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, so a role is found directly as `roles[role]`.
- On a Bitrix24 account without the v2 methods, the mode responds with `422 BITRIX_ERROR` and the Bitrix24 error code in `error.b24Code`, and does not silently fall back to the legacy response: the response shape is different.

## See also

- [Search chats by text](/docs/chats/discovery/search)
- [Find a CRM entity chat](/docs/chats/discovery/find)
- [Dialog details](/docs/chats/discovery/get)
- [Chat discovery](/docs/chats/discovery)
