# Изменения API: 28 июля 2026

[← Журнал изменений](/docs/changelog) · [Июль 2026](/docs/changelog/2026-07)

### NEW-0728-1: приложение стёртого автора больше не выдаёт новые пользовательские токены

Когда автор приложения удалил свои персональные данные, приложение перестаёт выдавать токены новым пользователям. Раньше такой запрос доходил до Битрикс24 и создавал рабочий токен вместе с именем и почтой нового пользователя, хотя автора в системе уже нет.

Возврат из [GET /v1/oauth/callback](/docs/keys-auth/oauth) в этом случае приходит на ваш `redirect_uri` с `?error=app_unavailable` — той же формой, что уже используют `token_exchange_failed`, `invalid_domain` и `profile_fetch_failed`. [POST /v1/oauth/placement-session](/docs/keys-auth/oauth) отвечает `403` с кодом `APP_UNAVAILABLE` — тем же кодом отвечает и обработчик размещения, куда Битрикс24 открывает виджет приложения (раньше там приходил `401` с общим `USER_AUTH_REQUIRED`, который читался как проблема авторизации пользователя).

Уже выданные токены и сессии этого приложения не продлеваются. Ранее работавшие вызовы не затронуты: пока автор активен, поведение обоих эндпоинтов прежнее.

### NEW-0728-2: снятие зависшего лока стало кросс-репличным; ответ DELETE /lock несёт broadcast и localLock

[DELETE /v1/infra/servers/:id/lock](/docs/infra/deploy/lock) теперь рассылает снятие лока на **все реплики** платформы, поэтому снимает зависший лок и тогда, когда он держится на другой реплице (частый случай под горизонтальным масштабированием). Ответ дополнен полями `broadcast` (снятие разослано по флоту, best-effort) и `localLock` (держался ли лок на этой реплице). Поле `released` теперь описывает только текущую реплику и **не является подтверждением снятия по всему флоту** — при зависшем локе на другой реплице `released` может быть `false`, хотя лок реально снят; не опрашивайте эндпоинт в цикле до `released: true`, повторите операцию. Прежние вызовы работают без изменений (поля добавлены аддитивно). Дополнительно зависший `exec`-лок теперь гарантированно снимается серверным авто-сбросом вскоре после истечения TTL.

Ответ [POST /v1/infra/servers/:id/exec](/docs/infra/deploy/exec) при `502 EXEC_BUSY` для galaxy-приложения теперь несёт `error.hint` с честным путём восстановления (общий exec-канал хоста; эскалация к платформенной команде — `DELETE /lock` тут не помогает, так как блокирует мьютекс агента). Ответ [POST /v1/infra/servers/:id/deploy](/docs/infra/deploy/deploy) при `409 GALAXY_APP_BUSY` дополнен `error.hint`, `retryable: true`, `retryAfter` и заголовком `Retry-After`.

### NEW-0728-3: указатели на открытые линии, чек-листы задач и ТЗ приложений в ответах самоописания

Ответ [GET /v1/guide](/docs/keys-auth/guide) дополнен указателем `data.appBlueprints` — ссылка на документацию готовых ТЗ приложений и условие ответа `403 BLUEPRINTS_DISABLED`.

Для ключа со скоупом `imopenlines` в ответ добавлен блок `data.openLines`: описание раздела, ссылки на все семь страниц документации и разграничение двух групп эндпоинтов. Настройка линий и действия оператора доступны на любом портале. Статистика дашборда до прихода обновления Битрикс24 отвечает `422 METHOD_NOT_YET_AVAILABLE`, а без права на просмотр статистики — `403 B24_TARIFF_RESTRICTION`.

В ответе [GET /v1/me](/docs/keys-auth/me) блок `api._rules` получил три новых указателя — на [чек-листы задач](/docs/entities/tasks/checklist), [открытые линии](/docs/openlines) и [ТЗ приложений](/docs/app-blueprints).

Поля аддитивные, существующие клиенты не затронуты. Сами эндпоинты не менялись.

### FIX-0728-4: деплой в галактику восстанавливает оборванный туннель хоста

**Было**

Деплой галакси-приложения на хост, туннель которого молча оборвался под нагрузкой сборки (в том числе «фантомно-CONNECTED» хост — флаг завис, а туннель уже мёртв), зацикливался на `GALAXY_HOST_UNREACHABLE` / `GALAXY_DEPLOY_INTERRUPTED`: платформа не чинила туннель сама, и повторные попытки клиента били в тот же мёртвый туннель.

**Стало**

Такой обрыв на пути деплоя теперь запускает фоновый ремонт туннеля хоста, поэтому честный повтор попадает уже на восстановленный туннель и деплой доезжает. Коды ошибок и их «повторяемая» семантика не изменились — меняется только поведение (самовосстановление).

### BC-0728-5: единый конверт 404 для несуществующих маршрутов /v1

> Поддержка старого формата до: 28.07.2026

> Прежняя форма тела не возвращается — переходного периода с двойным форматом нет, изменение действует с даты публикации.

**Было**

Запрос на несуществующий путь или неподдерживаемый HTTP-глагол под `/v1/` отвечал телом веб-сервера вне единого конверта API:

```json
{
  "message": "Route GET:/v1/dealz not found",
  "error": "Not Found",
  "statusCode": 404
}
```

**Стало**

Тот же запрос отвечает в едином конверте V1 с новым кодом `ROUTE_NOT_FOUND`. HTTP-статус прежний — `404`:

```json
{
  "success": false,
  "error": {
    "code": "ROUTE_NOT_FOUND",
    "message": "Route GET:/v1/dealz not found. Check GET /v1/guide for available endpoints and verbs."
  }
}
```

`ROUTE_NOT_FOUND` означает «такого маршрута или глагола не существует» — сверьте путь со списком в `GET /v1/guide`. Не путайте с `ENTITY_NOT_FOUND` и доменными кодами вида `*_NOT_FOUND`: там маршрут существует, не найден запрошенный объект. Вне `/v1/` форма тела 404 не изменилась.

**Что делать интеграторам**

Ветвитесь по `error.code`, а не по форме тела. Клиент, который разбирал поля `message`, `error` и `statusCode` прежнего тела на путях `/v1/`, должен перейти на единый конверт `success` и `error.code`.

### BC-0728-6: календарь: неизвестное имя в select отвечает ошибкой, а не пустым объектом

> Поддержка старого формата до: 28.07.2026

**Было**

[GET /v1/calendar-events](/docs/entities/calendar-events/list) (а также `GET /v1/calendar-events/{id}`, `POST /v1/calendar-events/search` и calendar-events-подвызовы `POST /v1/batch`) при незнакомом имени поля в `select` (например `dateFrom` — такого поля нет, реальное имя `from`) молча возвращали объекты, состоящие из одного `id`. Клиент считал, что сузил трафик, а на деле терял данные.

**Стало**

Незнакомое имя поля в `select` отвечает `400 UNKNOWN_SELECT_FIELD` с перечнем допустимых имён (`Available: …`). Имена в формате Битрикс24 (`DATE_FROM`) и алиасы дат (`updatedAt`) по-прежнему принимаются и проецируют канонический ключ — ошибку вызывают только имена, которые не разрешаются ни в одно поле схемы. На остальных сущностях поведение прежнее: незнакомое имя даёт предупреждение в `meta.warnings`, без ошибки.

**Что делать интеграторам**

Брать имена полей из [GET /v1/calendar-events/fields](/docs/entities/calendar-events/fields) и убрать из `select` несуществующие имена (`dateFrom`/`dateTo` → `from`/`to`). Клиенты, не передающие `select` или передающие корректные имена, не затронуты.

### NEW-0728-7: календарь: поля occurrenceIndex и version

У событий календаря появились два поля только для чтения. `occurrenceIndex` — порядковый номер вхождения в развёрнутой серии повторяющегося события, с нуля: строки серии делят один `id`, и пара `id` + `occurrenceIndex` однозначно идентифицирует строку набора. `version` — монотонный счётчик изменений события, растёт при каждом изменении и не зависит от региональных настроек аккаунта. Рецепт диффа: запросите [GET /v1/calendar-events](/docs/entities/calendar-events/list) с `select=id,version`, сравните пары со своим снимком и дочитайте изменённые события по `id`.

### NEW-0728-8: чаты: эхо ограничения limit в meta

Три эндпоинта чатов — [GET /v1/chats/recent](/docs/chats/discovery/recent), [GET /v1/chats/:dialogId/messages](/docs/chats/messages/list) и `GET /v1/chats/:dialogId/users` — при срезании переданного `limit` до допустимого диапазона дополняют ответ полем `meta` с парой `requestedLimit` и `appliedLimit`: сколько запросили и сколько применено. Когда `limit` в диапазоне, `meta` не добавляется — конверт прежний. Для чтения сообщений задокументирован реальный потолок облачного Битрикс24 — не больше 50 записей за вызов независимо от `limit`, продолжение читается курсором `lastId`.

### NEW-0728-9: взаимные алиасы полей дат updatedAt и createdAt

Поля дат в каталоге сущностей носят два семейства имён: одни сущности объявляют `updatedAt` и `createdAt`, другие — `updatedTime` и `createdTime`. Теперь члены пары принимаются на вход взаимозаменяемо: в `filter` и `select` — на всех сущностях, где объявлен парный ключ, в `sort` — на сущностях с camelCase-схемой полей. Например, `updatedTime` на сущности с полем `updatedAt` работает как `updatedAt`, и наоборот. Канонические имена полей в ответах не меняются — алиас действует только на входе.

### NEW-0728-10: POST-алиас поиска по Базе знаний

Поиск документов Базы знаний 2.0 принимает и `POST /v1/note/documents/search` с JSON-телом `{ "query": "...", "limit": 20 }` — для агентов, которые ожидают поиск POST-запросом по аналогии с остальными сущностями. Канонической остаётся форма [GET /v1/note/documents/search](/docs/note/documents/search) с query-параметрами. Обе формы принимают только `query` и `limit`, при передаче параметра и в теле, и в query приоритет у тела.

### NEW-0728-11: лента: limit до 200 записей за запрос

[GET /v1/posts](/docs/feed/posts/list) принимает `limit` от 1 до 200. Страница ленты Битрикс24 фиксирована в 50 записей — при `limit` больше 50 платформа склеивает до четырёх страниц в один ответ. Значение больше 200 отвечает прежним `400 INVALID_LIMIT`. В `meta` добавлено поле `returned` — фактическое число записей в ответе, а `meta.nextOffset` при многостраничном чтении выводится из окна ответа, чтобы цепочка страниц продолжалась как раньше.

### FIX-0728-12: календарь: честные offset и hasMore, детерминированный порядок

**Было**

[GET /v1/calendar-events](/docs/entities/calendar-events/list) при любом `offset` возвращал начало одного и того же набора: запрошенный диапазон приходит от Битрикс24 одним массивом без пагинации, поэтому каждая «страница» повторяла первую, `meta.hasMore` оставался `true`, а хвост набора за пределами первой страницы был недоставаем.

**Стало**

Полный набор сортируется детерминированно — по началу события `from`, при равенстве по `id`, затем по `occurrenceIndex` — и из него отдаётся честное окно от `offset` до `offset + limit`. `meta.total` — число вхождений в наборе: повторяющиеся события развёрнуты по-вхожденно, строки серии делят один `id`. `meta.hasMore` отвечает `true` только пока за окном остаются записи. Порядок элементов в ответе стал детерминированным и может отличаться от прежнего.

**Влияние на интеграторов**

Обход набора через `offset` теперь отдаёт весь диапазон. Клиентам, которые обходили дубли страниц самостоятельно, ничего менять не нужно — дублей больше нет.

### FIX-0728-13: select принимает объявленные имена Битрикс24 и предупреждает о незнакомых полях

**Было**

Параметр `select` понимал только канонические имена полей из `GET /v1/{entity}/fields`. Имя в другом написании — исходное имя Битрикс24 (`DATE_FROM`, `UF_DEPARTMENT`) или другой регистр — молча выпадало из проекции: поля не было в ответе без какого-либо признака ошибки, а запрос из одних таких имён вырождался в объекты с единственным полем `id`. Пакетные вызовы `select` не применяли вовсе: и глобальный [POST /v1/batch](/docs/batch), и пакетный вызов одной сущности `POST /v1/{entity}/batch` возвращали полные объекты.

**Стало**

`select` принимает канонические имена без учёта регистра, объявленные исходные имена Битрикс24 (`DATE_FROM` проецирует `from`, `UF_DEPARTMENT` — `departmentId`) и взаимные алиасы полей дат — поле приходит в ответе под каноническим ключом. Незнакомое имя больше не теряется молча: список, поиск и получение записи по `id` дополняют ответ массивом `meta.warnings` с записями `{ "code": "UNKNOWN_SELECT_FIELD", "field": "<имя>" }` — до 10 предупреждений на ответ. Оба пакетных вызова применяют `select` к операциям списка и поиска так же, как одиночные эндпоинты; получение записи по `id` внутри глобального пакета `select` не применяет. Глобальный вызов дополнительно отдаёт предупреждения о незнакомых именах в `meta` по каждому своему вызову. Пакетный вызов одной сущности предупреждений не несёт и жёсткого отклонения незнакомых имён не выполняет — незнакомое имя там по-прежнему просто отсутствует в ответе.

**Влияние на интеграторов**

Ответы одиночных эндпоинтов только дополняются. В пакетных вызовах клиент, который передавал `select` и при этом читал поля за его пределами, теперь получит только запрошенные поля — уберите `select` из вызова или перечислите в нём все нужные поля.

### FIX-0728-14: быстрый отказ оконного поиска при таймауте портала

**Было**

Поиск `POST /v1/{entity}/search` с широким диапазоном дат разбивается на временные окна. Таймаут Битрикс24 на первом окне пропускался, и остальные окна выполнялись каждое со своим таймаутом: до ответа проходило 60–75 секунд, после чего приходил `503 BITRIX_TIMEOUT`. Если поздние окна успевали, возможен был частичный `200` с неполными данными после той же минуты ожидания.

**Стало**

Таймаут первого окна (`BITRIX_TIMEOUT`) завершает запрос сразу: ответ `503` с кодом `BITRIX_TIMEOUT` и заголовком `Retry-After` приходит примерно за 15 секунд, остальные окна не выполняются. Частичный `200` при таймауте первого окна больше невозможен — это осознанный размен: окна одинаковы по форме, таймаут первого предсказывает таймауты остальных, а частичный ответ после минуты ожидания провоцировал волны повторов. Таймаут любого последующего окна обрабатывается как раньше — окно пропускается, ответ может быть частичным.

**Влияние на интеграторов**

Повторяйте запрос по заголовку `Retry-After`. Клиенты, которые полагались на частичный ответ при перегрузке портала, теперь получают быстрый `503` — сузьте диапазон дат или повторите позже.

### FIX-0728-15: Galaxy-деплой из .zip: честная причина ошибки извлечения вместо EMPTY_BUILD_CONTEXT

Деплой Galaxy-приложения из .zip теперь возвращает реальную (санитизированную) причину ошибки извлечения архива в `buildLog` 502-ответа, а не вводящее в заблуждение «EMPTY_BUILD_CONTEXT»/общее сообщение.

### FIX-0728-16: отбой лимитера времени операций Битрикс24 возвращается как `OPERATION_TIME_LIMIT` с `Retry-After`

**Было**

Когда портал отбивал вызов метода, израсходовавшего бюджет рабочего времени, платформа
отдавала `429 RATE_LIMITED` с `Retry-After: 2`, а сам вызов повторяла до трёх раз. Против
адресного отказа на несколько минут эти повторы не помогали, а `Retry-After: 2` вводил в
заблуждение: клиент возвращался через две секунды и получал тот же отказ.

**Стало**

Такой отбой возвращается кодом `OPERATION_TIME_LIMIT` — тем же, что отдаёт портал, — с
`Retry-After`, посчитанным от известного срока снятия ограничения, и понятным текстом в
`userMessage`. Повторы отключены: ограничение адресное — на тройку «портал + ключ +
метод», ровно так же, как его накладывает сам Битрикс24, — и до истечения срока платформа
отбивает вызовы этого метода сама, не обращаясь к порталу. Остальные методы портала, как и
тот же метод под другим ключом, не затронуты. Прочие отказы `429` (в том числе
`RATE_LIMITED` и `QUEUE_OVERFLOW`) не изменились.

Соблюдайте `Retry-After`: раньше срока тот же вызов пройти не может. Тяжёлые чтения стоит
разредить по времени или сузить — меньше полей, меньше страница, `POST /v1/batch`.

### FIX-0728-17: Значение * в select возвращает все поля

**Было**

Привычная для Битрикс24 запись `select: ["*"]` (и `["*", "UF_*"]`) на одиночных эндпоинтах приводила к обратному результату: `*` не совпадает ни с одним объявленным полем, поэтому в ответе оставался только `id`. Признака ошибки не было — запись просто приходила пустой.

**Стало**

`*` и `UF_*` (в любом регистре) распознаются как запрос «вернуть все поля»: отбор полей не применяется, приходит полная запись. Незнакомое имя, переданное рядом со звёздочкой, не отклоняется — ответ дополняется предупреждением `UNKNOWN_SELECT_FIELD`. Это работает одинаково в списке, поиске, получении записи по `id` и в обоих пакетных вызовах — [POST /v1/batch](/docs/batch) и `POST /v1/{entity}/batch`. У событий календаря, где незнакомое имя поля отвечает ошибкой `400`, значение `*` ошибкой не считается.

**Влияние на интеграторов**

Ничего менять не нужно. Клиент, переносивший запросы из портального кода Битрикс24 вместе с `select: ["*"]`, начнёт получать полные записи вместо объектов с одним `id`.

### FIX-0728-18: оконный поиск останавливается, набрав запрошенное, и сообщает о неполном окне

**Было**

`POST /v1/{entity}/search` с широким диапазоном дат разбивает диапазон на временные окна и объединяет их результаты. Обход шёл до конца диапазона даже тогда, когда запрошенных `limit` записей уже набрано: поиск с `limit: 50` по диапазону в несколько месяцев вычитывал весь диапазон целиком — отвечал долго и создавал на аккаунте Битрикс24 нагрузку, несопоставимую с размером ответа. Если внутри одного окна подходящих записей оказывалось больше, чем отдаёт одно чтение окна, окно возвращало только начало своего набора, и делало это молча: ни признака в ответе, ни способа дочитать остаток (оконный поиск отвергает `offset > 0` кодом `UNSTABLE_OFFSET_PAGINATION`).

Молчала и вторая причина неполноты, на аккаунтах, где окна читаются пакетами: набрав предельные 5000 записей, поиск перестаёт отправлять оставшиеся окна и отдаёт срезанный префикс — в ответе об этом тоже ничего не было.

**Стало**

Обход окон прерывается, как только набрано больше уникальных записей, чем запрошено в `limit`. В ответе по-прежнему не больше `limit` записей, а признак «есть ещё» несёт `hasMore`. При досрочной остановке `meta.total` равен количеству набранного, то есть это нижняя оценка числа подходящих записей, а не полный счёт по диапазону — ровно так уже вёл себя этот поиск на аккаунтах с пакетным чтением окон, теперь поведение единое.

Неполная выдача больше не молчит — ни по одной из двух причин, независимо от того, читаются окна по одному или пакетами, **на сущностях, чей метод списка Битрикс24 отдаёт постранично**. Ответ дополняется предупреждением `{ "code": "WINDOW_TRUNCATED", "field": "…", "message": "…" }` в массиве `meta.warnings`, где `field` — поле диапазона, по которому шло разбиение. Код предупреждения один и тот же в обоих случаях — ветвиться по нему можно, не разбирая текст; `message` называет сработавшую причину: одно окно держало больше записей, чем отдаёт одно чтение окна, либо набраны предельные 5000 записей и оставшиеся окна не отправлялись.

Исключения названы прямо. Три сущности предупреждения не получат никогда, потому что их метод списка Битрикс24 не отдаёт постранично: файлы и папки (`/v1/files`, `/v1/folders`) и рабочие группы (`/v1/workgroups`). Там запрос идёт одиночным вызовом, `limit` в Битрикс24 не передаётся вовсе, и аккаунт отдаёт свою страницу примерно на 50 записей: окно, в котором лежит 200 дисковых объектов, вернёт 50 и промолчит, как и до правки. У страниц (`/v1/pages`), сайтов (`/v1/sites`) и событий календаря (`/v1/calendar-events`) метод списка тоже не постраничный, но он отдаёт весь запрошенный набор за один вызов — там предупреждение просто недостижимо, а неполноты не возникает.

Дополнительно снижена нагрузка, которую этот поиск создаёт на аккаунте Битрикс24: вырожденная нижняя граница по `id` больше не уходит в Битрикс24. Снимаются две формы, и опираются они на разное. `>=` со значением 0 или меньше и `>` с отрицательным значением исключают только отрицательные `id` — они тавтологичны при одном допущении неотрицательности. `>` ровно с нулём (`filter[>id]=0` — именно её передают в начале обхода курсором) исключает запись с `id = 0`, то есть дополнительно опирается на автоинкрементную нумерацию записей Битрикс24 от единицы; это целевой случай правки, и он снимается сознательно. Граница `>=id=1` сохраняется — гард намеренно узкий и смотрит только на значения 0 и меньше.

Состав выдачи не меняется ни на одной сущности, где Битрикс24 действительно применяет фильтр по `id`. Известное исключение — воронки (`/v1/categories`): среди них есть запись с `id: 0` («Общая»), но метод списка воронок игнорирует `filter` целиком, поэтому и до, и после правки в ответ приходит полный набор воронок. Форма ответа прежняя.

**Влияние на интеграторов**

Не считайте `meta.total` точным числом подходящих записей на широком диапазоне дат — при досрочной остановке это нижняя оценка; ориентируйтесь на `hasMore`. Дочитать оконный поиск постранично нельзя (`offset > 0` отвергается), поэтому «есть ещё» закрывается либо большим `limit` (до 5000), либо более узким диапазоном дат.

Проверяйте `meta.warnings` на `WINDOW_TRUNCATED`: это признак неполной выдачи, и пагинацией он тоже не лечится — сузьте диапазон дат или добавьте фильтров, чтобы поиск перестал упираться в потолок. Одного `hasMore` для этого недостаточно: оконное разбиение работает только при `offset === 0`, поэтому запрос следующей страницы уйдёт уже не оконным и даст другой результат. Поиск по узкому диапазону, который не разбивается на окна, не затронут.

Учтите границу оконного поиска: сортировка применяется внутри окна, а не поверх объединения окон. Окна строятся от старых к новым и склеиваются в этом же порядке, после чего результат режется до `limit`; глобальной сортировки по объединению нет. Поэтому запрос с сортировкой по убыванию и небольшим `limit` по широкому диапазону дат возвращает самые СТАРЫЕ подходящие записи, а не самые новые. Само поведение не новое, но досрочная остановка закрывает сортировку после слияния как способ починки (записи поздних окон больше не вычитываются), поэтому называем это прямо. Нужен настоящий «последние N по дате» — либо сузьте диапазон так, чтобы разбиение на окна не включалось, либо возьмите диапазон одним запросом с большим `limit` и отсортируйте на своей стороне.
