[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"docs-chats\u002Fdiscovery\u002Frecent-external":3,"docs-tabs-chats\u002Fdiscovery\u002Frecent-external":6},{"content":4,"lastmod":5},"## External chats of a section\n\n`GET \u002Fv1\u002Fchats\u002Frecent\u002Fexternal`\n\nReturns 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.\n\n## Parameters\n\n| Parameter | Type | Required | Default | Description |\n|----------|-----|:-----:|-----------|---------|\n| `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](\u002Fdocs\u002Fchats\u002Fdiscovery\u002Fcounters) |\n| `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` |\n| `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 |\n\nOther 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](\u002Fdocs\u002Fchats\u002Fdiscovery\u002Frecent), collabs — [collab list](\u002Fdocs\u002Fchats\u002Fdiscovery\u002Frecent-collabs).\n\n**Pagination** — as in [recent dialogs in the v2 mode](\u002Fdocs\u002Fchats\u002Fdiscovery\u002Frecent). 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 appear at the top of 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.\n\n## Examples\n\n### curl — personal key\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fchats\u002Frecent\u002Fexternal?type=tasksTask&limit=50\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\"\n```\n\n### curl — OAuth application\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fchats\u002Frecent\u002Fexternal?type=tasksTask&limit=50\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\"\n```\n\n### JavaScript — personal key\n\n```javascript\nconst rows = new Map()\nlet limit = 50\nlet cursor\nfor (;;) {\n  const url = new URL('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fchats\u002Frecent\u002Fexternal')\n  url.searchParams.set('type', 'tasksTask')\n  url.searchParams.set('limit', String(limit))\n  if (cursor) url.searchParams.set('lastMessageDate', cursor)\n  const res = await fetch(url, { headers: { 'X-Api-Key': 'YOUR_API_KEY' } })\n  const { data } = await res.json()\n  for (const item of data.recentItems) rows.set(item.chatId, item)\n  if (!data.hasNextPage) break\n  const dates = data.recentItems.filter((i) => !i.pinned && i.dateLastActivity).map((i) => i.dateLastActivity)\n  const next = dates.length ? dates.reduce((a, b) => (Date.parse(a) \u003C= Date.parse(b) ? a : b)) : undefined\n  if (!next || (cursor && Date.parse(next) >= Date.parse(cursor))) {\n    if (limit === 200) throw new Error('Recent-list cursor did not advance; restart with a full refresh')\n    limit = Math.min(limit * 2, 200)\n    continue\n  }\n  cursor = next\n  limit = 50\n}\n```\n\n### JavaScript — OAuth application\n\n```javascript\nconst rows = new Map()\nlet limit = 50\nlet cursor\nfor (;;) {\n  const url = new URL('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fchats\u002Frecent\u002Fexternal')\n  url.searchParams.set('type', 'tasksTask')\n  url.searchParams.set('limit', String(limit))\n  if (cursor) url.searchParams.set('lastMessageDate', cursor)\n  const res = await fetch(url, {\n    headers: {\n      'X-Api-Key': 'YOUR_APP_KEY',\n      'Authorization': 'Bearer USER_SESSION_TOKEN',\n    },\n  })\n  const { data } = await res.json()\n  for (const item of data.recentItems) rows.set(item.chatId, item)\n  if (!data.hasNextPage) break\n  const dates = data.recentItems.filter((i) => !i.pinned && i.dateLastActivity).map((i) => i.dateLastActivity)\n  const next = dates.length ? dates.reduce((a, b) => (Date.parse(a) \u003C= Date.parse(b) ? a : b)) : undefined\n  if (!next || (cursor && Date.parse(next) >= Date.parse(cursor))) {\n    if (limit === 200) throw new Error('Recent-list cursor did not advance; restart with a full refresh')\n    limit = Math.min(limit * 2, 200)\n    continue\n  }\n  cursor = next\n  limit = 50\n}\n```\n\n## Response fields\n\n| Field | Type | Description |\n|------|-----|---------|\n| `success` | boolean | Always `true` on success |\n| `data.recentItems` | array | Section rows in the order Bitrix24 returns them: pinned ones first, then by activity |\n| `data.recentItems[].dialogId` | string | Dialog ID, `chatXXX` |\n| `data.recentItems[].chatId` | number | Chat ID, the deduplication key |\n| `data.recentItems[].messageId` | number | ID of the last message |\n| `data.recentItems[].pinned` | boolean | The chat is pinned for the current user |\n| `data.recentItems[].unread` | boolean | The chat is manually marked as unread |\n| `data.recentItems[].dateLastActivity` | string\\|null | Instant of the last activity. The smallest value among the unpinned rows is the cursor of the next page |\n| `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 |\n| `data.messages` | array | The chats' last messages |\n| `data.users` | array | Authors of the last messages |\n| `data.recentConfigs` | array | The list sections each chat is counted in: `chatId` and `sections` |\n| `data.hasNextPage` | boolean | `false` — the end of the section |\n| `meta.requestedLimit` | number | The `limit` passed. Present together with `appliedLimit` only when the value was outside the range of 50 to 200 |\n| `meta.appliedLimit` | number | The `limit` applied — only when it was clamped to a bound |\n\n## Response example\n\nThe `tasksTask` section, with the main fields shown:\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"recentItems\": [\n      {\n        \"dialogId\": \"chat264388\",\n        \"chatId\": 264388,\n        \"messageId\": 608251,\n        \"type\": \"chat\",\n        \"pinned\": false,\n        \"unread\": false,\n        \"dateUpdate\": \"2026-09-11T12:53:10+00:00\",\n        \"dateLastActivity\": \"2026-09-11T12:53:10+00:00\"\n      }\n    ],\n    \"chats\": [\n      {\n        \"id\": 264388,\n        \"dialogId\": \"chat264388\",\n        \"name\": \"Prepare the release\",\n        \"type\": \"tasksTask\",\n        \"entityType\": \"TASKS_TASK\",\n        \"entityId\": \"27\",\n        \"owner\": 1,\n        \"role\": \"owner\"\n      }\n    ],\n    \"recentConfigs\": [\n      { \"chatId\": 264388, \"sections\": [\"tasksTask\"] }\n    ],\n    \"hasNextPage\": false\n  }\n}\n```\n\n## Error response example\n\n400 — the common list is requested:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INVALID_PARAMS\",\n    \"message\": \"`type` default is not an external-chat section — read it through GET \u002Fv1\u002Fchats\u002Frecent?format=v2.\"\n  }\n}\n```\n\n## Errors\n\n| HTTP | Code | Description |\n|------|-----|---------|\n| 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 |\n| 422 | `BITRIX_ERROR` | Bitrix24 returned an error; the Bitrix24 code is in `error.b24Code` |\n| 403 | `BITRIX_ACCESS_DENIED` | Bitrix24 denied access |\n| 403 | `SCOPE_DENIED` | The API key does not have the `im` scope |\n| 401 | `TOKEN_MISSING` | The API key has no Bitrix24 tokens configured |\n| 502 | `BITRIX_UNAVAILABLE` | Bitrix24 is unavailable or returned a server error |\n\nFull list of common API errors — [Errors](\u002Fdocs\u002Ferrors).\n\n## Known specifics\n\n**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](\u002Fdocs\u002Fchats\u002Fdiscovery\u002Fcounters).\n\n**The `limit` floor is 50.** A user can have up to 45 pinned chats, and they repeat on every page. A page of fewer than 50 rows could consist of repeats only and give no cursor.\n\n**Pagination does not give a snapshot.** A chat lifted above the cursor by new activity during pagination does not appear in the results. Re-read the first page after pagination.\n\n## See also\n\n- [Recent dialogs](\u002Fdocs\u002Fchats\u002Fdiscovery\u002Frecent)\n- [Collab list](\u002Fdocs\u002Fchats\u002Fdiscovery\u002Frecent-collabs)\n- [Channel list](\u002Fdocs\u002Fchats\u002Fdiscovery\u002Frecent-channels)\n- [Chat discovery](\u002Fdocs\u002Fchats\u002Fdiscovery)\n","2026-10-07",{},1794739535656]