
## Последние диалоги

`GET /v1/chats/recent`

Возвращает список последних диалогов текущего пользователя: закреплённые диалоги первыми, далее по времени последнего сообщения. Каждый элемент содержит краткую информацию о диалоге, последнем сообщении и счётчике непрочитанных.

## Параметры

| Параметр | Тип | Обяз. | По умолч. | Описание |
|----------|-----|:-----:|-----------|---------|
| `skipOpenLines` | string | нет | — | Исключить линии открытых каналов. Принимает `true`, `Y` или `y` |
| `skipChat` | string | нет | — | Исключить групповые чаты. Принимает `true`, `Y` или `y` |
| `skipDialog` | string | нет | — | Исключить личные диалоги. Принимает `true`, `Y` или `y` |
| `unreadOnly` | string | нет | — | Только диалоги с непрочитанными сообщениями. Принимает `true`, `Y` или `y` |
| `onlyOpenLines` | string | нет | — | Только линии открытых каналов. Принимает `true`, `Y` или `y` |
| `onlyCopilot` | string | нет | — | Только чаты с AI-ассистентом. Принимает `true`, `Y` или `y` |
| `onlyChannel` | string | нет | — | Только каналы. Принимает `true`, `Y` или `y` |
| `lastMessageDate` | string | нет | — | Дата последнего сообщения для постраничной навигации (ISO 8601) |
| `updatedAfter` | string | нет | — | Режим дельты: вернуть диалоги, изменённые начиная с указанного момента. Дата в ISO 8601 с явным смещением или `Z`. Несовместим с `offset` и `lastMessageDate` |
| `limit` | number | нет | — | Количество элементов в ответе. Максимум 200 |
| `offset` | number | нет | `0` | Смещение для пагинации |

**Режим дельты.** Параметр `updatedAfter` переключает эндпоинт в режим «что изменилось с указанного момента»: `GET /v1/chats/recent?updatedAfter=2026-06-28T00:00:00Z`. В ответ приходит массив диалогов, у которых `dateUpdate` не раньше переданного момента. Граница включительна — диалог с `dateUpdate`, равным переданному моменту, попадает в ответ, поэтому при сдвиге курсора на максимальное увиденное значение пограничная запись придёт повторно.

Размером выборки в этом режиме управляет сервер: читается одна страница до 200 диалогов. Переданный `limit` на неё не влияет и возвращается в `meta.requestedLimit` вместе с применённым `meta.appliedLimit`.

Если в `meta` пришло `truncated` со значением `true`, дельта может быть неполной: Битрикс24 сообщил, что за отданной страницей есть ещё диалоги, либо форму ответа не удалось разобрать. В этом случае не сдвигайте `updatedAfter`, а получите полный список постраничным режимом с курсором `lastMessageDate`. **Число возвращённых строк признаком полноты не является** — короткий ответ приходит и тогда, когда за ним есть ещё данные.

Дата без явного смещения — например `2026-06-29 10:00:00` — отклоняется с `400 INVALID_PARAMS`: такое значение читается по-разному в зависимости от часового пояса сервера, поэтому момент требуется указать однозначно.

## Примеры

### curl — личный ключ

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

### curl — OAuth-приложение

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

### JavaScript — личный ключ

```javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/chats/recent?limit=20', {
  headers: { 'X-Api-Key': 'YOUR_API_KEY' },
})
const { data } = await res.json()
console.log('Последние диалоги:', data.items)
```

### JavaScript — OAuth-приложение

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

## Поля ответа

| Поле | Тип | Описание |
|------|-----|---------|
| `data` | object | Объект результата |
| `data.items` | array | Массив последних диалогов |
| `data.items[].id` | string | Идентификатор диалога (`chatXXX` для группового чата или числовой ID для личного диалога) |
| `data.items[].chatId` | number | Числовой ID чата |
| `data.items[].type` | string | Тип диалога: `chat`, `openlines`, `copilot`, `channel` |
| `data.items[].title` | string | Название диалога |
| `data.items[].avatar` | object | Аватар диалога |
| `data.items[].avatar.url` | string | URL изображения аватара. Пустая строка, если аватар не задан |
| `data.items[].avatar.color` | string | Цвет аватара в шестнадцатеричном формате, например `#4ba984` |
| `data.items[].message` | object | Последнее сообщение в диалоге |
| `data.items[].message.id` | number | ID сообщения |
| `data.items[].message.text` | string | Текст сообщения |
| `data.items[].message.authorId` | number | ID автора сообщения |
| `data.items[].message.date` | string | Дата сообщения (ISO 8601) |
| `data.items[].message.file` | boolean | Содержит ли сообщение файл |
| `data.items[].message.attach` | boolean | Содержит ли сообщение вложение |
| `data.items[].message.sticker` | string \| null | Стикер сообщения или `null` |
| `data.items[].message.status` | string | Статус сообщения (например, `received`) |
| `data.items[].lastId` | number | ID последнего прочитанного сообщения |
| `data.items[].unread` | boolean | Есть ли непрочитанные сообщения |
| `data.items[].counter` | number | Количество непрочитанных сообщений |
| `data.items[].pinned` | boolean | Диалог закреплён |
| `data.items[].dateUpdate` | string | Дата последнего обновления диалога (ISO 8601) |
| `data.items[].dateLastActivity` | string | Дата последней активности (ISO 8601) |
| `data.items[].chat` | object | Расширенная информация о чате |
| `data.items[].chat.id` | number | Числовой ID чата |
| `data.items[].chat.name` | string | Системное название чата |
| `data.items[].chat.type` | string | Тип чата: `chat`, `general`, `openlines`, `copilot`, `channel`, `mail`, `crm` и другие |
| `data.items[].chat.owner` | number | ID владельца чата |
| `data.items[].chat.userCounter` | number | Количество участников |
| `data.items[].chat.role` | string | Роль текущего пользователя: `OWNER`, `MANAGER`, `MEMBER` |
| `data.items[].chat.entityType` | string | Тип связанной сущности (`CRM`, `TASKS`, `MAIL`, `GENERAL` и другие) |
| `data.items[].chat.entityId` | string | ID связанной сущности |
| `data.hasMorePages` | boolean | `true`, если есть следующая страница |
| `data.hasMore` | boolean | Дублирует `hasMorePages`. Сохранён для обратной совместимости |
| `meta.requestedLimit` | number | Переданный `limit` до срезания. Присутствует вместе с `appliedLimit`, только когда переданное значение вышло за диапазон от 1 до 200 или когда `limit` передан в режиме дельты |
| `meta.appliedLimit` | number | Применённое после срезания значение `limit`. В режиме дельты это размер серверной страницы — 200, а не срезанный `limit` |
| `meta.mode` | string | Значение `delta`. Приходит только в режиме дельты |
| `meta.returned` | number | Число диалогов в ответе. Приходит только в режиме дельты |
| `meta.truncated` | boolean | Приходит со значением `true`, когда дельту не удалось подтвердить полной: за отданной страницей есть ещё диалоги либо форма ответа не разобрана |

В режиме дельты `data` — массив диалогов с теми же полями элемента, что перечислены выше для `data.items`. Поля `hasMorePages` и `hasMore` в этом режиме не возвращаются, их роль выполняет `meta.truncated`.

## Пример ответа

Постраничный режим:

```json
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "chat456",
        "chatId": 456,
        "type": "chat",
        "title": "Команда разработки",
        "avatar": {
          "url": "",
          "color": "#4ba984"
        },
        "message": {
          "id": 1201,
          "text": "Обновление задачи готово",
          "authorId": 1,
          "date": "2026-06-03T16:51:12+03:00",
          "file": false,
          "attach": false,
          "sticker": null,
          "status": "received"
        },
        "lastId": 1195,
        "pinned": false,
        "unread": false,
        "counter": 0,
        "dateUpdate": "2026-06-03T16:51:12+03:00",
        "dateLastActivity": "2026-06-03T16:51:12+03:00",
        "chat": {
          "id": 456,
          "name": "Команда разработки",
          "type": "chat",
          "owner": 1,
          "userCounter": 5,
          "role": "OWNER",
          "entityType": "",
          "entityId": ""
        }
      },
      {
        "id": "chat123",
        "chatId": 123,
        "type": "crm",
        "title": "Чат по сделке",
        "avatar": {
          "url": "",
          "color": "#f76187"
        },
        "message": {
          "id": 980,
          "text": "Договор согласован",
          "authorId": 7,
          "date": "2026-06-02T14:08:10+03:00",
          "file": false,
          "attach": false,
          "sticker": null,
          "status": "received"
        },
        "lastId": 0,
        "pinned": false,
        "unread": false,
        "counter": 0,
        "dateUpdate": "2026-06-02T14:08:10+03:00",
        "dateLastActivity": "2026-06-02T14:08:10+03:00",
        "chat": {
          "id": 123,
          "name": "Чат по сделке",
          "type": "crm",
          "owner": 1,
          "userCounter": 2,
          "role": "MEMBER",
          "entityType": "CRM",
          "entityId": "DEAL|42"
        }
      }
    ],
    "hasMorePages": true,
    "hasMore": true
  }
}
```

Режим дельты. Показаны основные поля элемента, полный набор — в таблице выше:

```json
{
  "success": true,
  "data": [
    {
      "id": "chat456",
      "chatId": 456,
      "type": "chat",
      "title": "Команда разработки",
      "pinned": false,
      "unread": false,
      "counter": 0,
      "dateUpdate": "2026-06-29T10:12:44+03:00",
      "dateLastActivity": "2026-06-29T10:12:44+03:00"
    }
  ],
  "meta": {
    "mode": "delta",
    "returned": 1
  }
}
```

## Пример ответа при ошибке

403 — нет скоупа `im`:

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

## Ошибки

| HTTP | Код | Описание |
|------|-----|---------|
| 400 | `INVALID_PARAMS` | `updatedAfter` передан без явного смещения или `Z`, либо не является датой |
| 400 | `INVALID_PARAMS` | `updatedAfter` передан вместе с `offset` или `lastMessageDate` |
| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `im` |
| 401 | `TOKEN_MISSING` | API-ключ не имеет настроенных токенов Битрикс24 |
| 422 | `BITRIX_ERROR` | Битрикс24 вернул ошибку (подробности в `message`) |
| 502 | `BITRIX_UNAVAILABLE` | Битрикс24 недоступен или вернул ошибку сервера |

Полный список общих ошибок API — [Ошибки](/docs/errors).

## Известные особенности

- Флаги `skipOpenLines` и `onlyOpenLines` взаимоисключающие: включение обоих приводит к результату, при котором ни один из чатов открытых линий не попадёт в список.
- Порядок между страницами по `offset` на живом портале не гарантирован: список пересобирается при каждом новом сообщении, соседние страницы могут пересечься или пропустить диалог. Для устойчивого обхода используйте курсор `lastMessageDate`: передайте значение `dateLastActivity` последнего элемента текущей страницы, чтобы получить следующую порцию. При этом передавайте и `offset`, чтобы избежать дублирования записей на границе страниц.
- Ответ содержит поле `data.copilot` с конфигурацией AI-ассистента портала. Структура используется для отображения ролей в интерфейсе Битрикс24 и не является частью списка диалогов.
- В режиме дельты диалог с нечитаемым значением `dateUpdate` остаётся в ответе: лишняя запись безопаснее потерянного изменения.
- Фильтры семейств `skip*`, `only*` и `unreadOnly` действуют и в режиме дельты, сужая просматриваемую страницу.

## Смотрите также

- [Поиск чатов](/docs/chats/discovery/search)
- [Найти чат CRM-сущности](/docs/chats/discovery/find)
- [Информация о диалоге](/docs/chats/discovery/get)
- [Поиск чатов](/docs/chats/discovery)
