Для AI-агентов: markdown этой страницы — /docs-content/changelog/2026-09-20.md индекс документации — /llms.txt

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

← Журнал изменений · Сентябрь 2026

FIX-0920-1: спецификация OpenAPI и справочник API называют обязательные ключи фильтра и считают `count` по `"*"`

Было

Девять сущностей не выполняют чтение без обязательного ключа фильтра: календарь (type, ownerId), разделы календаря, файлы и папки Диска, комментарии таймлайна (entityType, entityId), товары и разделы каталога (iblockId), значения списочных свойств товаров (propertyId) и узлы оргструктуры (type). Без ключа операция отвечает 400 MISSING_REQUIRED_PARAMS или 400 MISSING_REQUIRED_FILTER. Машинная спецификация GET /v1/openapi.json об этом молчала: у списка GET ни одной из девяти сущностей, у поиска POST /search шести из них и у агрегации POST /v1/catalog-products/aggregate фильтр был описан как необязательный. Примеры справочника вели в тот же отказ: поиск печатал пустой filter, список — вызов без параметров. Пример агрегации у двадцати с лишним сущностей считал count по полю, хотя операция принимает count только по "*" и отвечает 400 INVALID_PARAMS, а перечень допустимых полей агрегации в спецификации "*" не содержал. Пакетный вызов сущности POST /v1/{entity}/batch в примере передавал limit рядом с params, где подвызов его не читает.

Стало

Спецификация объявляет обязательные ключи там, где операция их требует. У поиска и агрегации они указаны в описании filter, у поиска файлов и папок ключ можно передать и на верхнем уровне тела. Значение folderId / parentId описано как непустой скаляр. У списка каждый ключ описан отдельным параметром запроса, и его можно передать как ?type=… или как filter[type]=…, как и раньше. Машиночитаемый перечень ключей добавлен расширением x-required-filter-keys, а признак, поднимает ли пакетный вызов обязательные параметры подвызова, — расширением x-batch-list-lifts-params. Каждый ключ назван вместе с кодом отказа. Перечень полей агрегации содержит "*". Подвызов пакетного вызова описан полем params. Примеры справочника передают обязательные ключи, агрегация считает count по "*", пакетный пример кладёт параметры в params. Там, где пакетный list не может получить обязательные параметры, пример показывает get, а у разделов календаря — create. Ответы операций не изменились.

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

Рабочие запросы не затронуты: операции отказывали без этих ключей и раньше. Клиент, сгенерированный по спецификации, после обновления получит обязательный filter у поиска и агрегации этих сущностей. Исключение — поиск файлов и папок: там обязателен ключ, в filter или на верхнем уровне тела.

BC-0920-2: правка и удаление несуществующего поля сотрудника отвечают 404

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

Было

PATCH /v1/userfields/users/:id и DELETE /v1/userfields/users/:id на идентификатор, которого на портале нет, отвечали 422 BITRIX_ERROR с сообщением Access denied. — тем же ответом, что и настоящий отказ по правам. Повторное удаление уже удалённого поля отвечало так же, а GET /v1/userfields/users/:id на тот же идентификатор отвечал 404. Идентификатор с ведущими нулями GET не находил: 007 отвечал 404, хотя правка и удаление того же поля по 007 работали.

Стало

Перед правкой и удалением платформа проверяет, существует ли поле. Поля нет — ответ 404 ENTITY_NOT_FOUND, и запрос на изменение на портал не уходит. Тот же код приходит на повторное удаление и на идентификатор поля, которое не является полем сотрудника. Отказ по правам остаётся прежним: владельцу ключа, не являющемуся администратором портала, приходит 422 BITRIX_ERROR с сообщением Access denied.. Заодно GET /v1/userfields/users/:id теперь находит поле по идентификатору с ведущими нулями: 007 читается как 7, как это давно работает у правки и удаления.

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

Ветку «поля нет» переведите с 422 BITRIX_ERROR на 404 ENTITY_NOT_FOUND — так же, как она уже написана для полей CRM. Повторный DELETE, получивший 404, означает «поле уже удалено», а не ошибку. Ветку 422 BITRIX_ERROR с сообщением Access denied. сохраните: теперь она означает нехватку прав либо гонку, когда поле удалили между проверкой и записью. Правка и удаление стали на один запрос к порталу дольше.

FIX-0920-3: два одновременных создания сервера приложению дают одну машину

Было

Два запроса POST /v1/infra/servers, пришедшие почти одновременно ключом одного приложения, у которого сервера ещё нет, выполнялись оба: каждый видел приложение без сервера, и каждый создавал платную машину. К карточке приложения привязывалась одна из них, вторая оставалась работать и тарифицироваться, но не показывалась ни в карточке, ни в переиспользовании — найти её можно было только в списке GET /v1/infra/servers, а удалять приходилось руками.

Стало

Речь об обычном создании — запросе БЕЗ placement: "dedicated". Место приложения под сервер занимается на платформе Вайбкод неделимо, в тот же момент, когда запись сервера создаётся в базе, и до обращения в облако. Это касается и приложений, вызывающих API ключом авторизации: раньше их карточка получала сервер только при выкладке, а до неё каждое следующее создание заводило ещё одну машину — теперь сервер встаёт в карточку сразу, и повторный вызов возвращает его же. Поэтому вторая машина больше не рождается: запрос, опоздавший к свободному месту, останавливается раньше, чем что-либо оплачено, и в ответ получает 201 с сервером, который создал первый запрос, и reused: true, так что повторять запрос не нужно. Если такой опоздавший вызов нёс исходники, судьбу их называет сам ответ, и она зависит от того, есть ли что портить. Когда на возвращаемом сервере прямо сейчас идёт выкладка первого запроса — и всегда на обычном сервере, куда создание исходники не кладёт в принципе, — вторая выкладка поверх чужой не запускается: ответ несёт sourceIgnored: true, следите за состоянием сервера, а свои исходники выложите отдельным вызовом, если они отличались. Когда портить нечего (сервер приложения ещё ни разу не выложен), исходники опоздавшего вызова выкладываются обычным порядком и ответ несёт deploying: true. Смотрите на эти поля, а не предполагайте исход заранее. Если сойтись на этот сервер не удалось, ответ будет 409 с новым кодом APPLICATION_SERVER_SLOT_TAKEN, и машина по нему всё равно не создаётся. Такой ответ означает, что пригодного к выдаче сервера у приложения сейчас нет: его карточку успели удалить либо стоящая на ней машина отдана быть не может — она мертва или это общий хост. Повтор запроса в обоих случаях заведёт НОВЫЙ сервер, а не вернёт прежний, поэтому перед повтором стоит прочитать GET /v1/applications.

Защита работает и тогда, когда карточки приложения ещё нет: первый вызов заводит сервер, второй получает 409 с кодом APPLICATION_FIRST_SERVER_IN_PROGRESS, и повтор возвращает уже существующий сервер, как только карточка появилась. Коды отказа тут намеренно разные, потому что и делать по ним надо разное: после APPLICATION_FIRST_SERVER_IN_PROGRESS повтор вернёт сервер, а после APPLICATION_SERVER_SLOT_TAKEN — заведёт новый. Различайте их по коду: текст сообщения для этого не предназначен. Границ у защиты две, обе намеренные. Первая — запрос с placement: "dedicated": им дополнительную машину просят осознанно, и отказывать в ней нельзя. Вторая — аккаунт, где раздел «Приложения» выключен: карточек там не заводят вовсе, обе машины остаются видны в списке серверов, и ни одна не теряется. На этих двух путях собственную защиту от повторных вызовов снимать рано. Успешные ответы одиночных созданий не изменились.

FIX-0920-5: статус сервера перестаёт показывать «подключён» при мёртвом туннеле

Было

Когда соединение сервера с платформой обрывалось незаметно, статус сервера мог навсегда остаться CONNECTED: карточка сервера показывала «Работает», хотя вызовы внешнего API приложения уже не доходили. Вызывающий получал 503 APP_API_UNAVAILABLE, но состояние само не исправлялось — сервер оставался в этом виде до случайного переподключения, и по статусу отличить «приложение сейчас молчит» от «соединения нет вовсе» было нельзя.

Стало

Получив отказ из-за оборванного соединения, платформа сверяется с фактическим списком живых соединений и, если соединения действительно нет, снимает неверный CONNECTED — статус сервера становится DISCONNECTED, и сервер подхватывает штатное восстановление. Сверка идёт только на этой ветке отказа и только при подтверждённом отсутствии соединения: пока список живых соединений недоступен или соединение в нём есть, статус не меняется. Ответ вызывающему при этом прежний — 503 APP_API_UNAVAILABLE, новых кодов ошибок нет.

FIX-0920-6: схема OpenAPI объявляет отказ по скоупу на читающих операциях сущностей

Было

Читающие операции сущностей — список, чтение по идентификатору, fields, search, aggregate, чтения связей и позиций товаров — не объявляли в схеме OpenAPI ответ 403, хотя API Вайбкод отвечает 403 с кодом SCOPE_DENIED, когда у ключа нет скоупа, который требует операция. Требование скоупа при этом публиковалось: расширение x-required-scope и фраза «Requires scope» в описании операции. Клиент, сгенерированный по схеме, не имел модели ошибки для самого обычного отказа и получал её как непредусмотренный ответ сервера.

Стало

Каждая читающая операция сущности объявляет 403 с кодом SCOPE_DENIED и перечисляет коды, которые приходят на том же статусе от общего гейта ключа и от самого Битрикс24. У fields кодов Битрикс24 в этом перечислении нет намеренно, и причин тут две. Там, где у сущности есть живой метод набора полей, запрос выполняется по мере возможности, а его отказ приходит успешным ответом с предупреждением fields_partial. Там, где такого метода нет, набор полей отдаётся по объявленному контракту и обращения в Битрикс24 не происходит вовсе — это сказано в описании самой операции.

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

Делать ничего не нужно: поведение ручек не менялось, коды и статусы прежние. Перегенерируйте клиент по схеме, чтобы получить типизированную ветку обработки отказа по скоупу.

BC-0920-7: пакетные записи и импорт больше не отвечают успехом, когда Битрикс24 не применил стадию или ручную сумму

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

Было

Одиночные PATCH /v1/deals/{id}, POST /v1/deals/{id}/move (и те же у лидов) уже отвечали 422 STAGE_NOT_APPLIED, а POST / PATCH /v1/items/{entityTypeId} — 422 AMOUNT_NOT_APPLIED, когда Битрикс24 принял запись, но не применил стадию, воронку или явно запрошенный ручной режим суммы. Те же записи через POST /v1/{entity}/batch, общий POST /v1/batch и POST /v1/{entity}/import отвечали success: true по каждому элементу: сделка оставалась на прежней стадии (при импорте — ложилась на стадию по умолчанию), сумма элемента смарт-процесса становилась нулём, а клиент видел успех.

Стало

В POST /v1/{entity}/batch элемент, чью стадию, воронку или ручную сумму Битрикс24 не применил, приходит с success: false, кодом STAGE_NOT_APPLIED либо AMOUNT_NOT_APPLIED в error, пояснением в message и details.unappliedFields / details.currentValues; такой элемент сохраняет id — запись уже создана или изменена, повторять её не нужно. В общем POST /v1/batch такой подвызов переходит из data.results в data.errors под своим id, с теми же code / message / details, а запись, которую Битрикс24 вернул в ответе на подвызов, приходит в data.errors.<id>.data. В POST /v1/{entity}/import строка результата получает те же поля и тоже сохраняет id. Для обеих пакетных дверей лишних обращений к Битрикс24 не появилось: сверяется запись, которую Битрикс24 и так возвращает в ответе на каждый подвызов. Импорт после всех пачек один раз перечитывает созданные записи страницами по пятьдесят, и только те, у которых была стадия, воронка или явный ручной режим суммы, — остальные импорты стоят ровно столько, сколько раньше; импорт не запускает правила автоматизации, поэтому перечитанная запись — итог самого импорта. Одиночное создание POST /v1/deals и POST /v1/leads стадию не сверяет и отвечает как прежде: там автоматизация портала работает, и робот «при создании — сменить стадию» выдавал бы ложный отказ. Лид, который портал в простом режиме CRM сам перевёл в «сконвертирован» при создании, за неприменённую стадию не считается; при изменении уже закрытого лида неприменённая стадия отказывается, как и в одиночном PATCH. Записи с валидными значениями отвечают как прежде; сумма без явного isManualOpportunity: true по-прежнему не проверяется.

FIX-0920-8: рассуждение прошлого хода снова доходит до модели

Было

В диалоге с инструментами рассуждение прошлого хода ассистента, возвращённое в messages[].reasoning_content, до модели не доходило. Модель теряла нить между вызовами инструментов, а текст рассуждения не попадал во входные токены.

Стало

Платформа передаёт рассуждение под тем именем, которое шаблон модели читает, и в диалоге с инструментами модель снова видит свои прошлые рассуждения. Поле запроса не изменилось: возвращайте рассуждение в messages[].reasoning_content, как описано в вызове функций. Текст рассуждения теперь учитывается во входных токенах, поэтому usage.prompt_tokens у таких запросов выше.

Затронутые эндпоинты: POST /v1/chat/completions.