## Folder chats

`GET /v1/chats/folders/:folderId/recent`

Returns the recent dialog list of one folder — the v2 messenger method `im.v2.Folder.Recent.tail`. Folders and their IDs come from [`GET /v1/chats/folders`](/docs/chats/discovery/folders). The response and paging work the same way as in the [recent dialog list in the v2 mode](/docs/chats/discovery/recent): `data` holds the v2 object — `recentItems` and the `chats`, `users`, `messages`, `files` collections — with keys in camelCase.

## Parameters

| Parameter | Type | Required | Default | Description |
|----------|-----|:-----:|-----------|---------|
| `folderId` (path) | number | yes | — | Folder ID, a positive integer |
| `limit` (query) | number | no | 50 | Rows per page, from 50 to 200: a smaller value is raised to 50, a larger one is clamped to 200, both echoed in `meta.requestedLimit` and `meta.appliedLimit` |
| `lastMessageDate` (query) | string | no | — | Cursor of the next page, format `YYYY-MM-DDTHH:MM:SS` with an offset or `Z`, no fractional seconds. Not sent on the first page |
| `unreadOnly` (query) | boolean | no | `false` | `true` — only chats with unread messages. Personal folders ignore this filter |

Other parameters and repeated parameters are rejected with `400 INVALID_PARAMS`.

**Paging.** The cursor of the next page is the smallest non-empty `dateLastActivity` among the **unpinned** rows of the current page; compare moments in time, not strings. The bound is inclusive. Only `hasNextPage: false` marks the end of the list. 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; `true` without progress even at 200 is a failure, not the end of the list. The response contains repeated rows: pinned rows come first on a page — on the first page in a system folder, and on the following pages as well in a personal folder — and the all-chats folder mixes in colleagues the user has not chatted with yet. Deduplicate by `dialogId`.

## Examples

### curl — personal key

```bash
curl "https://vibecode.bitrix24.com/v1/chats/folders/1/recent?limit=200" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### curl — OAuth application

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

### JavaScript — personal key

```javascript
// Full walk of a folder in pages of 200 rows
const seen = new Map()
let cursor = null
for (;;) {
  const qs = new URLSearchParams({ limit: '200' })
  if (cursor) qs.set('lastMessageDate', cursor)
  const res = await fetch(`https://vibecode.bitrix24.com/v1/chats/folders/1/recent?${qs}`, {
    headers: { 'X-Api-Key': 'YOUR_API_KEY' },
  })
  const { data } = await res.json()
  for (const row of data.recentItems) seen.set(row.dialogId, row)
  if (!data.hasNextPage) break
  const dates = data.recentItems
    .filter((r) => !r.pinned && r.dateLastActivity)
    .map((r) => r.dateLastActivity)
  const next = dates.sort((a, b) => Date.parse(a) - Date.parse(b))[0]
  // At limit=200 a cursor without progress is a failure, not the end of the list
  if (!next || (cursor && Date.parse(next) === Date.parse(cursor))) throw new Error('The cursor did not advance')
  cursor = next
}
console.log('Chats in the folder:', seen.size)
```

### JavaScript — OAuth application

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

const { data } = await res.json()
```

## Response fields

| Field | Type | Description |
|------|-----|---------|
| `success` | boolean | Always `true` on success |
| `data.recentItems` | array | List rows: pinned first, then by activity |
| `data.recentItems[].dialogId` | string | Dialog ID (`chatXXX` or a user ID) — the deduplication key |
| `data.recentItems[].chatId` | number | Chat ID |
| `data.recentItems[].messageId` | number | ID of the last message |
| `data.recentItems[].pinned` | boolean | Whether the chat is pinned in the folder |
| `data.recentItems[].unread` | boolean | The "unread" mark — see [unread mark](/docs/chats/management/unread) |
| `data.recentItems[].dateLastActivity` | string \| null | Moment of the last activity; the smallest value among unpinned rows is the cursor of the next page |
| `data.chats` | array | Chats referenced by the rows |
| `data.users` | array | Users referenced by the rows |
| `data.messages` | array | Last messages of the rows |
| `data.files` | array | Files of the last messages |
| `data.hasNextPage` | boolean | `false` — the end of the list |
| `meta.requestedLimit` | number | The `limit` that was sent. Arrives together with `appliedLimit`, only when the value is below 50 or above 200 |
| `meta.appliedLimit` | number | The applied `limit` value |

## Response example

```json
{
  "success": true,
  "data": {
    "recentItems": [
      {
        "dialogId": "chat42",
        "chatId": 42,
        "messageId": 1002,
        "type": "chat",
        "pinned": false,
        "unread": false,
        "options": [],
        "invited": false,
        "lastReadMessageId": 1002,
        "dateUpdate": "2026-09-20T10:01:00+00:00",
        "dateLastActivity": "2026-09-20T10:01:00+00:00",
        "ownMessageId": 1001
      }
    ],
    "chats": [
      { "id": 42, "dialogId": "chat42", "name": "Project", "type": "chat", "owner": 5, "role": "member" }
    ],
    "users": [
      { "id": 7, "active": true, "name": "John Brown", "firstName": "John", "lastName": "Brown", "type": "user" }
    ],
    "messages": [
      { "id": 1002, "chatId": 42, "authorId": 7, "date": "2026-09-20T10:01:00+00:00", "text": "The mockup is ready" }
    ],
    "files": [],
    "hasNextPage": false
  }
}
```

## Error response example

422 — the folder does not exist:

```json
{
  "success": false,
  "error": {
    "code": "BITRIX_ERROR",
    "message": "FOLDER_NOT_FOUND",
    "b24Code": "FOLDER_NOT_FOUND"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|---------|
| 400 | `INVALID_PARAMS` | `folderId` is not a positive integer; a parameter other than `limit`, `lastMessageDate` or `unreadOnly`; a repeated parameter; a non-numeric `limit`; an `unreadOnly` that is neither `true` nor `false`; or a `lastMessageDate` that is not in the `YYYY-MM-DDTHH:MM:SS` format with an offset or `Z`, or that names a date or time that does not exist. Checked before any call to Bitrix24 |
| 403 | `SCOPE_DENIED` | The API key does not have the `im` scope |
| 401 | `TOKEN_MISSING` | The API key has no Bitrix24 tokens configured |
| 404 | `ENTITY_NOT_FOUND` | Bitrix24 answered "not found"; the portal code is in `error.b24Code` |
| 422 | `BITRIX_ERROR` | Bitrix24 returned an error; the portal code is in `error.b24Code`: `FOLDER_NOT_FOUND` — no such folder, `FOLDER_ACCESS_DENIED` — the folder belongs to another user, `FOLDER_OPERATION_NOT_SUPPORTED` — the folder has no chat list (the public channel showcase) or the section is not available to the user |
| 502 | `BITRIX_UNAVAILABLE` | Bitrix24 is unavailable or returned a server error |

Full list of common API errors — [Errors](/docs/errors).

## Known specifics

- **Personal folders ignore `unreadOnly`.** This is Bitrix24 behaviour: a personal folder's list is built from the chats added to it, without the unread filter, so `unreadOnly=true` returns all of its chats. To find the unread chats of a personal folder, use the [counters](/docs/chats/discovery/counters): they carry the unread count and the "unread" mark for every chat.
- The Bitrix24 code `FOLDER_NOT_FOUND` returned by this endpoint arrives as `422 BITRIX_ERROR` with `error.b24Code` and is not the same as the Vibecode code `404 FOLDER_NOT_FOUND`, which refers to [the chat folder on Drive](/docs/chats/files/folder).
- The call only reads: its argument is a folder, not a chat, so it does not make anyone a chat member, and a read-only key can run it.
- A walk is not a snapshot: a chat that new activity lifts above the cursor during the walk does not appear in it, so re-read the first page after the walk.

## See also

- [Chat folders](/docs/chats/discovery/folders)
- [Recent dialogs](/docs/chats/discovery/recent)
- [Unread counters](/docs/chats/discovery/counters)
- [Chat discovery](/docs/chats/discovery)
