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

External chats of a section

GET /v1/chats/recent/external

Returns one section of the current user's chat list: the chats that other Bitrix24 modules create, such as task chats or calendar event chats. Ordering and paging work as in recent dialogs: pinned ones first, then by the time of the last activity.

Parameters

Parameter Type Required Default Description
type (query) string yes — Section code: a Latin letter, then letters, digits or _, up to 64 characters. tasksTask — task chats, calendar — calendar event chats. The sections a chat is counted in are listed in the recentSections field of the unread counters
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 and repeated parameters are rejected with 400 INVALID_PARAMS. The default and collab sections are not read here, as they have their own endpoints: the common list — recent dialogs in the v2 mode, collabs — collab list.

Walk — 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, and pinned chats open every page, 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.

Examples

curl — personal key

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

curl — OAuth application

Terminal
curl "https://vibecode.bitrix24.com/v1/chats/recent/external?type=tasksTask&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/recent/external')
  url.searchParams.set('type', 'tasksTask')
  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/recent/external')
  url.searchParams.set('type', 'tasksTask')
  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 Section rows in the order Bitrix24 returns them: pinned ones first, then by activity
data.recentItems[].dialogId string Dialog ID, chatXXX
data.recentItems[].chatId number Chat ID, the key for removing repeats
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: id, dialogId, name, type — the same section code, entityType and entityId — the linked entity, entityLink.url — its address in Bitrix24, the current user's role and other chat fields
data.messages array The chats' last messages
data.users array Authors 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 section
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

Response example

The tasksTask section, with the main fields shown:

JSON
{
  "success": true,
  "data": {
    "recentItems": [
      {
        "dialogId": "chat264388",
        "chatId": 264388,
        "messageId": 608251,
        "type": "chat",
        "pinned": false,
        "unread": false,
        "dateUpdate": "2026-09-11T12:53:10+00:00",
        "dateLastActivity": "2026-09-11T12:53:10+00:00"
      }
    ],
    "chats": [
      {
        "id": 264388,
        "dialogId": "chat264388",
        "name": "Prepare the release",
        "type": "tasksTask",
        "entityType": "TASKS_TASK",
        "entityId": "27",
        "owner": 1,
        "role": "owner"
      }
    ],
    "recentConfigs": [
      { "chatId": 264388, "sections": ["tasksTask"] }
    ],
    "hasNextPage": false
  }
}

Error response example

400 — the common list is requested:

JSON
{
  "success": false,
  "error": {
    "code": "INVALID_PARAMS",
    "message": "`type` default is not an external-chat section — read it through GET /v1/chats/recent?format=v2."
  }
}

Errors

HTTP Code Description
400 INVALID_PARAMS No type, type is not a section code, or type is default or collab — the message names the right endpoint. Also a parameter other than type, limit, 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. Checked before the Bitrix24 call
422 BITRIX_ERROR Bitrix24 returned an error; the Bitrix24 code is in error.b24Code
403 BITRIX_ACCESS_DENIED Bitrix24 denied access
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

An unknown section is an empty list, not an error. A code that does not exist in the Bitrix24 account returns 200 with an empty recentItems and hasNextPage: false. The response does not reveal a typo in type: check the code against the recentSections field of the unread counters.

The limit floor is 50. A user can have up to 45 pinned chats, and they repeat on every page. A page smaller than 50 rows could consist of repeats only and give no cursor.

A walk is not a snapshot. A chat lifted above the cursor by new activity during the walk does not appear in it. Re-read the first page after the walk.

See also