# Изменения API: 21 сентября 2026

[← Журнал изменений](/docs/changelog) · [Сентябрь 2026](/docs/changelog/2026-09)

### FIX-0921-1: пример пакетного чтения почтовых ящиков показывает подвызов

**Было**

Карточка справочника для `POST /v1/mail/mailboxes/batch` печатала тело `{"action":"list"}` — без массива `calls`. Скопированный как есть, такой вызов не выполнял ни одного подвызова и возвращал пустой результат, который читался как «у портала нет ящиков», хотя ящики были. Параметры подвызова эта дверь принимает только внутри `calls[].params`, и передать их было некуда. В машинной спецификации подвызов был описан как произвольный объект, а признак того, поднимает ли дверь обязательные параметры подвызова, не публиковался вовсе.

**Стало**

Пример показывает рабочую форму: `{"action":"list","calls":[{"params":{"limit":5}}]}`. Спецификация описывает элемент `calls` полем `params` и публикует расширение `x-batch-list-lifts-params`, как у соседних пакетных дверей. Ответы операции и её поведение не изменились: адрес, тело запроса, коды ответов и требуемое право прежние. В спецификации операция переехала из группы `mail` в группу `mail-mailboxes` — к ней же относятся остальные операции почтовых ящиков. Если вы генерируете клиента по `GET /v1/openapi.json`, при следующей генерации изменится имя класса, в который попадает этот метод; сам вызов менять не нужно.

### FIX-0921-2: генерация изображений учитывает output_format и negative_prompt

**Было**

[POST /v1/images/generations](/docs/ai/images) принимал `output_format` и `negative_prompt`, но сервис генерации их не получал. Запрос с `output_format: "jpeg"` возвращал картинку в PNG, и поле `output_format` в ответе тоже приходило равным `png`. Запрос с `negative_prompt` давал ту же картинку, что и без него.

**Стало**

Оба поля доходят до сервиса генерации. `output_format: "jpeg"` возвращает JPEG, `"webp"` — WebP, и поле `output_format` в ответе называет фактический формат. Без этого поля формат по-прежнему PNG. `negative_prompt` влияет на результат.

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

Менять ничего не нужно. Если вы присылали `output_format: "jpeg"` и рассчитывали на PNG в ответе, читайте формат из поля `output_format` ответа, а не считайте его постоянным.

### FIX-0921-3: `/exec` больше не портит многобайтные символы в выводе

**Было**

В ответе `POST /v1/infra/servers/:id/exec`, а также в потоковом режиме `?stream=true`, многобайтный символ UTF-8, попавший на границу внутреннего буфера чтения, приходил как `�`: например `—` мог прийти как `—��`, а следующий фрагмент — начаться с `�`. Однобайтный вывод это не затрагивало.

**Стало**

Вывод собирается по границам символов: неполный символ на стыке фрагментов удерживается и дописывается следующим фрагментом, поэтому `stdout` и `stderr` доходят без порчи и в потоковом, и в JSON-режиме. Ответ остаётся HTTP 200, `exitCode` не меняется.

### FIX-0921-4: внешний API приложений переживает короткое переподключение туннеля

**Было**

Вызов `ANY /v1/applications/:id/api/*`, который попадал в короткое окно переподключения туннеля приложения, мог сразу получить `503 APP_API_UNAVAILABLE` с `Retry-After`, даже если приложение становилось доступно через несколько секунд.

**Стало**

В таком окне платформа Вайбкод ненадолго удерживает вызов и, если туннель успевает вернуться в общий бюджет запроса, доставляет его в приложение. Если туннель не вернулся вовремя, ответ остаётся `503 APP_API_UNAVAILABLE` с `Retry-After`.

### FIX-0921-5: схема 403 на пишущих операциях допускает все опубликованные коды

**Было**

Пишущие операции V1, которые документируют отказ `WRITE_BLOCKED_READONLY_KEY`, тем же описанием называли соседние коды 403: `SCOPE_DENIED`, отказы общего гейта ключа и отказы Битрикс24. При этом схема тела закрывала `error.code` одним значением `WRITE_BLOCKED_READONLY_KEY`, поэтому SDK, собранный по OpenAPI, считал остальные коды невозможными.

**Стало**

Схема этого ответа допускает стандартный V1-конверт `{ success: false, error: { code, message } }` рядом со строгой веткой `WRITE_BLOCKED_READONLY_KEY`. Типизированные `details` для READONLY-отказа сохранены, а остальные опубликованные коды 403 больше не запрещены схемой.

### FIX-0921-6: polling бота с остановленной подпиской возвращает пустую очередь

**Было**

[GET /v1/bots/{botId}/events](/docs/bots/events/polling) для бота, остановленного из-за неактивной подписки Маркетплейса Битрикс24, возвращал `403 B24_MARKET_SUBSCRIPTION_REQUIRED`. Клиенты, которые не учитывали этот отказ, могли продолжать частый polling.

**Стало**

Для такого бота [GET /v1/bots/{botId}/events](/docs/bots/events/polling) возвращает успешный ответ с пустым списком событий, `hasMore:false`, сохранённым offset и `nextPollAfterMs:60000` — опрашивайте с этим темпом. На запрос с `withUserEvents=true` поле темпа не приходит, как и в остальных режимах. Остальные операции с остановленным ботом продолжают получать `403 B24_MARKET_SUBSCRIPTION_REQUIRED` до восстановления подписки.

### BC-0921-7: Поле, которого нет в описании сущности, проверяется на записи по типу из Битрикс24

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

**Было**

Проверка формы значения на записи охватывала только поля, объявленные в описании сущности. Поле, которое сущность отдаёт при чтении, но не объявляет (`utmSource` и `locationId` у сделок, `birthdate`, `honorific` и `categoryId` у контактов, `employees` у компаний, `statusDescription` у лидов), и пользовательские поля `UF_*` она не видела: `{"locationId": {"a": 1}}` на [POST /v1/deals](/docs/entities/deals/create) отвечал `201`, а в карточке сохранялась строка `Array`; `{"categoryId": "abc"}` у контакта сохранялся как `0`, а `{"categoryId": {"a": 1}}` — как `1`, и контакт переезжал в другую категорию; объект в `birthdate` сохранялся пустым. Так вели себя все семь дверей записи — одиночные `POST` и `PATCH`, [пакетная запись](/docs/batch) по одной сущности и общая, импорт.

**Стало**

Такое поле проверяется по тому, что о нём сообщает сам Битрикс24 в своём списке полей: объект или массив в поле строкового, числового, логического или датного типа портала и нечисловая строка в числовом отклоняются `400` с кодом `INVALID_PARAMS` до записи в Битрикс24, а сообщение называет поле и тип, которым портал его объявил. Пустая строка в поле-ссылке (сотрудник, контакт, компания, лид, сделка) и в пользовательском числовом поле не отклоняется — так Битрикс24 снимает привязку и очищает значение; в категории она отклоняется, потому что переносит запись в категорию 0. Поля, которые портал объявляет многозначными или только для чтения, неизменяемые после создания поля на обновлении, поля типов вне перечисленных (файл, перечисление, деньги), имена, которых в списке портала нет, и сущности без признака многозначности в списке полей (задачи) не проверяются — как прежде. Список полей запрашивается только когда в теле есть такое имя, и держится в памяти пять минут; если Битрикс24 его не отдал, запись проходит как раньше, и ближайшие тридцать секунд список перед записью не запрашивается повторно. Границы — в разделе [INVALID_PARAMS](/docs/errors/request#invalid_params-400).

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

Проверьте места, где в поля из списка `GET /v1/<entity>/fields`, отсутствующие в описании сущности, значения собираются структурой или строкой из внешней системы: раньше такой запрос отвечал успехом, а данные терялись или искажались, теперь он честно вернёт ошибку с указанием поля. Значения верного типа проходят без изменений.

### BC-0921-8: схемы контакта и сделки достроены: двенадцать полей ответа объявлены только для чтения, тринадцать служебных и пустых ключей убраны

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

**Было**

[GET /v1/contacts/{id}](/docs/entities/contacts/get) (а также список, поиск, `include` и перечитанная запись в ответах на создание и изменение) отдавал восемь ключей, которых не было ни в схеме, ни в справочнике, ни в машинном описании OpenAPI: `phoneWork`, `phoneMobile`, `phoneMailing`, `imol`, `address`, `shortName`, `login` и `entityTypeId`. [GET /v1/deals/{id}](/docs/entities/deals/get) — семнадцать: `isWon`, `isLose`, `isWork`, `hasProducts`, `receivedAmount`, `lostAmount`, `begindateShort`, `closedateShort`, `dateCreateShort`, `dateModifyShort`, `eventDateShort`, `eventId`, `eventDate`, `eventDescription`, `orderStage`, `productId` и `entityTypeId`. Явный `select` любого из них отвечал `400 UNKNOWN_SELECT_FIELD`, сортировка у контактов — `400 UNKNOWN_SORT_FIELD`, а у сделок `?sort=` по любому из них уходил в Битрикс24 как есть (`200`, по `entityTypeId` — сырой `422 BITRIX_ERROR`). Значение, присланное в любой из этих ключей при создании, изменении, импорте или в пакете, Битрикс24 не хранил ни на одной двери, но платформа отвечала успехом (`201`/`200`): создание и изменение — с предупреждением `UNRECOGNIZED_WRITE_FIELD`, импорт — молча. Незаполненное значение любого из шести полей контакта (телефоны, контакт открытой линии, адрес, краткое имя) приходило пустой строкой, а не `null`, как у описанных полей.

**Стало**

У контакта объявлены шесть полей, все только для чтения: `phoneWork`, `phoneMobile`, `phoneMailing` (телефоны из мультиполя `phone` по типам), `imol` (контакт открытой линии), `address` (старая колонка адреса — заполняет только legacy `crm.contact.add`, адрес правится в реквизитах) и `shortName` (фамилия с инициалами, вычисляет Битрикс24). У сделки — шесть полей, которые Битрикс24 вычисляет, все только для чтения: `isWon`, `isLose`, `isWork` (флаги по смысловой категории стадии), `hasProducts` (есть ли товарные позиции), `receivedAmount` и `lostAmount` (сумма сделки в учётной валюте портала — пересчитанная по курсу, если валюта сделки другая, — пока она выиграна или проиграна, иначе `0`). Все двенадцать описаны на [странице полей контакта](/docs/entities/contacts/fields) и [странице полей сделки](/docs/entities/deals/fields), в `GET /v1/contacts/fields` и `GET /v1/deals/fields`, в справочнике и в машинном описании; типы по живому замеру. Явный `select` этих полей работает; фильтр и сортировка по `phoneWork`, `phoneMobile`, `phoneMailing`, `address`, `shortName` и по `receivedAmount`/`lostAmount` работают (по `imol` — принимаются), `receivedAmount`/`lostAmount` принимаются и в `sum`/`avg`/`min`/`max` `POST /v1/deals/aggregate`; четыре флага сделки в `filter` не принимаются (`400 UNKNOWN_FILTER_FIELD`) — булев фильтр уходит в Битрикс24 как `Y`/`N`, а портал сравнивает эти флаги только с булевым значением: по трём флагам стадии строка `Y` даёт обратный набор (фильтруйте по `stageSemanticId`), по `hasProducts` не совпадает ни с одной записью (замены в фильтре нет); сортировка по флагам работает. Шесть вычисляемых полей сделки и `shortName` контакта Битрикс24 считает только для списка или поиска с явным `select` без `*`; одиночный `GET`, список без `select` и действие `list` пакета сущности отдают по ним `null`. Значение, присланное в любое из двенадцати полей, — которое Битрикс24 и раньше не сохранял — теперь отклоняется: создание и изменение — `400 READONLY_FIELD`, импорт — `400 IMPORT_ITEM_VALIDATION`, пакетный запрос сущности — `400 BATCH_ITEM_VALIDATION`, общий пакетный запрос — `READONLY_FIELD` под вызовом в `data.errors`. Незаполненные значения шести полей контакта приходят как `null`, как остальные пустые строки записи. Тринадцать ключей из ответов убраны: у контакта `entityTypeId` (константа типа, 3) и `login` (пустой на любом портале, ни одна дверь его не пишет); у сделки `entityTypeId` (константа 2), пять коротких дат `begindateShort`, `closedateShort`, `dateCreateShort`, `dateModifyShort`, `eventDateShort` (те же даты второй раз) и пять пустых старых граф `eventId`, `eventDate`, `eventDescription`, `orderStage`, `productId` (пусты на любом портале, не заполняются ни через API, ни через legacy-методы). Убранные ключи не принимаются в `select`, `filter` и сортировке — `?sort=`, `?order[]`, `order` тела поиска (`400 UNKNOWN_SELECT_FIELD` / `400 UNKNOWN_FILTER_FIELD` / `400 UNKNOWN_SORT_FIELD`) — у сделок раньше `?sort=` по ним уходил в Битрикс24 насквозь. Исключение прежнее: `params.order` подвызова `POST /v1/batch` у контактов и сделок по-прежнему уходит на портал как есть.

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

Если вы отправляли любое из двенадцати полей при создании, изменении, импорте или в пакете — уберите его из тела: Битрикс24 не хранил его и раньше, телефоны записываются через `phone`, адрес — в реквизитах, флаги и суммы считает портал. Если вы читали `entityTypeId`, `login`, короткие даты или `eventId`/`eventDate`/`eventDescription`/`orderStage`/`productId` — эти ключи больше не приходят: тип сущности задан самим эндпоинтом, даты берите из `begindate`/`closedAt`/`createdAt`/`updatedAt`, остальные всегда были пустыми. Если вы проверяли `phoneWork`, `phoneMobile`, `phoneMailing`, `imol`, `address` или `shortName` на пустую строку — проверяйте на `null`. Если вам нужны `isWon`/`isLose`/`isWork`/`hasProducts`/`receivedAmount`/`lostAmount` или `shortName` — запрашивайте их явным `select` (без `*`) на списке или поиске.

**Затронутые эндпоинты:** [GET /v1/contacts/{id}](/docs/entities/contacts/get), [GET /v1/contacts](/docs/entities/contacts/list), [POST /v1/contacts/search](/docs/entities/contacts/search), [GET /v1/contacts/fields](/docs/entities/contacts/fields), [POST /v1/contacts](/docs/entities/contacts/create), [PATCH /v1/contacts/{id}](/docs/entities/contacts/update), [GET /v1/deals/{id}](/docs/entities/deals/get), [GET /v1/deals](/docs/entities/deals/list), [POST /v1/deals/search](/docs/entities/deals/search), [GET /v1/deals/fields](/docs/entities/deals/fields), [POST /v1/deals](/docs/entities/deals/create), [PATCH /v1/deals/{id}](/docs/entities/deals/update), [POST /v1/deals/{id}/move](/docs/entities/deals/move), [POST /v1/{entity}/import](/docs/import), [POST /v1/{entity}/batch](/docs/batch), [POST /v1/batch](/docs/batch).

### NEW-0921-11: удаление сервера сообщает, чем кончился запрос на удаление машины в облаке

В ответе [DELETE /v1/infra/servers/{id}](/docs/infra/servers/delete) появилось поле `cloudDelete`: `confirmed` — облако приняло запрос на удаление машины, `already-gone` — машины уже не было, `failed` — облако запрос не приняло, `not-applicable` — у сервера нет машины в облаке, например у galaxy-приложения. `success` по-прежнему `true` во всех четырёх случаях: запись сервера удалена, начисления остановлены. Раньше ответ этого не показывал: при `failed` запись сервера всё равно удалялась, а машина могла продолжать работать — по ответу это было незаметно. При `failed` не считайте ресурс уничтоженным.

### NEW-0921-12: код DISK_FULL в provisionErrorCode сервера после неудачной починки

Если починка сервера (`POST /v1/infra/servers/{id}/repair`) не удалась из-за нехватки места на
диске, сервер в `GET /v1/infra/servers` и `GET /v1/infra/servers/{id}` получает новое значение
`provisionErrorCode: "DISK_FULL"` и понятный текст в `provisionError`: повтор не поможет, сначала
нужно освободить место. Успешная починка снимает `DISK_FULL`. Уже стоящие вердикты
`AGENT_NEVER_CONNECTED` и `GUEST_NOT_BOOTING` значение `DISK_FULL` не заменяет. Статус сервера не
меняется, прежние запросы работают как раньше.

### NEW-0921-13: у дел появился include: responsible, author, editor

Дело теперь объявляет три связи на сотрудников, и их можно затребовать параметром `include` у [GET /v1/activities](/docs/entities/activities/list), [GET /v1/activities/{id}](/docs/entities/activities/get) и [POST /v1/activities/search](/docs/entities/activities/search): `responsible` — ответственный (по полю `responsibleId`), `author` — автор (по `authorId`), `editor` — последний редактор (по `editorId`). Раньше связей у сущности не было объявлено ни одной, поэтому любое имя отвергалось с `400 INVALID_INCLUDE`, а список допустимых имён в тексте отказа был пустым.

Запрос вида `GET /v1/activities/{id}?include=responsible,author` отвечает `200` и кладёт карточки сотрудников в `_included`. Запрос без `include` работает как прежде. Связь читает карточку сотрудника, поэтому ключ обязан нести право `user` — иначе запрос отвечает `403 SCOPE_DENIED`. Доступные имена публикует [GET /v1/activities/fields](/docs/entities/activities/fields) и спецификация OpenAPI.

Имя вне этого списка отвергается по-прежнему, но отказ теперь называет доступные связи: `Unknown include 'owner'. Available: responsible, author, editor`. Родитель дела (`ownerTypeId` + `ownerId`) и `communications` связями не объявлены намеренно: целевая сущность там меняется от записи к записи, и по одному числовому идентификатору невозможно выбрать, чью карточку читать; обе величины и так приезжают полями самой записи.

### FIX-0921-14: структурированный ответ проверяется на разбор, а не только на обрыв

**Было**

При `response_format` типа `json_object` или `json_schema` платформа проверяла только то, что модель не оборвала генерацию и что ответ не пуст. Непустой ответ, который не разбирается как JSON-документ — текст вокруг объекта, ответ в markdown-ограждении кода, одиночное значение, — приходил как обычный ответ HTTP 200, и `JSON.parse` падал уже в интеграции.

**Стало**

Платформа разбирает ответ перед выдачей. Успешный ответ `200` не изменился: валидный JSON-объект или массив приходит как прежде, байт в байт. Если ответ непустой, но не разбирается, приходит отказ `422` с кодом `structured_output_invalid_json`, полем `finishReason` и подсказкой `hint`; полей `param` и `suggestedMaxTokens` в нём нет — больший бюджет токенов результата не меняет. В потоковом режиме отказ приходит событием `{"error":{"code":"structured_output_invalid_json"}}` перед `data: [DONE]`, уже переданные фрагменты следует отбросить. Отказ `structured_output_truncated` остаётся за обрывом и пустым ответом. Вызов, дошедший до модели, тарифицируется так же, как и прежде.
