For AI agents: markdown of this page — /docs-content-en/chats/discovery/project-recent.md documentation index — /llms.txt

Project chats

GET /v1/chats/projects/:projectId/recent

Returns the chats of one project from the current user's recent dialog list: the project's own chat and the chats nested in it. Use it for a project screen that shows the project's discussions next to its main chat.

Parameters

Parameter Type Required Default Description
projectId (path) integer yes — ID of the project chat, a positive integer. This is the chatId of the project row in the collab list
limit (query) integer no 50 Rows per page, 50 to 200. A smaller value is raised to 50 and a larger one is clamped to 200, and both adjustments are echoed in meta
lastMessageDate (query) string no — Cursor of the next page: the smallest dateLastActivity among the unpinned rows of the previous page. Format YYYY-MM-DDTHH:MM:SS with an offset or Z, without fractional seconds. Not passed on the first page

Other parameters, including format and unreadOnly, and repeated parameters are rejected with 400 INVALID_PARAMS.

Pagination — as in recent dialogs in the v2 mode. The cursor of the next page is the smallest non-empty dateLastActivity among the unpinned rows; compare instants, not strings. The bound is inclusive: the row with the cursor date arrives again, so 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. Do not discard response rows: the cursor is computed over the whole response.

The first page modifies data. On the first read of a project, Bitrix24 creates this project's CoPilot chat for the key owner — once per project and only for a project member. For that reason a read-only key gets 403 WRITE_BLOCKED_READONLY_KEY on a request without lastMessageDate, before the Bitrix24 call. Requests with lastMessageDate only read and are available to such a key — see access rights.

Examples

curl — personal key

Terminal
curl "https://vibecode.bitrix24.com/v1/chats/projects/53/recent?limit=50" \
  -H "X-Api-Key: YOUR_API_KEY"

curl — OAuth application

Terminal
curl "https://vibecode.bitrix24.com/v1/chats/projects/53/recent?limit=50" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN"

JavaScript — personal key

javascript
const rows = new Map()
let limit = 50
let cursor
for (;;) {
  const url = new URL('https://vibecode.bitrix24.com/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 application

javascript
const rows = new Map()
let limit = 50
let cursor
for (;;) {
  const url = new URL('https://vibecode.bitrix24.com/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
}

Response fields

Field Type Description
success boolean Always true on success
data.recentItems array List rows in the order Bitrix24 returns them. On the first page the row of the project itself arrives together with the nested chats
data.recentItems[].dialogId string Dialog ID, chatXXX
data.recentItems[].chatId number Chat ID, the deduplication key
data.recentItems[].messageId number ID of the last message
data.recentItems[].pinned boolean The chat is pinned for the current user
data.recentItems[].unread boolean The chat is manually marked as unread
data.recentItems[].dateLastActivity string|null Instant of the last activity. The smallest value among the unpinned rows is the cursor of the next page
data.chats array Chat cards of the rows: id, dialogId, name, the current user's role and other chat fields
data.chats[].type string Chat type. collab for the project's own chat; for nested chats, the type of the chat itself, for example chat or copilot
data.chats[].parentChatId number 0 for the project's own chat, projectId for nested chats
data.chats[].entityType string SONET_GROUP for the project's own chat
data.chats[].entityId string ID of the project group for the project's own chat
data.messages array The chats' last messages
data.users array Authors of the last messages
data.files array Files of the last messages
data.recentConfigs array The list sections each chat is counted in: chatId and sections
data.hasNextPage boolean false — the end of the list
data.sectionMeta object Project details. Present only on the first page; responses to requests with lastMessageDate do not have the field
data.sectionMeta.fixedChatIds array ID of the project's own chat. An empty array if projectId is not a project chat
data.sectionMeta.collabInfo object|null Project summary. null if projectId is not a project chat
data.sectionMeta.collabInfo.collabId number ID of the project group
data.sectionMeta.collabInfo.guestCount number Number of project guests
data.sectionMeta.collabInfo.entities object The project sections tasks, files and calendar. Each has counter — the counter, and url — the section address relative to the Bitrix24 account domain
meta.requestedLimit number The limit passed. Present together with appliedLimit only when the value was outside the range of 50 to 200
meta.appliedLimit number The limit applied — only when it was clamped to a bound

The data object has the same shape as in recent dialogs in the v2 mode. Its other collections — copilot, birthdayList, stickers, additionalMessages, messagesAutoDeleteConfigs, forwardSource — arrive unchanged.

Response example

The first page, with the main fields shown:

JSON
{
  "success": true,
  "data": {
    "recentItems": [
      {
        "dialogId": "chat57",
        "chatId": 57,
        "messageId": 431,
        "type": "chat",
        "pinned": false,
        "unread": false,
        "dateUpdate": "2026-09-30T10:51:36+00:00",
        "dateLastActivity": "2026-09-30T10:51:36+00:00"
      },
      {
        "dialogId": "chat55",
        "chatId": 55,
        "messageId": 415,
        "type": "chat",
        "pinned": false,
        "unread": false,
        "dateUpdate": "2026-09-30T10:07:32+00:00",
        "dateLastActivity": "2026-08-10T14:31:46+00:00"
      },
      {
        "dialogId": "chat53",
        "chatId": 53,
        "messageId": 431,
        "type": "chat",
        "pinned": false,
        "unread": false,
        "dateUpdate": "2026-09-30T10:51:36+00:00",
        "dateLastActivity": "2026-09-30T10:51:36+00:00"
      }
    ],
    "chats": [
      {
        "id": 57,
        "dialogId": "chat57",
        "name": "Terms negotiation",
        "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": "Partner program launch",
        "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/" }
        }
      }
    }
  }
}

Error response example

403 — the first page is requested with a read-only key:

JSON
{
  "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"
    }
  }
}

Errors

HTTP Code Description
400 INVALID_PARAMS projectId is not a positive integer. Checked before the Bitrix24 call
400 INVALID_PARAMS A parameter other than limit and lastMessageDate, a repeated parameter, a non-numeric limit, or lastMessageDate not in the YYYY-MM-DDTHH:MM:SS format with an offset or Z, or with a nonexistent date or time (2026-02-30, 24:00). Checked before the Bitrix24 call
403 WRITE_BLOCKED_READONLY_KEY A request without lastMessageDate is sent with a read-only key. The key name and its mode are in error.details
422 BITRIX_ERROR Bitrix24 returned an error; the portal code is in error.b24Code. CHAT_NOT_FOUND — there is no chat with this projectId
404 ENTITY_NOT_FOUND Bitrix24 returned "not found"; the portal code is in error.b24Code
403 BITRIX_ACCESS_DENIED The key owner has no access to the project chat
403 SCOPE_DENIED The API key does not have the im scope
401 TOKEN_MISSING The API key has no Bitrix24 tokens configured
502 BITRIX_UNAVAILABLE Bitrix24 is unavailable or returned a server error

Full list of common API errors — Errors.

Known specifics

A chat that is not a project is an empty list, not an error. The ID of a group chat not linked to a project returns 200 with empty collections and hasNextPage: false. Such a response does not reveal a mistake in projectId: a project is indicated by a non-empty sectionMeta.fixedChatIds on the first page.

The refusal for a read-only key does not depend on the project state. The first page is closed to such a key even when the project's CoPilot chat already exists.

See also