Для AI-агентов: markdown этой страницы — /docs-content/changelog/2026-09-07.md индекс документации — /llms.txt
Изменения API: 7 сентября 2026
← Журнал изменений · Сентябрь 2026
FIX-0907-1: погашение купона на своём портале закрыто новым кодом отказа
Было
POST /v1/cowork/coupon/redeem смотрел только на купон, кампанию и место. Кто именно гасит код
и чей это портал, ручку не интересовало: сотрудник платформы, имеющий право выдавать купоны, мог
выписать код и активировать его на портале, за которым стоит он сам, — тариф уезжал за счёт
платформы, а в ответе было обычное HTTP 200.
Стало
Погашение отбивается кодом COUPON_SELF_PORTAL (HTTP 409), если за порталом стоит сотрудник
платформы с правом выдавать купоны — сам гасящий либо любой участник портала, владелец
API-ключа или владелец сервера на нём. Успешное погашение при этом не меняется: на портале без
таких людей ответ остаётся HTTP 200 с тем же телом, и трогать интеграцию не нужно. Попытку в
счётчике подбора кодов отказ не расходует — причина в составе портала, а не в том, что человек
не угадал код. Код добавлен в таблицу ответов ручки в документации.
NEW-0907-2: самоописание отвечает, остановлена ли инфраструктура за неуплату
Ответы GET /v1/me и GET /v1/cowork/state
несут новый блок infraState: frozen — остановлены ли за неуплату серверы, деплой и
хранилище, reason — код причины (DEBT либо null), topupUrl — адрес пополнения
кабинета либо null.
Блок отвечает на вопрос, который до сих пор было неоткуда прочитать: что именно остановлено у портала с долгом. Строки внутри блока машинные, текст для человека пишет клиент.
⚠️ Смысл поля зависит от того, включена ли на портале развязка «долг за инфраструктуру
гасит только инфраструктуру» (FIX в этом же выпуске). Пока не включена, отрицательный
баланс отбивает вызовы V1 широко, включая GET /v1/cowork/state; читается в этом режиме
только GET /v1/me — он от заморозки освобождён. infraState.frozen там означает
«остановлено практически всё». После включения поле означает
ровно то, что написано: инфраструктура остановлена, а вызовы в пределах месячной квоты
тарифа продолжают работать.
Блок добавлен, существующие поля не изменились.
FIX-0907-3: исправлена пагинация списков действий и роботов бизнес-процессов
Было
Запрос с offset=50 возвращал те же коды, что и запрос с offset=0. Ответ оставался успешным с HTTP 200.
Стало
offset пропускает указанное число кодов в полном списке. meta.total показывает размер полного списка, а meta.hasMore — остались ли коды после текущей страницы. То же поведение действует для list-подвызовов этих сущностей в глобальном пакетном запросе, включая оба канала total. Ответ по-прежнему возвращает HTTP 200, требования к OAuth-авторизации не изменились.
Влияние на интеграторов
Действия не требуются. Интеграции, использующие offset, теперь получают запрошенную страницу вместо повторной первой страницы; формат ответа и требования к авторизации не изменились.
Затронутые эндпоинты: GET /v1/bizproc-activities, GET /v1/bizproc-robots, POST /v1/batch.
FIX-0907-4: долг за инфраструктуру больше не выключает то, что оплачено отдельно
Было
Отрицательный баланс кошелька отбивал 402 ACCOUNT_FROZEN почти на всех вызовах V1 —
освобождены были только самоописание, справочник и разговор с поддержкой, — включая
и те, что кошелька не касаются: обращения к своему Битрикс24 через прокси, состояние
подписки Коворка, список моделей. Долг за виртуалку выключал ИИ, выданный тарифом
Битрикс24.
Стало
Заморозка счёта применяется к тому, что платится кошельком: серверы, хранилище, деплой, поиск и research за деньги платформы, расход сверх месячной квоты, выписка ключей развёртывания и создание приложения. Вызов по месячной квоте тарифа, оплаченный период подписки Коворка и REST-прокси к своему порталу проходят при любом состоянии кошелька.
⚠️ Новое поведение включается по порталам постепенно. Пока оно на портале не включено, ответы прежние.
FIX-0907-5: завышенный числовой CRM ID отклоняется до обращения к Битрикс24
Было
Числовые ID CRM проверялись только на форму: строка из цифр без ведущих нулей проходила гард целиком. Это затрагивало обычный :id записи в пути (/v1/deals/{id}, /v1/contacts/{id} и аналогичные сущности), а также положительные entityTypeId smart-processes и соседних CRM-операций. ID длиннее безопасного диапазона целых уезжал в Битрикс24, где мог округлиться до другого значения, а часть методов crm.item отвечала на него сырой ошибкой PHP вместо внятного отказа.
Стало
Такие ID отклоняются с ошибкой невалидных параметров до вызова Битрикс24. Проверка действует для обычных record ID в path и batch, а также для entityTypeId smart-processes, dynamic-параметров и связанных CRM-операций. Граница проходит по безопасному целому: 9007199254740991 по-прежнему принимается, 9007199254740992 и всё, что длиннее, отклоняется. ID 0 остаётся допустимым там, где он был разрешён раньше (например, для основной воронки сделок).
FIX-0907-6: конвертация лида учитывает выбранную воронку сделки
Было
POST /v1/leads/{id}/convert игнорировал параметр categoryId, поэтому новая сделка попадала в воронку по умолчанию.
Стало
Если передать categoryId, новая сделка сразу создаётся в указанной воронке. Значение 0 по-прежнему выбирает воронку по умолчанию.
Целочисленная неотрицательная строка остаётся допустимой для совместимости и приводится к числу. Остальные формы значения по-прежнему игнорируются и оставляют сделку в воронке по умолчанию; null и отсутствие параметра равнозначны.
NEW-0907-7: номер сотрудника Битрикс24 у автора приложения в ответах /v1/apps
Ответы семейства приложений теперь несут два новых поля: authorBitrixUserId —
числовой идентификатор сотрудника Битрикс24, создавшего приложение, и
authorBitrixUserIdSource — признак того, откуда этот идентификатор взят.
authorBitrixUserId — тот же идентификатор, что приходит в поле id ответа
GET /v1/users, поэтому он служит ключом связи между
двумя ответами: раньше у приложения был только authorId — идентификатор
пользователя платформы Вайбкод, — и сопоставить создателя с карточкой сотрудника
было нечем.
Значения authorBitrixUserIdSource: member — идентификатор взят из
подтверждённого участия автора в этом аккаунте Битрикс24, на такой идентификатор
ссылку ставить можно; snapshot — идентификатор взят из значения, снятого при
создании приложения, это оценка по возможности, и она может указывать на другого
сотрудника, чем текущий автор приложения; null — идентификатор неизвестен,
authorBitrixUserId тогда тоже null. Реестру, который не должен ошибаться,
ссылку на карточку сотрудника стоит ставить только при member. На аккаунтах в
коробке и на аккаунтах с микросервисными учётными данными значение snapshot не
выдаётся вовсе: там происхождение снятого значения не связано с подтверждением
личности. Подтверждённое участие на таких аккаунтах работает как обычно, поэтому
идентификатор там либо member, либо пуст.
Имя и фамилию автора ответы приложений НЕ отдают: это решение принято сознательно
и сужает исходную заявку, которая просила ещё и имя. Персональные данные не
уезжают в ответ, который читает любой ключ аккаунта, включая ключ, вшитый в код
развёрнутого приложения; имя запрашивается по authorBitrixUserId из
GET /v1/users, где доступ к нему ограничен
разрешением user.
Существующие запросы продолжают работать без изменений: поля добавлены, ни одно не убрано и не переименовано.
Затронутые эндпоинты: GET /v1/apps, GET /v1/apps/:id, POST /v1/apps, PATCH /v1/apps/:id, POST /v1/apps/:id/publish, POST /v1/apps/:id/unpublish, POST /v1/apps/:id/relink-oauth
BC-0907-8: Поле companies удалено из API контактов
Поддержка старого формата до: не предусмотрена
Было
Поле companies публиковалось в GET /v1/contacts/fields и принималось в select для операций чтения контактов. Платформа Битрикс24 не возвращала значение этого поля, поэтому успешный ответ мог не содержать ключ companies.
Стало
Поле companies больше не публикуется и всегда удаляется из ответов контактов. Попытка запросить его в select отклоняется с 400 UNKNOWN_SELECT_FIELD.
Что делать интеграторам
Не запрашивать companies. Для идентификаторов связанных компаний использовать поле companyIds.
Затронутые эндпоинты: GET /v1/contacts, GET /v1/contacts/:id, POST /v1/contacts/search, POST /v1/contacts/aggregate, POST /v1/contacts/batch, GET /v1/contacts/fields.
FIX-0907-9: составная загрузка заменяет крупный объект под прежним ключом
Было
POST /v1/storage/objects/multipart/create возвращал 409 STORAGE_KEY_EXISTS, если живой объект приложения уже занимал тот же key. Поэтому содержимое размером больше 10 МБ нельзя было заменить без смены ключа. Для личного ключа совет использовать multipart вёл к тому же отказу.
Стало
Для уже загруженного объекта приложения запрос возвращает 200 и открывает сессию замены с прежним objectId. Пока POST /v1/storage/objects/multipart/complete не опубликовал новые байты, чтение отдаёт старое содержимое; успешный ответ подтверждает новую версию. Подтверждённый POST /v1/storage/objects/multipart/abort до публикации сохраняет старую версию. Если complete вернул 502 STORAGE_BUCKET_ERROR, публикация могла состояться: по этому ответу нельзя определить текущую версию, сессия остаётся активной и блокирует удаление объекта до разрешения администратором платформы. Видимость объекта не меняется.
POST /v1/storage/objects/multipart/create для уже существующего живого объекта личного ключа возвращает 409 STORAGE_KEY_EXISTS и не открывает сессию загрузки. Сначала удалите объект, затем снова начните составную загрузку под тем же key. Это неатомарная операция: между удалением и успешным завершением новой загрузки объект недоступен.
Влияние на интеграторов
Файлы приложения больше 10 МБ можно обновлять составной загрузкой под прежним key. Операции multipart create, complete и abort могут вернуть повторяемый 409 STORAGE_KEY_CONFLICT, когда другой запрос одновременно изменяет тот же key или состояние сессии изменилось. Для create и complete повторяйте ту же операцию. 409 на abort во время финализации требует повторить complete с теми же parts; если complete вернул 502 STORAGE_BUCKET_ERROR, не делайте вывод о текущей версии и обратитесь к администратору платформы. Если abort вернул 502 STORAGE_BUCKET_ERROR, повторяйте abort, пока освобождение сессии не будет подтверждено. DELETE /v1/storage/objects/{key} во время активной multipart-сессии возвращает 409 STORAGE_MULTIPART_IN_PROGRESS: сначала завершите или отмените сессию, учитывая особый случай финализации выше.
FIX-0907-10: перевод сделки/лида на несуществующую стадию теперь отвечает ошибкой, а не тихим успехом
Было
POST /v1/deals/{id}/move, POST /v1/leads/{id}/move, PATCH /v1/deals/{id} и PATCH /v1/leads/{id} отвечали 200 success:true и возвращали полный объект сделки/лида даже тогда, когда переданная stageId (или categoryId у сделки) не существовала в справочнике — Битрикс24 тихо игнорировал значение, стадия оставалась прежней, а вызывающий не мог отличить реальный перевод от отклонённого без ручной сверки поля data.stageId.
Стало
Ответ по-прежнему 200, если переданная стадия или воронка существует и применяется — поведение для валидных значений не изменилось. Когда переданное значение не применилось (Битрикс24 принял вызов, но фактическая стадия осталась прежней), ответ — 422 STAGE_NOT_APPLIED с деталями в error.message: какое значение запросили и какое оказалось на самом деле после перечитывания записи.
FIX-0907-11: Подсказка привязки встройки говорит «trial», а не «demo»
Было
GET /v1/me отдавал в подсказке возможности apps.bindPlacements старый термин:
«…403 B24_MARKET_SUBSCRIPTION_REQUIRED (or B24_MARKET_TRIAL_USED if the demo was
already used)». Запись FIX-0904-7 уже перевела латинскую терминологию на слово trial,
поэтому эта строка осталась последней, расходившейся с кодом отказа, который она сама
и описывает. Тот же термин держался в двух статьях документации — про запуск
пробного периода Маркетплейса и про имя тарифа в GET /v1/me.
Стало
Подсказка читается как «…if the trial was already used», документация переведена на тот же термин. Коды отказов, статусы и поля ответа не изменились, успешный ответ остался успешным. Строка не локализована и отдаётся одинаково на обеих установках.
Влияние на интеграторов
На логику — никакого. Клиенту, который сравнивает эту подсказку по подстроке, стоит перепроверить сравнение.
FIX-0907-12: спека объявляет все ответы переключения режима сервера
Было
Публичная спека GET /v1/openapi.json объявляла у PATCH /v1/infra/servers/:id/mode только ответы 200, 400, 403 и 404. Клиент, собранный по спеке, считал остальные исходы невозможными, хотя ручка их отдавала: отказ по исчерпанному балансу, отказ роли сервера, конфликт параллельного запроса и два отказа применения сетевой политики.
Стало
Спека дополнена ответами, которые ручка отдаёт: 401, 402 ACCOUNT_FROZEN и OPEN_MODE_REQUIRES_COMMERCIAL, 409 SERVER_NOT_RUNNING, AGENT_NOT_CONNECTED, MODE_SWITCH_SEALED_ROLE и CONFLICT, 502 IPTABLES_FAILED, PROVIDER_NOT_CONFIGURED, GATEWAY_UNREACHABLE и TUNNEL_NOT_FOUND, 503 SECURITY_GROUP_ATTACH_FAILED и отказ, код которого начинается с GATEWAY_TIMEOUT. Описание 400 дополнено кодами SAME_MODE, NO_SUBDOMAIN и MODE_SWITCH_STANDALONE_ONLY. Те же коды заведены в таблицу ошибок страницы: до этой правки она не называла ни одного из трёх отказов состояния — сервер не запущен, агент не на связи, у сервера нет субдомена. Из таблицы ошибок на странице ручки снят код PROVIDER_ERROR: переключение режима его не отдаёт. Статус 429 объявления не получил намеренно — своего лимитера у ручки нет, а общий пограничный лимит платформы описан на странице лимитов и в спеке по маршрутам не объявляется. Поведение ручки не изменилось: успешный ответ остаётся HTTP 200 с тем же телом, менять запросы не нужно, и правка касается только объявления и документации.
NEW-0907-13: `/v1/cowork/me` и `/v1/cowork/state` называют портал, к которому привязан ключ
Обе ручки самоописания Коворк/Код получили новое поле portal:
{
"portal": { "id": "8f3c…", "domain": "acme.bitrix24.ru" },
"tier": "PRO",
"state": "ACTIVE"
}
Поле отвечает на вопрос, о каком портале говорят остальные поля тела. Сиденье
Коворк/Код определено на паре «владелец ключа + портал», а ключ привязан к
одному порталу навсегда, поэтому у человека с несколькими порталами приложение
докладывает про тариф и квоту одного портала, а браузер — другого. Раньше домен
приезжал ровно один раз, при обмене кода на ключ (POST /v1/connect/token), и
переспросить его было негде.
portal.id присутствует всегда — он взят с самого ключа. portal.domain равен
null только в вырожденном случае, когда строки портала уже нет. Остальные поля
обоих ответов не изменились, и запросы, написанные до появления поля, работают
без правок.
BC-0907-14: частичный отказ конвертации лида отвечает 422, а не 200
Поддержка старого формата до: не предусмотрена
Было
POST /v1/leads/:id/convert создаёт сделку, контакт и компанию отдельными записями одна за другой. Когда часть из них не создавалась, ответ всё равно приходил со статусом 200: поле success было false, а error, code и details лежали на верхнем уровне тела, причём error был строкой, а не объектом. Клиент, который ветвится по HTTP-статусу или читает error.code, принимал частично выполненную запись в CRM за успех.
Стало
Частичный отказ отвечает 422 с кодом CONVERSION_FAILED в обычном конверте ошибок: error — объект с полями code и message. Исход каждой запрошенной операции переехал в error.details — по ключу на сделку, контакт и компанию, с признаком success и либо идентификатором созданной записи, либо текстом отказа Битрикс24. Верхнеуровневых error-строки и code в ответе больше нет. Лид, который не удалось прочитать, отвечает 404 с кодом ENTITY_NOT_FOUND в том же конверте. Успешная конвертация по-прежнему отвечает 200 с полем data.
Что делать
Ветвиться по HTTP-статусу и по error.code, а не по полю success и не по верхнеуровневому code; исходы отдельных записей читать из error.details, а не из details. Уже созданные записи по-прежнему не откатываются, а статус лида не меняется, поэтому перед повторным вызовом сверяйте, что уже появилось в CRM: повтор создаёт второй комплект записей. Окна поддержки прежнего ответа со статусом 200 нет: он сообщал об успехе там, где часть записей в CRM не создалась, поэтому сохранять его означало бы и дальше выдавать отказ за успех.
FIX-0907-15: Проверка корневого типа тела на создании, обновлении, batch и поиске сущностей
Было
Тело запроса — JSON-литерал null — роняло POST /v1/{entity}, PATCH /v1/{entity}/{id} и POST /v1/{entity}/batch в 500 INTERNAL_ERROR; этот статус не был объявлен ни для одного из трёх маршрутов. JSON-массив или JSON-строка в теле POST /v1/{entity} создавали настоящую запись с пустыми или автосгенерированными полями и отвечали 201, как будто запрос был корректным. На PATCH /v1/{entity}/{id} JSON-строка проходила проверку пустого тела и отвечала 200, фактически ничего не изменив. На POST /v1/{entity}/search тело-массив отвечало 400 INVALID_FILTER_SHAPE с сообщением, неверно называющим присланный тип данных («получена function» вместо «массив»); тело-скаляр (число, строка, boolean) проходило как «фильтр не указан» и отвечало 200 с неотфильтрованным списком записей.
Стало
Корректное тело (JSON-объект; для /search — включая пустой {}, это законный запрос «без фильтра») обрабатывается без изменений. Ответ по-прежнему 201 при создании и по-прежнему 200 при обновлении, batch и поиске — ничего из этого не поменялось. Тело, чей корневой JSON-тип не является объектом (null, массив, строка, число, boolean), теперь на любом из четырёх маршрутов отвечает 400, а сама сущность не создаётся и не изменяется: EMPTY_CREATE_BODY на создании, EMPTY_UPDATE_BODY на обновлении, INVALID_BATCH_ACTION на пакетном маршруте сущности, INVALID_REQUEST на поиске. Сообщение об ошибке поиска теперь называет фактически присланный тип данных, а не внутренний артефакт реализации.
FIX-0907-16: отсутствующая запись отвечает 404 на складах, каталогах, статусах заказа, позициях корзины и смарт-процессах
Было
Один и тот же сценарий — «записи с таким id нет» — отвечал в пределах одного раздела разными
кодами. GET /v1/order-statuses/{id} и GET /v1/basket-items/{id} отдавали 422 BITRIX_ERROR,
хотя соседние заказы, счета, платежи и предложения на том же шаге отдавали 404. Так же вели себя
GET /v1/catalogs/{id}, GET /v1/warehouses/{id} и GET карточек свойств товара, при том что
товары и цены каталога уже отвечали 404. Проверка «удалилось ли» по коду ответа на этих путях
молча не срабатывала.
Отдельно на портале с нерусским языком интерфейса PATCH и DELETE для
/v1/smart-processes/{id} на несуществующий entityTypeId отвечали 400 INTERNAL_ERROR — код,
который для DELETE даже не объявлен в спецификации. На русском портале то же действие честно
отвечало 404 SMART_PROCESS_NOT_FOUND, а GET был верен на любом языке.
Стало
Отсутствующая запись отвечает 404 на всех перечисленных путях. GET, PATCH и DELETE
для складов, каталогов, статусов заказа и позиций корзины отдают 404 ENTITY_NOT_FOUND; текст
портала в поле message не меняется, меняется только классификация ответа. Тот же 404 приходит
и на страницах свойств товара и их значений.
PATCH, DELETE и POST /v1/smart-processes/batch отвечают 404 SMART_PROCESS_NOT_FOUND
независимо от языка портала — так же, как это давно делал GET. Клиент может опираться на один
код существования записи во всём API Вайбкод, а не заводить исключения по разделам и по языку.