Для 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:

JSON
{
  "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 Вайбкод, а не заводить исключения по разделам и по языку.