Для AI-агентов: markdown этой страницы — /docs-content/chats/discovery/project-recent.md индекс документации — /llms.txt
Чаты проекта
GET /v1/chats/projects/:projectId/recent
Возвращает чаты одного проекта из списка последних диалогов текущего пользователя: собственный чат проекта и вложенные в него чаты. Подходит для экрана проекта, где рядом с общим чатом показаны его обсуждения.
Параметры
| Параметр | Тип | Обяз. | По умолч. | Описание |
|---|---|---|---|---|
projectId (path) |
integer | да | — | ID чата проекта, целое от 1. Это chatId строки проекта в списке коллаб |
limit (query) |
integer | нет | 50 | Строк на странице, от 50 до 200. Меньшее значение поднимается до 50, большее срезается до 200, оба раза с эхом в meta |
lastMessageDate (query) |
string | нет | — | Курсор следующей страницы — наименьшая dateLastActivity среди незакреплённых строк предыдущей страницы. Формат YYYY-MM-DDTHH:MM:SS со смещением или Z, без долей секунды. На первой странице не передаётся |
Другие параметры, в том числе format и unreadOnly, и повторы отклоняются с 400 INVALID_PARAMS.
Обход — как у последних диалогов в режиме v2. Курсор следующей страницы — наименьшая непустая dateLastActivity среди незакреплённых строк, сравнивайте моменты времени, а не строки. Граница включительная: строка с датой курсора приходит повторно, повторы снимайте по chatId. Конец списка — только hasNextPage: false. Если при hasNextPage: true курсора нет или он равен прежнему, повторите запрос с бо́льшим limit, вплоть до 200. Строк ответа не отбрасывайте: курсор считается по всему ответу.
Первая страница изменяет данные. При первом чтении проекта Битрикс24 создаёт для владельца ключа чат CoPilot этого проекта — один раз на проект и только участнику проекта. Поэтому ключ только для чтения получает на запрос без lastMessageDate ответ 403 WRITE_BLOCKED_READONLY_KEY до обращения к Битрикс24. Запросы с lastMessageDate только читают и такому ключу доступны. Подробнее — права доступа.
Примеры
curl — личный ключ
curl "https://vibecode.bitrix24.tech/v1/chats/projects/53/recent?limit=50" \
-H "X-Api-Key: YOUR_API_KEY"
curl — OAuth-приложение
curl "https://vibecode.bitrix24.tech/v1/chats/projects/53/recent?limit=50" \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN"
JavaScript — личный ключ
const rows = new Map()
let limit = 50
let cursor
for (;;) {
const url = new URL('https://vibecode.bitrix24.tech/v1/chats/projects/53/recent')
url.searchParams.set('limit', String(limit))
if (cursor) url.searchParams.set('lastMessageDate', cursor)
const res = await fetch(url, { headers: { 'X-Api-Key': 'YOUR_API_KEY' } })
const { data } = await res.json()
for (const item of data.recentItems) rows.set(item.chatId, item)
if (!data.hasNextPage) break
const dates = data.recentItems.filter((i) => !i.pinned && i.dateLastActivity).map((i) => i.dateLastActivity)
const next = dates.length ? dates.reduce((a, b) => (Date.parse(a) <= Date.parse(b) ? a : b)) : undefined
if (!next || (cursor && Date.parse(next) >= Date.parse(cursor))) {
if (limit === 200) throw new Error('Recent-list cursor did not advance; restart with a full refresh')
limit = Math.min(limit * 2, 200)
continue
}
cursor = next
limit = 50
}
JavaScript — OAuth-приложение
const rows = new Map()
let limit = 50
let cursor
for (;;) {
const url = new URL('https://vibecode.bitrix24.tech/v1/chats/projects/53/recent')
url.searchParams.set('limit', String(limit))
if (cursor) url.searchParams.set('lastMessageDate', cursor)
const res = await fetch(url, {
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
},
})
const { data } = await res.json()
for (const item of data.recentItems) rows.set(item.chatId, item)
if (!data.hasNextPage) break
const dates = data.recentItems.filter((i) => !i.pinned && i.dateLastActivity).map((i) => i.dateLastActivity)
const next = dates.length ? dates.reduce((a, b) => (Date.parse(a) <= Date.parse(b) ? a : b)) : undefined
if (!next || (cursor && Date.parse(next) >= Date.parse(cursor))) {
if (limit === 200) throw new Error('Recent-list cursor did not advance; restart with a full refresh')
limit = Math.min(limit * 2, 200)
continue
}
cursor = next
limit = 50
}
Поля ответа
| Поле | Тип | Описание |
|---|---|---|
success |
boolean | Всегда true при успехе |
data.recentItems |
array | Строки списка в порядке портала. На первой странице вместе с вложенными чатами приходит строка самого проекта |
data.recentItems[].dialogId |
string | ID диалога, chatXXX |
data.recentItems[].chatId |
number | ID чата — ключ для снятия повторов |
data.recentItems[].messageId |
number | ID последнего сообщения |
data.recentItems[].pinned |
boolean | Чат закреплён у текущего пользователя |
data.recentItems[].unread |
boolean | Чат вручную отмечен непрочитанным |
data.recentItems[].dateLastActivity |
string|null | Момент последней активности. Наименьшее значение среди незакреплённых строк — курсор следующей страницы |
data.chats |
array | Карточки чатов строк: id, dialogId, name, role текущего пользователя и другие поля чата |
data.chats[].type |
string | Тип чата. У собственного чата проекта — collab, у вложенных — тип самого чата, например chat или copilot |
data.chats[].parentChatId |
number | У собственного чата проекта — 0, у вложенных чатов — projectId |
data.chats[].entityType |
string | У собственного чата проекта — SONET_GROUP |
data.chats[].entityId |
string | У собственного чата проекта — ID группы проекта |
data.messages |
array | Последние сообщения чатов |
data.users |
array | Авторы последних сообщений |
data.files |
array | Файлы последних сообщений |
data.recentConfigs |
array | Разделы списка, в которых учитывается каждый чат: chatId и sections |
data.hasNextPage |
boolean | false — конец списка |
data.sectionMeta |
object | Сведения о проекте. Приходит только на первой странице, в ответах на запросы с lastMessageDate поля нет |
data.sectionMeta.fixedChatIds |
array | ID собственного чата проекта. Пустой массив, если projectId — не чат проекта |
data.sectionMeta.collabInfo |
object|null | Сводка проекта. null, если projectId — не чат проекта |
data.sectionMeta.collabInfo.collabId |
number | ID группы проекта |
data.sectionMeta.collabInfo.guestCount |
number | Число гостей проекта |
data.sectionMeta.collabInfo.entities |
object | Разделы проекта tasks, files и calendar. У каждого counter — счётчик и url — адрес раздела относительно домена портала |
meta.requestedLimit |
number | Переданный limit. Приходит вместе с appliedLimit, только когда значение вышло за диапазон от 50 до 200 |
meta.appliedLimit |
number | Применённый limit — только если он был приведён к границе |
Объект data — той же формы, что у последних диалогов в режиме v2. Остальные его коллекции — copilot, birthdayList, stickers, additionalMessages, messagesAutoDeleteConfigs, forwardSource — приходят без изменений.
Пример ответа
Первая страница, показаны основные поля:
{
"success": true,
"data": {
"recentItems": [
{
"dialogId": "chat57",
"chatId": 57,
"messageId": 431,
"type": "chat",
"pinned": false,
"unread": false,
"dateUpdate": "2026-09-30T10:51:36+03:00",
"dateLastActivity": "2026-09-30T10:51:36+03:00"
},
{
"dialogId": "chat55",
"chatId": 55,
"messageId": 415,
"type": "chat",
"pinned": false,
"unread": false,
"dateUpdate": "2026-09-30T10:07:32+03:00",
"dateLastActivity": "2026-08-10T14:31:46+03:00"
},
{
"dialogId": "chat53",
"chatId": 53,
"messageId": 431,
"type": "chat",
"pinned": false,
"unread": false,
"dateUpdate": "2026-09-30T10:51:36+03:00",
"dateLastActivity": "2026-09-30T10:51:36+03:00"
}
],
"chats": [
{
"id": 57,
"dialogId": "chat57",
"name": "Согласование условий",
"type": "chat",
"parentChatId": 53,
"entityType": "",
"entityId": "",
"owner": 1,
"role": "owner"
},
{
"id": 55,
"dialogId": "chat55",
"name": "Session #2",
"type": "copilot",
"parentChatId": 53,
"entityType": "SONET_PROJECT_COPILOT",
"entityId": "3",
"owner": 1,
"role": "owner"
},
{
"id": 53,
"dialogId": "chat53",
"name": "Запуск партнёрской программы",
"type": "collab",
"parentChatId": 0,
"entityType": "SONET_GROUP",
"entityId": "3",
"owner": 1,
"role": "owner"
}
],
"recentConfigs": [
{ "chatId": 57, "sections": ["default", "chat", "collabDefault", "collabChat"] },
{ "chatId": 55, "sections": ["default", "copilot", "collabDefault"] },
{ "chatId": 53, "sections": ["default", "collab"] }
],
"hasNextPage": false,
"sectionMeta": {
"fixedChatIds": [53],
"collabInfo": {
"guestCount": 0,
"collabId": 3,
"entities": {
"tasks": { "counter": 0, "url": "/workgroups/group/3/tasks/" },
"files": { "counter": 0, "url": "/workgroups/group/3/disk/path/?c_element=files_button" },
"calendar": { "counter": 0, "url": "/workgroups/group/3/calendar/" }
}
}
}
}
}
Пример ответа при ошибке
403 — первая страница запрошена ключом только для чтения:
{
"success": false,
"error": {
"code": "WRITE_BLOCKED_READONLY_KEY",
"message": "Key is in read-only mode. Switch the key to read and write in /keys to allow Bitrix24 data changes.",
"details": {
"method": "im.v2.Recent.load",
"keyName": "Reports dashboard",
"currentMode": "READONLY",
"switchUrl": "/keys"
}
}
}
Ошибки
| HTTP | Код | Описание |
|---|---|---|
| 400 | INVALID_PARAMS |
projectId не является целым числом от 1. Проверяется до обращения к Битрикс24 |
| 400 | INVALID_PARAMS |
Параметр вне limit и lastMessageDate, повтор параметра, нечисловой limit или lastMessageDate не в формате YYYY-MM-DDTHH:MM:SS со смещением или Z либо с несуществующей датой или временем (2026-02-30, 24:00). Проверяется до обращения к Битрикс24 |
| 403 | WRITE_BLOCKED_READONLY_KEY |
Запрос без lastMessageDate отправлен ключом только для чтения. Имя ключа и его режим — в error.details |
| 422 | BITRIX_ERROR |
Битрикс24 вернул ошибку, код портала — в error.b24Code. CHAT_NOT_FOUND — чата с таким projectId нет |
| 404 | ENTITY_NOT_FOUND |
Битрикс24 вернул «не найдено», код портала — в error.b24Code |
| 403 | BITRIX_ACCESS_DENIED |
У владельца ключа нет доступа к чату проекта |
| 403 | SCOPE_DENIED |
API-ключ не имеет скоупа im |
| 401 | TOKEN_MISSING |
API-ключ не имеет настроенных токенов Битрикс24 |
| 502 | BITRIX_UNAVAILABLE |
Битрикс24 недоступен или вернул ошибку сервера |
Полный список общих ошибок API — Ошибки.
Известные особенности
Чат, который не проект, — пустой список, а не ошибка. ID группового чата, не связанного с проектом, отвечает 200 с пустыми коллекциями и hasNextPage: false. Ошибку в projectId такой ответ не покажет: признак проекта — непустой sectionMeta.fixedChatIds на первой странице.
Отказ ключу только для чтения не зависит от состояния проекта. Первая страница закрыта такому ключу и тогда, когда чат CoPilot проекта уже создан.