# Изменения API: 21 августа 2026

[← Журнал изменений](/docs/changelog) · [Август 2026](/docs/changelog/2026-08)

### FIX-0821-1: iblockTypeId=structure принимается в /v1/lists

**Было**

`iblockTypeId` принимал только `lists`, `lists_socnet` и `bitrix_processes`. Значение `structure` (тип инфоблока графика отсутствий) давало `400 INVALID_IBLOCK_TYPE`, хотя `GET /v1/lists/:iblockId/type` его возвращал.

**Стало**

`structure` входит в допустимый набор на всех маршрутах `/v1/lists`. Ответ Битрикс24 на этом типе (данные или `422`/`403`) больше не подменяется нашим `400`. Прочие неизвестные типы по-прежнему дают `400 INVALID_IBLOCK_TYPE`. Значение по умолчанию не изменилось — `lists`.

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

Запросы к инфоблокам типа `structure`, включая штатный график отсутствий `absence`, теперь доходят до Битрикс24. Существующие интеграции с другими типами менять не нужно.

### FIX-0821-2: currencyId и явный select невозвращаемых полей

**Было**

[POST /v1/products](/docs/entities/products/create) и [PATCH /v1/products/:id](/docs/entities/products/update) принимали валюту только под именем `currency`. Явный `select` поля с признаком `notReturned: true` принимался без предупреждения, хотя значения в ответе не было.

**Стало**

Методы записи товара принимают `currencyId` как дополнительное имя `currency`. Если переданы оба имени, используется `currency`, а в ответах по-прежнему приходит только `currency`. Дополнительное имя помечено как `writeOnly` и `notReturned` в `/fields`, `/v1/guide` и OpenAPI. Явный `select` любого невозвращаемого канонического имени теперь добавляет предупреждение `UNKNOWN_SELECT_FIELD`: это `products.currencyId`, `tasks.realStatus`, `product-sections.sort`, `bank-details.entityTypeId`, `telephony-lines.serverName`, `bizproc-templates.templateData` и `catalog-products.iblockSection`. Нативное имя Битрикс24 `select=CURRENCY_ID` по-прежнему выбирает читаемое поле `products.currency`. Поведение каждого имени описано в справочниках [полей товаров](/docs/entities/products/fields), [задач](/docs/entities/tasks/fields), [разделов товаров](/docs/entities/product-sections/fields), [банковских реквизитов](/docs/entities/bank-details/fields), [линий телефонии](/docs/telephony/lines/fields), [шаблонов бизнес-процессов](/docs/entities/bizproc-templates/fields) и [товаров каталога](/docs/entities/catalog-products/fields). Фильтрация по валюте не поддерживается и возвращает `400 UNSUPPORTED_FILTER`.

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

Запросы чтения продолжают возвращать `200` и остальные выбранные поля, но теперь содержат предупреждение. Уберите невозвращаемые имена из `select`: для валюты используйте `currency`, для фактического статуса задачи — `status`.

### FIX-0821-3: фильтр товаров по описанию больше не отклоняется

**Было**

[GET /v1/products](/docs/entities/products/list) с `filter[description]` и [POST /v1/products/search](/docs/entities/products/search) с тем же фильтром отвечали `400 UNSUPPORTED_FILTER`, хотя описание сохранялось при создании товара.

**Стало**

Точное равенство и `$in` по полю `description` принимаются так же, как по `name`. Операторы, включая `$contains`, по-прежнему отвечают `400`. Фильтры по `price` и `currency` не изменились.

### FIX-0821-4: отказ при создании сотрудника без отдела называет поле, которое нужно передать

**Было**

`POST /v1/users` без `departmentId` на портале с установленным модулем extranet отвечал `422` с текстом `no_extranet_field`. Поля с таким именем нет ни в теле запроса, ни в выдаче `GET /v1/users/fields`, и ничто в ответе не указывало на отдел — по такому отказу нельзя было понять, что именно исправить. Сотрудник при этом не создавался.

**Стало**

Ответ дополнен подсказкой: она называет поле в обоих написаниях — `UF_DEPARTMENT` для прямого вызова и `departmentId` для обёртки `POST /v1/users` (оба принимаются), указывает источник значений `GET /v1/departments`, приводит корневой отдел как рабочий пример и упоминает `POST /v1/users/invite`, который подставляет отдел сам. Для внешнего пользователя названа альтернатива — `EXTRANET` вместе с `SONET_GROUP_ID` вместо отдела. Описание поля `departmentId` в справочнике полей теперь тоже говорит про это требование, поэтому условие видно до отправки запроса.

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

Действий не требуется: код ответа и структура конверта не изменились, добавилась только подсказка. Поле `departmentId` намеренно не сделано обязательным на стороне API Вайбкод — портал без модуля extranet принимает создание без отдела, и жёсткое требование сломало бы такие запросы.

### NEW-0821-5: метаданные диалога Открытой линии через API

Добавлен эндпоинт [POST /v1/openlines/dialogs/lookup](/docs/openlines/dialog) — обёртка над методом Битрикс24 `imopenlines.dialog.get`. Возвращает карточку одного диалога Открытой линии (название, тип, линию, число сообщений, даты) по одному из идентификаторов: `chatId`, `dialogId` или `sessionId`. Переписку метод не отдаёт. Требует скоуп `imopenlines`.

В ответе есть производное поле `lineId` — идентификатор линии, разобранный из привязки диалога; он даёт вход в [GET /v1/openline-configs/:id](/docs/openlines/config/get). Поиск по `sessionId` позволяет найти диалог прямо по идентификатору сессии из поля `id` ответа [POST /v1/openlines/sessions/search](/docs/openlines/sessions).

### BC-0821-6: структура в скалярном поле на записи отклоняется вместо тихой потери значения

> Поддержка старого формата до: не предусмотрена

**Было**

Запись сущности принимала в поле, объявленное скаляром, объект или массив и отвечала успехом. [POST /v1/deals](/docs/entities/deals/create) с телом `{"title": {"a": 1}}` возвращал 201, а в карточке сделки оказывалась строка `Array`: Битрикс24 приводит массив к строке именно так. Значение восстановить было нельзя, и ошибки клиент не видел.

**Стало**

Такой запрос отклоняется до вызова Битрикс24 — `400` с кодом `INVALID_PARAMS` и именем поля. Правило применяется к полям, объявленным как `string`, `number`, `boolean`, `date` и `datetime`, на записи сущностей по путям `/v1/<entity>`: создание и обновление, [POST /v1/batch](/docs/batch), пакетная запись по сущности и импорт.

Поведение, которое НЕ изменилось: число и логическое значение в строковом поле по-прежнему принимаются (Битрикс24 сохраняет `123456` и `1` — значение не теряется), `null` по-прежнему принимается, а поля, объявленные как `object`, `array` и многозначные, структуру принимали и принимают.

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

Проверьте код, который собирает тело записи из внешнего источника: если в скалярное поле уезжал объект или массив, раньше запрос завершался успехом с потерей значения, а теперь вернёт `400`. Отправляйте в такое поле скалярное значение.

Границы названы явно. Имя, которого нет в схеме сущности (пользовательские поля `UF_*`, `propertyNNN`, опечатка), проверке не подлежит — у него нет объявленного типа. Форма отказа различается по поверхности, и различается охват. Общий пакетный вызов отклоняет только свой подвызов и кладёт отказ в `data.errors["<id>"]` отдельными полями `code` и `message`. Пакетная запись по одной сущности и импорт отклоняют весь запрос: `400` с кодом `BATCH_ITEM_VALIDATION` или `IMPORT_ITEM_VALIDATION`, а номер элемента и `INVALID_PARAMS` — внутри `message`. Поэлементных результатов в таком ответе нет, и в Битрикс24 не уходит ни одной записи. Несколько обособленных маршрутов записи проверку пока не проходят — адреса, комментарии задач, шаблоны документов, настройки открытых линий и товарные строки; там прежнее поведение сохраняется, и они закрываются отдельно.

### FIX-0821-7: PATCH /v1/infra/servers/:id/access-policy сохраняет список доступа при смене политики

**Было**

При переключении [PATCH /v1/infra/servers/:id/access-policy](/docs/infra/access/access-policy) с `NAMED_USERS` или `DEPARTMENT` на любую другую политику список доступа (пользователи и отделы, добавленные через `POST /access`) стирался безвозвратно — хотя документация обещала, что записи сохраняются в базе и просто не применяются, пока политика не станет именной снова.

**Стало**

Список сохраняется при уходе с `NAMED_USERS`/`DEPARTMENT` — поведение снова соответствует документации.

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

Действий не требуется — поведение приведено в соответствие с уже опубликованной документацией.

### BC-0821-8: занятость общего exec-канала галактики отдаётся как 409, а не 502

> Поддержка старого формата до: не предусмотрена

**Было**

[POST /v1/infra/servers/:id/exec](/docs/infra/deploy/exec) для приложения в галактике (`kind: "GALAXY_APP"`) отвечал `502` с кодом `EXEC_BUSY`, когда общий exec-канал хоста был занят. Ни заголовка `Retry-After`, ни полей `retryable` / `retryAfter` в ответе не было, поэтому машинный клиент читал отказ как сбой шлюза и не повторял вызов. Тот же по смыслу отказ на [POST /v1/infra/servers/:id/deploy](/docs/infra/deploy/deploy) при этом уже приходил как `409` с сигналом повтора.

Подсказка `error.hint` в ответах `409 EXEC_BUSY` на галактических маршрутах предлагала расклинить канал вызовом `POST /v1/infra/servers/:id/unstick`. Владельцу приложения и владельцу хоста этот вызов недоступен по контракту — он отвечает `409 GALAXY_UNSTICK_UNSUPPORTED`, потому что exec-канал общий для всех приложений хоста.

**Стало**

Занятость общего exec-канала хоста приходит как `409` с кодом `EXEC_BUSY`, заголовком `Retry-After` и полями `retryable: true` / `retryAfter` (секунды) — тем же контрактом, что и остальные отказы «занято». Прочие ошибки выполнения у приложения в галактике по-прежнему приходят со статусом `502`.

На одном статусе `409` теперь живут два разных состояния, и они машинно различимы по наличию поля `error.hint.autoExpiresInSeconds`: лок самого приложения несёт его (остаток времени жизни лока), занятость общего канала хоста — нет. По `recoveryAction` их различать нельзя: у приложения в галактике и у самого галактического хоста это поле конкретного вызова не называет вовсе — только «подождать» и «повторить», — потому что снятие лока способно оборвать выполняющуюся операцию, а на общем хосте вместе с ней и команды соседних приложений. Машинный совет поэтому в обоих состояниях один — подождать и повторить по `Retry-After`.

⚠️ Сказанное относится к галактическим состояниям, а не к маршруту `/exec` целиком. На отдельной виртуальной машине (`kind: "STANDALONE"`) `recoveryAction` того же `409` по-прежнему называет `DELETE /v1/infra/servers/:id/lock`, безусловно, — и вызов этот против идущего деплоя снимает пер-серверную сериализацию, на которую деплой опирается. Поэтому исполнять `recoveryAction` не читая `error.hint.recovery` нельзя ни на одном состоянии. Адрес снятия лока остаётся в `error.hint.recovery`, с подставленным идентификатором сервера, требованием ключа-владельца и оговоркой, что звать его следует, только убедившись, что ничего не выполняется.

Тем же контрактом ответило и чтение журнала. [GET /v1/infra/servers/:id/logs](/docs/infra/deploy/logs) у приложения в галактике идёт через тот же общий exec-канал хоста, и при занятом канале отвечал `200` с пустым списком `logs` — то есть утверждал, что у приложения нет вывода. Теперь занятость приходит как `409` с `Retry-After`, а прочие сбои чтения — как `502` с кодом агента.

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

Клиенту, который ветвился по HTTP-статусу и считал `502` на `/exec` терминальным сбоем, надо перестать это делать: занятость теперь приходит как `409` и её следует повторять с интервалом из `Retry-After`. Клиенту, который читал `error.code`, менять нечего. Если ваш сценарий вызывал `POST /v1/infra/servers/:id/unstick` по подсказке платформы — на галактических серверах он не работал и раньше, замените его повтором вызова, а при устойчивом отказе обращайтесь в поддержку. Если вы разводите два состояния `409` на `/exec` в коде — сверяйтесь с наличием `error.hint.autoExpiresInSeconds`, а не с текстом `recoveryAction`.

### BC-0821-9: пакет по одной сущности объясняет, что сделал с select

> Поддержка старого формата до: не предусмотрена

**Было**

`POST /v1/{entity}/batch` с действием `list` применял `select` к ответу, но молчал обо всём,
что при этом отбросил. Имя, которого у сущности нет, просто исчезало: запись приходила
суженной, а объяснения не было нигде — ни ошибки, ни предупреждения. У одиночного списка и у
[глобального пакета](/docs/batch) такое имя перечисляется предупреждением, и только у этой
двери отбор полей был полностью безмолвным.

**Стало**

Подвызов несёт своё `meta.warnings` с кодом `UNKNOWN_SELECT_FIELD` и именем поля — ровно той же
формы, что у одиночного списка. Предупреждение принадлежит СВОЕМУ подвызову: соседние элементы
пакета своё `meta` не получают, и пустым массивом ключ не приезжает — сказать нечего, значит
ключа нет.

Сущности, у которых незнакомое имя отвечает ошибкой (сегодня это события календаря), теперь
отклоняют такой подвызов и здесь — `UNKNOWN_SELECT_FIELD` до вызова Битрикс24. Отказ
per-call, как в глобальном пакете: падает только этот элемент, остальные сорок девять
выполняются. Раньше на этой двери жёсткий отбор не срабатывал вовсе.

Значение `*` по-прежнему означает «вернуть все поля»: отбора нет, и незнакомое имя рядом с ним
ничего не отклоняет. Предупреждение при этом приходит: `select` вида `*,titel` вернёт все поля
и скажет, что второе имя ничего не значит, — ровно как на одиночном списке и в общем пакете.

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

Аддитивная половина ничего не ломает: `meta` — новый необязательный ключ рядом с `data` и
`total`. Ломающая — жёсткий отбор: подвызов, который раньше отвечал суженной записью и кодом
`200`, на событиях календаря теперь отвечает `error` в своём слоте. Окна поддержки нет
намеренно: прежнее поведение и было дефектом — запрошенное имя молча пропадало, и отличить
это от «поля нет в записи» было нечем. Проверьте обработчики, которые считали отсутствие
ошибки признаком того, что все имена из `select` распознаны.

### NEW-0821-10: транскрипт сессии Открытой линии через API

Добавлен эндпоинт [POST /v1/openlines/sessions/history](/docs/openlines/history) — обёртка над методом Битрикс24 `imopenlines.session.history.get`. Возвращает переписку последней сессии чата Открытой линии по его идентификатору: сообщения, участников и метаданные файлов одним ответом. Требует скоуп `imopenlines`.

Вход только по идентификатору чата (`chatId`, принимается и форма `chat2043`). По нему берётся последняя сессия чата. Пагинации у метода нет — транскрипт приходит целиком. Для постраничного чтения сообщений остаётся [GET /v1/chats/:dialogId/messages](/docs/chats/messages/list).

Эндпоинт включается платформой Вайбкод постепенно: пока он не включён, вызов отвечает `403 OPENLINES_HISTORY_DISABLED` — это признак того, что возможность ещё не активирована, а не ошибка интеграции.

### NEW-0821-11: мультипарт-деплой galaxy-приложения хранит архив в хранилище исходников

Multipart-выкладка galaxy-приложения больше не собирает архив целиком в памяти платформы: там,
где включено депонирование multipart, архив уезжает версией в [хранилище исходников](/docs/source-storage)
и попадает на хост по подписанной ссылке. Где хосту разрешено забирать архив самому, распознанный
tar.gz он скачивает сам — со сверкой размера и контрольной суммы, записанных за версией, — и тогда
причина неудачной загрузки приходит отдельным кодом `UPLOAD_DOWNLOAD_FAILED` /
`UPLOAD_EXTRACT_FAILED` / `UPLOAD_NO_SPACE` в хвосте `buildLog`; ZIP и нераспознанный формат едут
прежним путём, через агента. Предел на размер архива в теле запроса при этом не меняется — тело
по-прежнему проходит через платформу. Версия остаётся в депо и тогда, когда деплой провалился:
её видно в списке версий и можно выложить повторно по `source.versionId`.
