## Поиск по Диску

`POST /v1/disk/search`

Ищет файлы и папки во всём Диске, который сейчас доступен пользователю Битрикс24, и возвращает карточки объектов без содержимого файлов. Выдача читается постранично, по курсору.

## Поля запроса (body)

| Поле | Тип | Обяз. | По умолч. | Описание |
|------|-----|:-----:|-----------|----------|
| `query` | string | да | — | Поисковая строка. Края обрезаются, пробелы внутри схлопываются в один. Длина — от 3 до 255 символов до и после этой обработки |
| `type` | string | да | — | Что искать: `file`, `folder` или `all` |
| `filter.storageId` | string | нет | — | ID хранилища, десятичная строка. Список хранилищ: [`GET /v1/storages`](/docs/entities/storages/list) |
| `filter.folderId` | string | нет | — | ID папки, десятичная строка. Корневая папка хранилища — поле `rootFolderId` в [`GET /v1/storages`](/docs/entities/storages/list), вложенные папки — [`GET /v1/folders`](/docs/entities/folders/list) или `objectId` карточки с `objectType: "folder"` из этого поиска |
| `filter.fileType` | string | нет | — | Тип файла, например `document`. Длина от 1 до 64 символов. Только при `type: "file"`, с другим `type` запрос отклоняется `400 INVALID_PARAMS` |
| `limit` | number | нет | `50` | Верхняя граница числа карточек в ответе, от 1 до 1000 |
| `cursor` | string | нет | — | Значение `nextCursor` из предыдущего ответа. В первом запросе не передаётся |

Других полей тело не принимает: неизвестное поле, как и поле в `filter` вне списка выше, даёт `400 INVALID_PARAMS`.

## Навигация

1. Отправьте первый запрос без `cursor`.
2. Если в ответе `hasMore: true`, повторите запрос с теми же `query`, `type` и `filter`, передав `nextCursor` целиком в поле `cursor`.
3. Продолжайте, пока `hasMore` не станет `false`. Страница может прийти короче `limit` и даже пустой при `hasMore: true` — это не конец выдачи.

Курсор действует час с момента первого запроса обхода, продолжение срок не продлевает. Курсор привязан к строке поиска, `type`, `filter` и к привязке ключа из [`GET /v1/disk/capabilities`](/docs/disk/capabilities). Просроченный курсор, курсор от другого запроса и курсор после роста `identity.accountGeneration` дают `403 DISK_GENERATION_STALE` — начните обход заново без `cursor`.

## Примеры

### curl — личный ключ

```bash
curl -X POST https://vibecode.bitrix24.tech/v1/disk/search \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "vibe-disk-docs-check",
    "type": "file",
    "limit": 1
  }'
```

### curl — OAuth-приложение

```bash
curl -X POST https://vibecode.bitrix24.tech/v1/disk/search \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "vibe-disk-docs-check",
    "type": "file",
    "limit": 1
  }'
```

### JavaScript — личный ключ

```javascript
// Обход всех страниц выдачи курсором
const items = []
let cursor

do {
  const res = await fetch('https://vibecode.bitrix24.tech/v1/disk/search', {
    method: 'POST',
    headers: {
      'X-Api-Key': 'YOUR_API_KEY',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      query: 'vibe-disk-docs-check',
      type: 'file',
      limit: 1,
      ...(cursor ? { cursor } : {}),
    }),
  })
  const { success, data, error } = await res.json()
  if (!success) throw new Error(error.code)
  items.push(...data.items)
  cursor = data.hasMore ? data.nextCursor : undefined
} while (cursor)

console.log(items.map(item => item.name))
```

### JavaScript — OAuth-приложение

```javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/disk/search', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    query: 'vibe-disk-docs-check',
    type: 'file',
    limit: 1,
  }),
})

const { data } = await res.json()
console.log(data.items, data.hasMore)
```

## Поля ответа

| Поле | Тип | Описание |
|------|-----|----------|
| `success` | boolean | `true` при успешном ответе |
| `data.items` | array | Карточки найденных объектов, не больше `limit` |
| `data.items[].objectId` | string | ID файла или папки. Для файла подходит в [`GET /v1/files/:id`](/docs/entities/files/get), для папки — в `filter.folderId` следующего поиска |
| `data.items[].objectType` | string | `file` или `folder` |
| `data.items[].name` | string | Название объекта |
| `data.items[].storageId` | string | ID хранилища, в котором лежит объект |
| `data.items[].parentId` | string | ID родительской папки. Может отсутствовать |
| `data.items[].size` | string | Только у файла. Размер в байтах, десятичная строка |
| `data.items[].contentFileId` | string | Только у файла. ID текущего содержимого файла |
| `data.items[].contentGeneration` | string | Только у файла. Номер текущей версии содержимого. Вместе с `contentFileId` и `size` нужен для загрузки новой версии через [`POST /v1/disk/uploads/reserve`](/docs/disk/uploads-reserve) |
| `data.items[].safeLink` | string | Адрес объекта в веб-интерфейсе Битрикс24. Только `https`, без параметров и учётных данных. Ссылкой на скачивание не является. Отсутствует, если портал не отдал такой адрес |
| `data.items[].source` | string | Происхождение карточки. В поиске всегда `native-current` — текущие данные портала на момент запроса |
| `data.hasMore` | boolean | Есть ли продолжение выдачи |
| `data.nextCursor` | string | Курсор следующей страницы. Только при `hasMore: true`, см. «Навигация» |
| `data.truncated` | boolean | `true`, когда выдача упёрлась в предел 1000 позиций поиска портала: результаты дальше этой границы недоступны, уточните `query` или `filter` |
| `data.limitations` | string[] | Пояснения к выдаче на английском. Первые две строки приходят всегда. Дополнительные строки сообщают, что часть найденных объектов не удалось показать или что страница закончилась раньше из-за ограничения времени или размера ответа — тогда продолжайте обход по `nextCursor` |

## Пример ответа

Первая страница при `limit: 1` — найдено два файла, второй придёт по `nextCursor`:

```json
{
  "success": true,
  "data": {
    "items": [
      {
        "objectId": "10901",
        "objectType": "file",
        "name": "vibe-disk-docs-check-2.txt",
        "storageId": "1",
        "parentId": "1",
        "size": "29",
        "contentFileId": "39571",
        "contentGeneration": "1",
        "safeLink": "https://example.bitrix24.ru/company/personal/user/1/disk/file/vibe-disk-docs-check-2.txt",
        "source": "native-current"
      }
    ],
    "hasMore": true,
    "nextCursor": "4OI2oJB6n4E0MLK5:ipx34E3vaJmBYDYNX5IKpKEMOJJEocUC2GenIl8JleEm7X1UnoGfWTtf2E1/gDE0aR3Nq+fesCeRRqN3520Gq0hkB2XxI+rSCbg+W3T6NbfMNHYlDWg010aWQOYz2VkxapPddVjEYUT3BUX4Hn5j68DX05BTFZv9gE5F4qUwxcEOTaeW8ReIfBa/dcCR0CzHMaEVxmilisorOtaRWGLDhdtjvYgRmIQpcFSOPuEfYn2JWYTZPRQmcn92Bi8PZx1Y70zRSYXxzw/mrsYgzAuzICojBvE=:LaEM6qPdvLmXkEoPFgtftA==",
    "truncated": false,
    "limitations": [
      "Native search pages contain at most 50 objects and offsets stop at 1000.",
      "The native index and access rights can change between requests; search is not a stable snapshot."
    ]
  }
}
```

На последней странице `hasMore` равно `false`, а поля `nextCursor` нет.

## Пример ответа при ошибке

400 — поисковая строка короче трёх символов:

```json
{
  "success": false,
  "error": {
    "code": "INVALID_PARAMS",
    "message": "Disk request unavailable",
    "retrySafe": false
  }
}
```

## Ошибки

| HTTP | Код | Описание |
|------|-----|----------|
| 400 | `INVALID_PARAMS` | Тело нарушает правила полей: не передан `query` или `type`, длина `query` вне 3–255, `filter.fileType` при `type` не `file`, `limit` вне 1–1000, ID не десятичная строка, неизвестное поле |
| 401 | `TOKEN_MISSING` | У ключа нет настроенной авторизации на портале Битрикс24 |
| 401 | `DISK_AUTH_UNAVAILABLE` | Не удалось подтвердить пользователя Битрикс24: вебхук ключа не совпадает с порталом или пользователем, ключ OAuth-приложения передан без `Authorization: Bearer`, токен сессии принадлежит другому приложению или порталу, либо токен Битрикс24 истёк или отозван |
| 401 | `MISSING_API_KEY` | Не передан заголовок `X-Api-Key` |
| 401 | `INVALID_API_KEY` | Ключ не опознан |
| 401 | `TOKEN_REFRESH_FAILED` | Не удалось обновить авторизацию портала для ключа. Повтор не поможет, пока ключ не авторизован заново |
| 403 | `DISK_GENERATION_STALE` | Курсор просрочен, выдан для другого запроса или до роста `identity.accountGeneration`. Начните обход без `cursor` |
| 403 | `DISK_ACCESS_DENIED` | Битрикс24 отказал пользователю в доступе к поиску |
| 403 | `SCOPE_DENIED` | У ключа нет скоупа `disk` |
| 501 | `DISK_UNSUPPORTED` | Портал не поддерживает поиск по Диску — признак `search.available` в [`GET /v1/disk/capabilities`](/docs/disk/capabilities) |
| 502 | `DISK_UNSUPPORTED` | Портал вернул выдачу в неожиданном виде либо первая же карточка превышает предел размера ответа |
| 502 | `DISK_READ_UNAVAILABLE` | Поиск не удался по причине, которой нет среди остальных кодов. Повторите запрос позже |
| 503 | `BITRIX_TIMEOUT` | Отведённые на запрос 20 секунд истекли до первого обращения к поиску портала, либо портал не ответил вовремя |
| 503 | `QUERY_LIMIT_EXCEEDED` | Портал ограничил частоту запросов |
| 503 | `QUEUE_OVERFLOW`, `QUEUE_TIMEOUT` | Очередь вызовов портала переполнена. Заголовок `Retry-After` называет паузу перед повтором |
| 503 | `user_self_deletion_pending` | Владелец ключа запросил удаление своей учётной записи — обращения к Диску остановлены |
| 503 | `RATE_LIMITED` | Портал ответил HTTP 429. Паузу перед повтором называет заголовок `Retry-After` |
| 503 | `ERROR_LOOP_DETECTED` | Повторяющиеся ошибки этого ключа на этом методе временно остановили вызовы. Паузу называет заголовок `Retry-After` |

Полный список общих ошибок API — [Ошибки](/docs/errors).

## Известные особенности

**Выдача — не снимок Диска.** Поисковый индекс портала и права доступа меняются между запросами, поэтому при обходе курсором объект может появиться, исчезнуть или сместиться между страницами.

**Один запрос ограничен по времени и объёму.** На запрос отводится 20 секунд, включая проверку пользователя. За один запрос читается столько страниц поиска портала по 50 позиций, сколько нужно на `limit` карточек, а карточки в ответе занимают не больше 2 МиБ. Если время, страницы или размер исчерпаны раньше, чем набралось `limit` карточек, ответ приходит с тем, что успело набраться, `hasMore: true` и строкой о причине в `limitations`.

**Ключ «только чтение» ищет.** Запрос не изменяет данные и доступен ключу в [режиме «только чтение»](/docs/keys-auth/access-mode).

**Содержимое одной папки.** Чтобы получить содержимое известной папки с условиями по полям, а не искать по всему Диску, используйте [`POST /v1/files/search`](/docs/entities/files/search).

## Смотрите также

- [Получить возможности Диска](/docs/disk/capabilities)
- [Проверить видимость объектов Диска](/docs/disk/sync-check)
- [Зарезервировать загрузку файла на Диск](/docs/disk/uploads-reserve)
- [Список хранилищ](/docs/entities/storages/list)
- [Список содержимого папки](/docs/entities/folders/list)
- [Ошибки](/docs/errors)
