Для AI-агентов: markdown этой страницы — /docs-content/changelog.md индекс документации — /llms.txt
Журнал изменений API Вайбкод
История изменений API Вайбкод: новые возможности, исправления и изменения с потерей обратной совместимости. Записи расположены от новых к старым.
Префиксы записей
- NEW — новая возможность: новый эндпоинт, новое необязательное поле или параметр, новый код ошибки в новом сценарии. Прежние запросы клиентов продолжают работать.
- FIX — исправление поведения. Ответ меняется на корректный, действий со стороны клиента не требуется.
- BC — изменение с потерей обратной совместимости. Требует действий со стороны клиента. Старый формат поддерживается указанный срок, затем прекращается.
Формат кода записи: {ТИП}-{ММДД}-{N}, где ММДД — дата публикации, N — сквозной номер в рамках даты.
Часть записей NEW помечена в процессе раскатки: метод вышел в конкретном обновлении Битрикс24 и доступен не на всех порталах. Пока обновление не приехало на портал, вызов возвращает 422 METHOD_NOT_YET_AVAILABLE с целевой версией — это признак раскатки, а не ошибка интеграции.
2026-08-11
NEW-0811-1: тарифы серверов называют единицу цены
В ответе GET /v1/infra/providers/{providerId}/plans у каждого тарифа появилось поле currency со значением "Vibes" — единица, в которой считаются priceMonthly и sleepPriceMonthly. Раньше цены приходили безымянными числами, и единицу приходилось брать из текста документации; теперь это та же машинная пометка, что у стоимости поиска в GET /v1/me.
Поле аддитивное: прежние вызовы работают без изменений, числа и их смысл не поменялись. Каталожная цена по-прежнему может отличаться от фактического списания за конкретный портал.
NEW-0811-2: каталог данных приложения объявляется в теле деплоя
Приложение на отдельной виртуальной машине запускается под непривилегированной учётной записью, и платформа передавала ей во владение только каталог распаковки. Каталог состояния вне него — тот самый /opt/data, который наша документация советует для данных, переживающих выкат, — создаётся администраторскими шагами install и preStart, поэтому оставался за администратором, и первая же запись из приложения падала с ошибкой доступа. Обойти это можно было только ручной раздачей прав в preStart на каждом деплое.
Было
Приложение писало только в свой каталог распаковки. Для каталога состояния декларативного способа не было.
Стало
В теле POST /v1/infra/servers/{id}/deploy появились два необязательных поля. dataDirs — до восьми каталогов вне каталога распаковки, которые платформа создаёт и передаёт учётной записи приложения на каждом деплое, а при откате возвращает обратно. Путь — абсолютный, нормализованный, внутри /opt, /srv или /var/lib; сами корни отвергаются новым кодом INVALID_DATA_DIRS до того, как деплой займёт сервер. dataDirsRecursive дополнительно передаёт содержимое объявленных каталогов — нужно только для заранее развёрнутого дерева; для /opt/data не принимается, потому что там по нашему же рецепту восстановления базы лежит файл с паролем.
Передаётся сам каталог, а не его содержимое: файлы, положенные администратором раньше, владельца не меняют. Владелец каталога может удалять и заменять файлы внутри него, поэтому скрипты, запускаемые от администратора, и учётные данные держите в необъявленном каталоге.
Прежние вызовы работают как раньше: без этих полей ни один каталог не создаётся и владельца не меняет.
NEW-0811-3: выпуск ключа ровно с выбранными платформенными правами
Тело POST /v1/keys принимает необязательное поле exactScopes. С exactScopes: true ключ сохраняет ровно те права, что перечислены в scopes: четыре платформенных (vibe:infra, vibe:ai, vibe:search, vibe:storage) не дописываются при выпуске, и vibe:ai / vibe:search не добавляются к правам запроса на лету. Поэтому GET /v1/me возвращает ровно сохранённый набор, а ключ без vibe:infra отвечает 403 INFRA_SCOPE_REQUIRED на POST /v1/infra/servers.
Умолчание не изменилось: без поля к запрошенным правам по-прежнему добавляются четыре платформенных, поэтому уже написанные скрипты работают как раньше.
Ключи, выпущенные в кабинете, теперь тоже точные — снятая галочка платформенного права означает, что права у ключа нет. Раньше набор Вайбкод дописывался безусловно, и сузить права можно было только правкой ключа после выпуска.
NEW-0811-4: выгодные часы видны в подписке Cowork/Code и в отказе по исчерпанной квоте
В отдельные часы недели квота расходуется медленнее — тот же вызов забирает меньшую долю лимита. Раньше об этом можно было узнать только из расписания выгодных часов, теперь то же самое видно в ответах подписки Cowork/Code и в отказе по исчерпанной квоте, поэтому приложение может предложить перенести объёмную задачу на выгодный час, не собирая расписание само.
| Ответ | Что появилось |
|---|---|
| GET /v1/off-peak | currentWindowEndsInHours — через сколько часов расход перестанет быть таким же выгодным. null, если в пределах недели вперёд дороже не станет |
| GET /v1/cowork/me | Блок offPeak — действует ли скидка сейчас, какой множитель расхода и когда наступит ближайший выгодный час. Без сетки часов |
| GET /v1/cowork/state | Тот же блок offPeak вместе с сеткой часов на неделю и поле touSavedPct — какую долю расхода за текущий расчётный период сняли выгодные часы |
Отказ 402 cowork_quota_exhausted на POST /v1/chat/completions |
offPeakHint — через сколько часов снимется блокировка (inHours) и какой множитель расхода будет действовать в этот момент (multiplier). Приходит, только когда этот момент попадает в час со скидкой |
Все перечисленные ключи необязательные и приходят, когда возможность включена для аккаунта. Включается она по очереди, и пока она не включена, ключей в теле ответа нет вовсе — со значением null они не приходят. Проверяйте наличие ключа, а не его значение.
Множитель — коэффициент расхода, а не размер скидки. Значение 0.5 означает, что вызов забирает половину той доли квоты, которую забрал бы без скидки.
Поля блока, примеры ответов и особенности — Выгодные часы в подписке Cowork/Code.
FIX-0811-5: ближайший выгодный час отсчитывается от границы часа, а не от минуты запроса
Было
Поле nextWindow.inHours в ответе GET /v1/off-peak отсчитывалось от момента запроса. Расписание почасовое, поэтому ответ «через 2 часа», полученный в 10:55, указывал на 12:55 — на середину выгодного часа, начавшегося в 12:00. Клиент, прибавивший это число к моменту запроса, попадал в дешёвый час, когда большая его часть была уже позади. Так же вёл себя тот же отсчёт в карточке выгодных часов в кабинете (GET /api/ai/tou).
Стало
Отсчёт идёт от границы текущего часа в часовом поясе расписания. То же «через 2 часа», полученное в любую минуту между 10:00 и 11:00, указывает на 12:00 — на начало выгодного часа. Поле стало тем, чем его описывает документация, — ближайшим часом, который дешевле текущего. Расхождение с прежним прочтением не превышает одного часа. То же исправление действует в карточке выгодных часов в кабинете.
Влияние на интеграторов
Менять ничего не нужно, форма ответа и тип поля прежние. Планировщик, который прибавляет inHours к моменту запроса, теперь стартует в начале выгодного часа, а не в его середине, и момент старта может сдвинуться не больше чем на час.
2026-08-10
NEW-0810-1: выдача ссылки-приглашения отмечается в журнале доступа портала
Ответ POST /v1/infra/servers/:id/access-tokens не изменился — меняется то, что происходит на стороне портала.
Было
Выдача ссылки с mode=share-url не оставляла следа в надзорном слое портала: администратор не видел, кто и когда открыл приложение ссылкой.
Стало
После успешной выдачи ссылки платформа записывает смену открытости в журнал портала, а если ссылка не требует входа в Битрикс24 (identityBound=false) и приложение до этого не было открыто наружу — отправляет администраторам портала сообщение в чат-бот со ссылкой на список открытых приложений.
Влияние на интеграторов
Формат запроса, ответа и коды ошибок не изменились — менять клиент не нужно. Учитывайте, что каждая выдача открытой ссылки теперь видима администратору портала.
BC-0810-2: ошибка фильтра в POST /v1/{entity}/batch стала ошибкой подвызова, а не всего запроса
Поддержка старого формата до: 04.02.2027
Было
Один неверный ключ фильтра в любом подвызове POST /v1/{entity}/batch отменял
весь запрос: ответ 400, код в error.code, результаты остальных подвызовов
отбрасывались. Глобальный POST /v1/batch вёл себя иначе — клал ошибку рядом с
конкретным подвызовом и выполнял остальные.
Стало
Обе поверхности ведут себя одинаково. Ответ — 200, код отказа лежит в data[i].error.code
для того подвызова, который его вызвал; остальные подвызовы выполняются и возвращают данные.
Сам набор отказов и их коды не изменились — изменился только радиус: UNKNOWN_FILTER_FIELD,
INVALID_FILTER_OPERATOR, INVALID_FILTER_FIELD, UNSUPPORTED_FILTER.
Прежний текст ошибки начинался с Call at index N: — теперь позиция подвызова видна по его
месту в массиве data, и префикс убран.
Что делать интеграторам
Если код читает отказ фильтра как HTTP 400 с error.code, добавьте разбор 200 с
data[i].error.code — иначе подвызов, у которого фильтр не принят, будет прочитан как
успешный. Проверяйте наличие error у каждого элемента data, как это уже делается для
глобального POST /v1/batch.
BC-0810-3: чтение через батч больше не обходит выключенную операцию сущности
Поддержка старого формата до: 04.02.2027
Было
Сущность может выключать отдельную операцию — обычно потому, что универсальный
обработчик для неё неверен: настоящий список живёт на собственном маршруте с другим
конвертом, либо метод Битрикс24 не понимает фильтр и отдаёт таблицу целиком. Одиночные
маршруты это учитывали, запись через батч — тоже, а чтение через батч — нет. Поэтому
POST /v1/{entity}/batch и POST /v1/batch с action list,
get, search или fields отвечали 200 там, где та же операция на своём маршруте
отвечает 404
— и отдавали ровно тот результат, ради отказа от которого операцию и выключили.
Отдельно: устаревший GET /v1/{entity}/aggregate вообще не спрашивал, есть ли у сущности
агрегация. У сущности, где все числовые поля — идентификаторы, POST /v1/{entity}/aggregate
отвечает 404, а этот адрес отвечал 200.
Стало
Оба батча отвечают на выключенную операцию 400 с кодом ACTION_NOT_SUPPORTED до вызова
Битрикс24: в per-entity батче это ответ всего запроса, в глобальном — ошибка конкретного
подвызова (остальные подвызовы выполняются). Устаревший GET /v1/{entity}/aggregate
регистрируется по тому же признаку, что и POST-версия, поэтому у сущности без агрегации
теперь 404 на обоих адресах.
Влияние на интеграторов
Затронуты только те сущности и операции, где ответ и раньше был неверным. Проверить стоит
все четыре чтения — list, get, search, fields: у шести сущностей выключен именно
search при работающем list, поэтому пачка, которая раньше проходила целиком, теперь
вернёт ACTION_NOT_SUPPORTED на этом подвызове. Полный набор действий
сущности — в operations.batch ответа GET /v1/guide и в описании OpenAPI; data.batch в
GET /v1/{entity}/fields перечисляет только действия ЗАПИСИ и на вопрос про чтения не отвечает. У сущности, где выключено всё,
маршрут остаётся на месте, но любое действие отвечает ACTION_NOT_SUPPORTED — отказ
называет действие, чего 404 на адресе сказать не может.
BC-0810-4: `filter`, переданный не объектом, отклоняется вместо тихой потери
Поддержка старого формата до: 04.02.2027
Как и в соседней записи про неизвестное имя поля, «старый формат» здесь означает неверный ответ, а не рабочий.
Было
filter — объект условий, но передать вместо него строку, число, булево значение или массив
никто не мешал. Строка и массив уходили в Битрикс24 как есть, число и булево превращались в
пустой отбор — и во всех случаях запрос отвечал 200 со ВСЕЙ коллекцией:
POST /v1/tasks/search { "filter": [{ "responsibleId": 1 }] } → 200, все задачи портала
Стало
Такой запрос отклоняется до вызова Битрикс24 — 400 с кодом INVALID_FILTER_SHAPE; в
сообщении сказано, что именно пришло вместо объекта, и показана правильная форма.
Действует на всех сущностях и на всех поверхностях, где filter приходит в теле: поиск,
агрегация и оба батча.
Что делать интеграторам
Передавайте filter объектом. В строке запроса это отдельная история: там условия пишутся
скобочной формой (?filter[responsibleId]=1), а filter, закодированный в JSON одной
строкой, с этой же поставки не отклоняется, а разбирается и применяется — раньше он молча
терялся и возвращал всю коллекцию.
BC-0810-5: неизвестное имя поля в фильтре отклоняется ещё на пятнадцати сущностях
Поддержка старого формата до: 04.02.2027
До сих пор такой фильтр отвечал
200и всей коллекцией — то есть «старый формат» здесь означает неверный ответ, а не рабочий.
Было
Фильтр по имени, которого у сущности нет, уходил в Битрикс24. Битрикс24 такой ключ не
отклоняет — он молча его выбрасывает и отвечает 200 со ВСЕЙ коллекцией. Опечатка в имени
поля поэтому выглядела как успешный запрос с неправдоподобно большим результатом:
GET /v1/tasks?filter[responsable]=1 → 200, все задачи портала
Так вели себя задачи, пользователи, рабочие группы, реквизиты, банковские реквизиты, шаблоны реквизитов, адреса, сайты, страницы, комментарии таймлайна, шаблоны бизнес-процессов, настройки открытых линий и элементы универсальных списков.
Стало
Такой запрос отклоняется до вызова Битрикс24 — 400 с кодом UNKNOWN_FILTER_FIELD и списком
доступных имён в сообщении — этот список и есть точный ответ на вопрос «по чему можно
фильтровать».
Проверяется только имя: операторы, диапазоны, $in/$nin и логика И работают как раньше.
Кроме объявленных полей принимаются пользовательские поля (UF_*, ufCrm*) и — у сущностей,
где он объявлен, — ключ id.
Отдельно: у действий и
роботов бизнес-процессов метод Битрикс24 не принимает
фильтр ни в каком виде, поэтому там отклоняется любой ключ фильтра — код UNSUPPORTED_FILTER.
Полное описание — Фильтрация и поиск.
Что делать интеграторам
Сверьте имена полей в своих фильтрах со списком из сообщения об ошибке. Запрос, который
раньше «работал», но возвращал больше записей, чем должен был, теперь ответит 400 с точным
указанием, какое имя не найдено — это и есть его исходная ошибка. Остальные фильтры не
затронуты.
FIX-0810-6: нехватка места на общем хосте — отдельный повторяемый отказ деплоя, а не поломка приложения
Было
Когда на общем хосте galaxy-приложения кончалось свободное место, сборка падала, и POST /v1/infra/servers/:id/deploy отвечал 502 GALAXY_APP_BUILD_FAILED — тем же кодом, что и ошибка в исходниках самого приложения. Приложение при этом помечалось сломанным, хотя работать не переставало: отказ происходил на сборке, до подмены контейнера, поэтому предыдущая версия продолжала отвечать на запросы. Отличить нехватку места от настоящей ошибки сборки можно было только по хвосту лога в buildLog, а повтор того же деплоя давал тот же результат.
Стало
Тот же случай возвращает 502 GALAXY_LOW_DISK с признаком retryable: true и структурированным полем error.hint: причина, что сделать и предупреждение не удалять слот. Приложение не помечается сломанным — слот, контейнер и его том с данными целы, а уже работающая версия обслуживает трафик дальше. Повторять деплой имеет смысл после того, как на хосте освободили место: само оно не освобождается, поэтому немедленный повтор упирается в тот же отказ. Новый код перечислен в машинном описании контракта деплоя, которое отдают GET /v1/me и GET /v1/openapi.json.
Влияние на интеграторов
Менять ничего не нужно: успешные деплои не затронуты. Ветку на GALAXY_APP_BUILD_FAILED оставьте — она по-прежнему приходит на настоящих ошибках сборки, а у нехватки места теперь есть отдельный код, по которому видно, что дело не в исходниках. Автоматический повтор бывает только там, где платформа сама пересобирает уже существующее приложение: там попытки идут без участия клиента, пока не истечёт отведённый на ожидание бюджет. При создании приложения сразу с исходниками отказ приходит сразу и не повторяется — решение о повторе остаётся за вами.
FIX-0810-7: Открытие из каталога Битрикс24 предупреждает, что приложение не авторизовано
Было
Приложение, открытое из каталога Битрикс24 пользователем, который ещё не выдал ему доступ, запускалось как обычно, но шлюз не проставлял заголовок X-Vibe-Authorization. Вызовы к API отвечали 401, и приложение показывало текст, который наша же документация предписывала для 401, — «откройте приложение из меню Битрикс24». Совет был тупиковым: пользователь именно так и открыл, а открытие из каталога доступ не выдаёт.
Стало
Такой запуск платформа перехватывает и показывает экран с кнопкой «Авторизовать приложение»; второй путь — один раз открыть приложение через встройку в меню Битрикс24. Там же есть неприметная ссылка «открыть без авторизации» на тот же адрес запуска — приложение, которому сессия не нужна, открывается как прежде. Экран показывается только там, где кнопке есть куда вести: облачный портал, ключ приложения и зарегистрированное OAuth-приложение. Во всех остальных случаях запуск идёт как раньше.
Влияние на интеграторов
Менять код не нужно, и ни один запуск не становится недоступным. Стоит поправить только текст своей ошибки на 401: у неё две причины — истёкшая сессия и не выданный доступ, — поэтому «откройте из меню» как единственная формулировка вводит пользователя в заблуждение. Рекомендация обновлена в разделе Среда выполнения приложения.
NEW-0810-8: список документов CRM появился в машинной схеме OpenAPI
Эндпоинт GET /v1/crm-documents теперь описан в схеме OpenAPI, которую отдаёт /v1/openapi.json. Сам вызов работал и раньше, но клиенты и ИИ-агенты, которые строят интеграцию по машинному описанию, считали его несуществующим. В описании указаны обязательный параметр entityTypeId, необязательные entityId, select, order и start, требуемый доступ crm, форма ответа с массивом документов и блоком meta с полями total, start и next, а также коды отказа MISSING_PARAMS, INVALID_ENTITY_ID, INVALID_START, TOKEN_MISSING и SCOPE_DENIED. Параметр размера страницы эндпоинт не принимает и в описании его нет: за следующей страницей идут со значением meta.next в start. Поведение самого эндпоинта не изменилось.
FIX-0810-9: при входе по прямой ссылке приложение получает имя и токен посетителя
Было
Приложение, открытое по прямой ссылке на свой адрес (а не плиткой из Битрикс24), получало имя посетителя из его учётной записи Вайбкода, а не из карточки сотрудника: участник нескольких Битрикс24 видел имя, под которым он записан в другом из них. Заголовок X-Vibe-Authorization на этом пути не приходил вовсе — приложение не могло обратиться в Битрикс24 от лица посетителя.
Стало
X-Vibe-User-Name и X-Vibe-User-Name-Encoded несут имя из карточки сотрудника того Битрикс24, которому принадлежит приложение — как и при открытии плиткой. Токен доступа X-Vibe-Authorization резолвится по номеру сотрудника, поэтому приходит и здесь.
Влияние на интеграторов
Формат заголовков не изменился, менять клиент не нужно. Если карточку сотрудника прочитать не удалось, имя остаётся прежним, из учётной записи Вайбкода: пустым заголовок не приходит.
FIX-0810-10: пользовательские поля счёта
Было
Пользовательские поля счёта нельзя было прочитать или создать ни одним путём.
GET /v1/userfields/invoices отвечал ошибкой UNKNOWN_ENTITY, а
GET /v1/items/31/userfields — ошибкой доступа от Битрикс24,
потому что счёт адресовался так же, как обычный смарт-процесс.
Стало
Оба пути работают и дают одинаковый результат: шесть операций (список, справочник типов,
чтение, создание, изменение, удаление) над пользовательскими полями счёта. Путь
/v1/userfields/invoices добавлен для тех, кому удобнее обращаться по имени сущности, а не по
числовому идентификатору типа.
Влияние на интеграторов
Менять ничего не нужно. Речь идёт о счетах в текущем виде; счета старого образца через API по-прежнему недоступны.
BC-0810-11: Справочник полей товарных позиций описывает то, что реально приходит в ответах
Поддержка старого формата до: не предусмотрена
Было
GET /v1/deals/{id}/products/fields отдавал набор полей Битрикс24 как есть, и он расходился с ответами самих товарных позиций сразу в трёх местах. Сумма скидки называлась в справочнике discountSum, а в данных и при записи — discount. Внешний код позиции и цена в валюте отчёта приходили в каждой строке, но в справочнике отсутствовали. Владелец строки, тип владельца и склад, наоборот, были в справочнике, но из ответов вырезались.
Хуже того, клиент, сгенерировавший запись по этому справочнику, отправлял discountSum — обёртка это имя не распознавала, отвечала 201 Создано и молча выбрасывала скидку. То же происходило с любым другим неизвестным полем: ответ об успехе, данные не записаны.
Стало
Справочник собирается из тех же таблиц, по которым формируются сами ответы, поэтому разъехаться они больше не могут. Скидка называется discount — как в данных, при записи и в документации. Прежнее имя discountSum при этом никуда не делось: оно осталось устаревшим псевдонимом, по-прежнему приходит в справочнике и по-прежнему принимается при записи, поэтому код, написанный по старому списку полей, продолжает работать — с той разницей, что скидка теперь действительно записывается, а не теряется молча. В самих товарных позициях приходит только discount. Добавлены priceAccount и xmlId, которые Битрикс24 возвращает в строках, но в своём справочнике не описывает. Поля ownerId, ownerType и storeId теперь приходят в ответах списка и одной позиции; storeId равен null, если складской учёт выключен.
Признаки isReadOnly и isRequired описывают контракт этого API, а не контракт Битрикс24: ownerId, ownerType, customized и measureName помечены только для чтения (записать их через обёртку нельзя), а у ownerId и ownerType снят признак обязательности — они берутся из адреса запроса. У id появилось пояснение: как атрибут он только для чтения, но в элементах PUT /products его надо возвращать, иначе строка будет создана заново с новым идентификатором.
Запись с неизвестным именем поля больше не отвечает успехом: POST, PUT и PATCH возвращают 400 INVALID_PARAMS и перечисляют записываемые поля. Поля только для чтения по-прежнему принимаются и игнорируются, поэтому объект, прочитанный через GET, можно отправить обратно без чистки.
Поле id в теле принимается только там, где оно что-то значит, — в элементах PUT /products, где оно велит обновить строку на месте вместо пересоздания. При создании и правке оно отбрасывается: строку там задаёт адрес запроса, а тело с id уже существующей строки — ровно то, что получается, если отправить обратно объект, прочитанный через GET.
Признак taxIncluded при записи снова принимается булевым: true и false уходят в Битрикс24 как Y и N. Раньше булево значение отправлялось как есть, и налог по строке фактически оставался незаданным — то есть строка, прочитанная через GET и отправленная обратно, теряла этот признак. Кто обходил это, посылая "Y" и "N" строками, ничего не заметит: такая форма принимается по-прежнему.
Если Битрикс24 отдал неполные метаданные, справочник всё равно приходит полным, но ответ дополняется предупреждением meta.warnings[].code = "fields_partial" — раньше такой ответ был неотличим от нормального. Текст предупреждения различает два случая: метаданных не пришло вовсе или не описана только часть полей (тогда он их перечисляет).
Что делать интеграторам
Ломается запись с посторонним ключом в теле. Проверьте, что при создании и правке товарной позиции вы не отправляете ничего сверх записываемых полей: своих служебных пометок, остатков внутренней модели, имён Битрикс24 в верхнем регистре (PRICE_ACCOUNT, XML_ID). Раньше такой запрос отвечал 201 Создано и молча выбрасывал значение — теперь он отвечает 400 INVALID_PARAMS и перечисляет, что принимается. Окна поддержки у прежнего поведения нет намеренно: оно и было дефектом, из-за которого заявку завели, а держать его параллельно значит держать тихую потерю данных. Полный список записываемых полей приходит в ответе на отказ и в справочнике полей.
Остальное менять не нужно. discountSum продолжает и приходить, и приниматься — перейти на discount можно без спешки, это имя приходит в самих товарных позициях, тогда как псевдоним живёт только в справочнике. Ветвитесь на isReadOnly или isRequired — сверьтесь с новыми значениями у ownerId, ownerType, customized и measureName. Заменяете строки целиком — сохраняйте id у каждого элемента.
FIX-0810-12: подбор сотрудников по серверу больше не пустует из-за свежего ключа без доступа к Bitrix24
Было
Когда управляющий ключ сервера не давал доступа к Bitrix24, GET /v1/infra/servers/{id}/b24-users
и подбор сотрудников в интерфейсе переходили к личным ключам владельца сервера, но проверяли
только один — самый свежий подходящий. Если у него не было ни вебхука, ни токена установленного
приложения, ответ приходил пустым (data: [] с подсказкой), хотя у владельца был другой активный
ключ с нужными правами. Ключи, которые платформа выпускает сама под задачи без обращений к
Bitrix24, всегда оказываются самыми свежими, поэтому подбор мог не работать постоянно.
Стало
Проверяются все подходящие личные ключи владельца, от свежего к старому, и используется первый, для которого действительно есть доступ к Bitrix24. Ключи без доступа пропускаются, к Bitrix24 по-прежнему уходит один запрос. Требования к ключу не смягчены: как и раньше, годится только активный неистёкший личный ключ владельца сервера в том же портале, не привязанный к приложению.
Заодно исправлена подсказка в поле hint: раньше она называла только неавторизованное приложение
и отозванный ключ, из-за чего активный ключ выглядел отозванным. Теперь третьей причиной названо
отсутствие доступа к Bitrix24 у ключей и подсказано действие — выпустить личный ключ с нужными
правами. Форма ответа не изменилась.
FIX-0810-13: привязка места встраивания называет отказ в правах установки вместо общей ошибки шлюза
Было
Битрикс24 отказывал в установке встройки, и проверить состояние подписки в этот момент не удавалось — POST /v1/placements/bind отвечал 502 BITRIX_UNAVAILABLE и складывал код Битрикс24 в details. Названного кода в ответе не было, а вместе с ним и подсказки, что делать дальше. Отказ приходил и на аккаунт с оформленной подпиской.
Стало
Когда проверку выполнить не удалось, а аккаунт уже числится подписанным в Вайбкод, тот же отказ приходит как 403 B24_EMBEDDING_INSTALL_DENIED с details.remedy равным install-rights. Ссылка на оформление не передаётся: подписка есть, не хватает права ставить локальные приложения. Если действующей подписки нет ни по одному из источников, ответ остаётся 502 BITRIX_UNAVAILABLE.
Влияние на интеграторов
Менять ничего не нужно. Ветка обработки 502 BITRIX_UNAVAILABLE продолжает работать для остальных отказов, а этот сценарий уходит из неё в 403. Повторять запрос в нём бессмысленно — нужно, чтобы ключ разработчика принадлежал пользователю с правом ставить локальные приложения и с доступом к приложению.
BC-0810-14: команды, выкладка и загрузка файлов по id машины-галактики больше не выполняются
Поддержка старого формата до: 07.08.2026
Было
Галактика — это одна машина, на которой в контейнерах живут приложения нескольких ключей одного аккаунта Битрикс24. Список серверов отдаёт как сами приложения (kind=GALAXY_APP), так и несущую их машину (kind=GALAXY), и вызов POST /v1/infra/servers/:id/exec, POST /v1/infra/servers/:id/deploy или POST /v1/infra/servers/:id/upload с id несущей машины выполнялся на ней самой — то есть за пределами контейнера вызывающего.
Стало
Тот же вызов с id машины вида GALAXY отвечает отказом: команды — 403 GALAXY_HOST_EXEC_FORBIDDEN, выкладка и загрузка файлов — 400 GALAXY_HOST_NOT_A_DEPLOY_TARGET. Текст отказа называет замену: работать с приложением по его собственному id (kind=GALAXY_APP). Для машин вида STANDALONE и для приложений галактики ничего не изменилось.
Что делать интеграторам
Возьмите из списка серверов строку своего приложения (kind=GALAXY_APP) и обращайтесь по её id. Если сценарий опирался на доступ к самой машине ради свободного места, эта величина теперь доступна как данные, а не как результат команды.
Окна поддержки прежнего поведения нет: отказ действует с момента выката. Причина в том, что прежнее поведение открывало доступ к машине, на которой стоят контейнеры других ключей, — держать такой доступ полгода ради совместимости нельзя.
NEW-0810-15: занятость диска машины-галактики видна в списке серверов
Строка машины-галактики (kind=GALAXY) в GET /v1/infra/servers и в карточке сервера теперь несёт четыре поля: diskTotalMb и diskFreeMb — размер и свободное место в мебибайтах, diskState — оценка (ok, warning, critical или unknown, если замера ещё не было), diskProbedAt — время замера в ISO-8601.
Обе границы оценки платформа держит с запасом: warning означает «место кончается», critical — что свободного места меньше, чем платформа считает безопасным; обе зажигаются раньше, чем выкладке действительно перестанет хватать места. Замер обновляется сам, пока машина не спит; у спящей показывается последнее известное значение вместе с его временем.
Для машин вида STANDALONE и для приложений галактики (kind=GALAXY_APP) все четыре поля равны null: у первых диск не измеряется, у вторых своего диска нет.
NEW-0810-16: агентская модель bitrix/bitrixgpt-5.6-agent
GET /v1/models отдаёт новую агентскую модель bitrix/bitrixgpt-5.6-agent с контекстом 1 048 576 токенов. Модель поддерживает потоковую выдачу, вызов инструментов (tools) и структурированный ответ по схеме — response_format с type: "json_schema". Публичный идентификатор модели входит в список ai.structuredOutputs.models в ответе GET /v1/me.
Модель добавлена в каталог и ничего не заменяет: прежние вызовы работают без изменений. Она включена в программу квоты, поэтому доступна и по партнёрскому токену Битрикс24.
Затронутые эндпоинты: GET /v1/models, POST /v1/chat/completions, GET /v1/me
NEW-0810-17: bitrix/bitrixgpt-5.5-agent помечена устаревшей
Ответы POST /v1/chat/completions на модели bitrix/bitrixgpt-5.5-agent теперь несут заголовки Deprecation: true, X-Model-Replacement: bitrix/bitrixgpt-5.6-agent и Link со ссылкой на преемника (rel="successor-version").
Модель продолжает работать без ограничений и остаётся в выдаче GET /v1/models. Дата отключения не назначена — заголовок Sunset не отдаётся, и менять в интеграции ничего не требуется. Преемник для новых интеграций — bitrix/bitrixgpt-5.6-agent.
FIX-0810-18: временный сбой транзакции базы больше не отдаёт 500 с внутренним кодом движка
Было
Если транзакция в базе закрывалась или истекала до конца операции, запрос отвечал 500 и клал в тело внутренний код движка — {"error":{"code":"P2028"}}. Кода нет ни на одной странице документации, заголовка Retry-After в ответе не было, и по ответу нельзя было понять, что запрос стоит повторить. Чаще всего это видели на DELETE /v1/apps/{id}: приложение оставалось на месте, а повтор выглядел бессмысленным.
Стало
Тот же класс отказа отвечает 503 с кодом DB_TRANSIENT, полем error.retryAfter и заголовком Retry-After — та же посадка на повтор, что у POOL_EXHAUSTED. Изменение при таком отказе не применяется ни частично, ни полностью, поэтому прямой повтор через несколько секунд безопасен. На маршрутах AI (/v1/ai/*, /v1/chat/*, /v1/audio/*, /v1/models) код приходит в нижнем регистре — db_transient — в конверте, совместимом с OpenAI. Внутренний код движка в теле ответа больше не встречается.
Влияние на интеграторов
Менять ничего не нужно. Если ваш обработчик считал 500 окончательным отказом — теперь этот случай приходит как 503 с указанным сроком и попадает в вашу ветку повторов. Отдельная обработка кода DB_TRANSIENT не требуется: достаточно уважать Retry-After на любом 503.
FIX-0810-19: preserveEnv не теряет .env, если выкладка упала после очистки каталога
Было
При cleanDeploy: true вместе с preserveEnv: true существующий .env читался до очистки
каталога, но записывался обратно только на шаге env — уже после установки рантайма и
зависимостей. Если выкладка обрывалась раньше, POST /v1/infra/servers/:id/deploy
отвечал DEPLOY_FAILED, а каталог оставался без .env совсем. Настройки приложения
приходилось заливать заново.
Стало
Сохранённая копия возвращается на диск при любом обрыве выкладки после очистки — на установке
зависимостей, на рантайме, на загрузке архива, на самой очистке, а также при разрыве связи с
сервером. Восстановление идёт по мере возможности и не добавляет отдельного шага в ответ.
Правило приоритета не изменилось: переданный в этом же запросе env по-прежнему побеждает, а
явный пустой env: {} означает «очистить» и сохранённую копию не возвращает. Успешная выкладка
работает как раньше, включая подстановку часового пояса для расписаний пробуждения. Флаг
по-прежнему только для отдельной виртуальной машины (kind: "STANDALONE").
Влияние на интеграторов
Менять на своей стороне ничего не нужно. Форма ответа DEPLOY_FAILED и набор шагов в
data.steps не изменились. Если вы обходили этот дефект — заливали .env вручную после каждой
неудачной выкладки — обходной путь больше не нужен.
2026-08-09
BC-0809-1: meta.total в списках больше не приходит по умолчанию
Поддержка старого формата до: 09.02.2027
Было
GET /v1/{entity} и POST /v1/{entity}/search присылали meta.total — количество записей под фильтр — если запрос не отказался от подсчёта явно. Отказаться можно было параметром withTotal=false или настройкой totalDefault на ключе, но умолчание платформы означало «считать», поэтому интеграция, которая про подсчёт ничего не знала, получала число всегда.
Стало
Платформенное умолчание сменилось на «не считать»: подсчёт заказывается явно. Попросить его можно тремя способами, они перекрывают друг друга в этом порядке: параметр запроса withTotal=true (у POST /v1/{entity}/search — поле тела "withTotal": true), настройка totalDefault на API-ключе, платформенное умолчание.
Если количество не попросили, наличие meta.total определяется формой вызова:
| Вызов | meta.total |
|---|---|
limit не больше 50, offset 0, страница короче запрошенного |
приходит, точное число — в том числе 0 |
limit не больше 50, страница полная либо offset больше нуля |
не приходит |
limit больше 50 |
приходит |
Короткая страница доказывает количество сама, поэтому точное число приходит бесплатно и подсчёт не заказывается. На вызове с limit больше 50 подсчёт нужен платформе, чтобы спланировать обход, поэтому число приходит как раньше — передавать туда withTotal=false ради экономии незачем: параметр уберёт число, а не стоимость.
Явный withTotal=false убирает ключ на любом из этих вызовов: за этим параметром обещание «поля не будет» остаётся безусловным. Настройка totalDefault на ключе и платформенное умолчание точное число из короткой страницы не запрещают, поэтому два одинаковых запроса от двух разных ключей могут вернуть ответы разной формы.
Всё это относится к вызовам, на которых подсчёт можно пропустить. Там, где пропустить его нельзя, withTotal=false игнорируется и meta.total приходит как раньше. Проверяйте наличие поля в конкретном ответе, а не выводите его из настроек ключа.
Новое умолчание действует и на списочных вызовах внутри POST /v1/batch: там количество приходит в data.totals и meta по идентификатору вызова и отсутствует по тем же правилам.
Остальной ответ не изменился: data — те же записи в том же порядке, meta.hasMore на месте и по-прежнему говорит, есть ли ещё страницы.
Что делать интеграторам
Если код читает meta.total, выберите одно из двух.
Разово и на всю интеграцию — включите на API-ключе настройку totalDefault: страница ключей в кабинете или PATCH /v1/keys/:id с телом {"totalDefault": true} (управляющий ключ vibe_live_). Код менять не нужно.
Точечно — добавьте withTotal=true тем вызовам, которым количество действительно нужно: GET /v1/deals?withTotal=true, в теле поиска "withTotal": true.
Что действует на ваш ключ сейчас, показывает блок totalDefault в GET /v1/me: key — настройка ключа, platform — платформенное умолчание, effective — что получится, если запрос не передаст withTotal.
Отдельно: если meta.total использовался как граница цикла листания, переключитесь на meta.hasMore — так надёжнее в любом случае. А если нужна была именно цифра, спросите её прямо: POST /v1/{entity}/aggregate с функцией count отдаёт количество одним вызовом. Обходить коллекцию постранично ради счётчика не надо — это десятки вызовов вместо одного.
2026-08-07
NEW-0807-1: импорт записей CRM: автор и даты из внешней системы, без запуска автоматизации
Перенести уже существующую базу в CRM теперь можно одним запросом на сущность: POST /v1/leads/import и такие же маршруты у сделок, контактов, компаний, предложений, счетов и элементов смарт-процессов. До ста записей за раз, в теле — массив items, поля записи те же, что у обычного создания.
Импорт отличается от создания тремя вещами, и все три — свойства самой операции в Битрикс24, а не наши параметры. Он проверяет отдельное право «импорт», которое администратор портала выдаёт явно. Он не запускает роботов, триггеры и бизнес-процессы, настроенные на создание элемента. И он принимает служебные поля, которые обычное создание молча игнорирует: кто создал (createdBy), кто изменил (updatedBy), кто перевёл на стадию (movedBy) и соответствующие даты. Проставить их может только администратор портала — обычному пользователю Битрикс24 ответит отказом по такой записи, остальные записи пакета при этом создадутся. Набор доступных служебных полей у объектов разный; точный перечень отдаёт GET /v1/leads/fields — там они помечены importable.
У даты создания есть окно, которое задаёт Битрикс24: не позже текущего момента и не раньше, чем у самой свежей уже существующей записи этого объекта. То есть историю целиком получится перенести в пустую CRM или в такую, где всё старше переносимого; «подложить» записи задним числом в наполненную CRM Битрикс24 не даст. Если история не нужна, даты можно не передавать — автор проставляется и без них.
Ответ приходит со статусом 200 даже когда часть записей не прошла: импорт не транзакционен, поэтому исход читается по каждой записи в results[], а сводка лежит в summary. Всегда проверяйте summary.failed — статус говорит «запрос обработан», а не «всё создано». Повторный импорт создаёт дубли; если повторы возможны, записывайте идентификатор из внешней системы в originatorId и originId.
Маршрут ограничен по темпу на портал, чтобы массовая заливка не блокировала другие интеграции аккаунта. Импортируйте в один поток: внутри запроса порядок гарантирован, между параллельными запросами — нет, а Битрикс24 требует неубывающих дат создания.
Подробности, коды ошибок и примеры — на странице Импорт записей CRM.
FIX-0807-2: версия исходников выкладывается по номеру на своём сервере
Было
Версия, сохранённая через POST /v1/infra/servers/:id/sources, не выкладывалась на том же сервере: POST /v1/infra/servers/:id/deploy с телом { "source": { "versionId": "v1" } } отвечал 400 SOURCE_VERSION_REQUIRES_APP, если ключ-владелец сервера не привязан к приложению — то есть на личном ключе vibe_api_*. Обойти можно было только вручную: скачать архив по ссылке и выложить его формой { "source": { "url": … } }.
Стало
Если сервер принадлежит тому же ключу, которым идёт вызов, версия ищется в контексте этого сервера, и выкладка по versionId работает, включая личный ключ. Не нашлась на сервере, а ключ-владелец привязан к приложению — поиск повторяется в контексте приложения, как раньше. Не нашлась нигде — 404 SOURCE_VERSION_NOT_FOUND; текст ошибки теперь называет сервер и путь сохранения вместо приложения.
Код SOURCE_VERSION_REQUIRES_APP не удалён, а сузился: он приходит только там, где сервер принадлежит НЕ вызывающему ключу — менеджмент-ключ и доступ через карточку приложения. Если вы ветвитесь на этот код, ветку оставьте.
Влияние на интеграторов
Менять ничего не нужно. Вызов, который раньше отбивался, теперь проходит; поле sha256 в ответе сохранено.
Одно исключение, редкое: если на сервере и на его приложении лежат версии с ОДНИМ номером — так бывает, когда версия сохранена до перепривязки сервера на другой ключ, — теперь берётся версия сервера, а не приложения. Проверить, какая именно версия уехала, можно по полю sha256 в ответе.
FIX-0807-3: чтение и правка несуществующего дела отвечают 404, а не 422
Было
GET /v1/activities/{id} и PATCH /v1/activities/{id} с идентификатором, которого
на портале нет, отвечали 422 с кодом BITRIX_ERROR и текстом вида
Bitrix24 API error: 400. Причина внешняя: на этих двух методах Битрикс24 отдаёт
отказ, в котором и код ошибки, и описание пустые, — распознать «записи нет» было
не по чему. При этом DELETE /v1/activities/{id} на тот же идентификатор уже
отвечал 404, потому что там портал текст ошибки присылает. Один и тот же
несуществующий идентификатор давал два разных ответа в зависимости от глагола,
и оба раздела документации — get и
update — обещали 404.
Стало
Оба метода отвечают 404 с кодом ENTITY_NOT_FOUND и сообщением
Activity is not found. — тем же, что и DELETE. Правило привязано к этим двум
методам и срабатывает только когда портал не прислал ни кода, ни текста: отказ с
любым кодом или сообщением (в том числе ошибка валидации при обновлении) остаётся
как был. Список ошибок в документации не менялся — изменился ответ, который теперь
ему соответствует.
FIX-0807-4: запрос без тела доходит до обработчика, а не падает на разборе
Часть HTTP-клиентов (axios, PowerShell Invoke-RestMethod, некоторые обёртки над fetch)
подставляет Content-Type: application/x-www-form-urlencoded в каждый POST, PATCH и DELETE —
даже когда тело не отправляется вовсе. Ещё часть не ставит Content-Type совсем. Обе формы
до этой правки не доходили до обработчика.
Было
POST /v1/deals без тела и без заголовка Content-Type отвечал
500 INTERNAL_ERROR — обработчик падал на пустом теле раньше, чем успевал проверить, что
создавать нечего. Тот же вызов с пустым телом и заголовком
application/x-www-form-urlencoded отвечал 415 Unsupported Media Type ещё до проверки
ключа. POST /v1/chats/events/subscribe — тело которому не
нужно вовсе — отвечал 415 с формовым заголовком и 400 с пустым телом под
application/json.
Стало
Пустое тело принимается независимо от заголовка: POST /v1/deals без тела отвечает
400 EMPTY_CREATE_BODY — тем же ответом, что и POST /v1/deals с телом {};
POST /v1/chats/events/subscribe без тела отрабатывает штатно.
Заголовок Content-Type с пустым телом теперь не мешает нигде на сущностях
(/v1/deals, /v1/contacts, /v1/tasks и остальные генерируемые маршруты, включая
пакетные и агрегирующие), в чатах (/v1/chats/*), в пользовательских полях
(/v1/userfields/*, /v1/items/:entityTypeId/userfields), в базе знаний (/v1/note/*) и в
ключах (/v1/portals, /v1/keys). Отдельный случай «заголовка нет совсем» — это другая
поломка, и она закрыта на сущностях: там запрос без тела и без заголовка теперь получает
обычный ответ проверки полей вместо 500.
Влияние на интеграторов
Менять ничего не нужно: запрос, который работал, работает так же. Непустое тело под
незнакомым Content-Type по-прежнему отклоняется с 415 — тем же кодом, что и раньше; 413
приходит только если тело действительно больше допустимого размера. Тело с битым JSON под
application/json теперь отвечает 400 INVALID_JSON_BODY на всех перечисленных маршрутах
(раньше на чатах приходил код разборщика Fastify).
BC-0807-5: дополнение своего обращения больше не выглядит ответом платформы
Поддержка старого формата до: 07.08.2026
Было
Автором обращения считался конкретный ключ, которым оно создано. Комментарий, отправленный другим ключом того же владельца (или обращение, заведённое из кабинета и дополняемое ключом), уходил в платформенную ветку: записывался как authorType: PLATFORM, переводил обращение в AWAITING_USER и перезаписывал Feedback.resolution своим телом. Тем ключом, которым обращение создано, дополнить решённое обращение было нельзя — POST /v1/feedback/:id/comments отвечал 409 FEEDBACK_CLOSED. Единственным обходом было сменить статус через PATCH /v1/feedback/:id.
Кроме того, Feedback.resolution работал зеркалом последней реплики команды: любой комментарий со скоупом vibe:feedback перезаписывал текст решения, и вернуть прежнее значение было нечем. Лимита темпа у операции комментирования не было вовсе.
Стало
Автором считается владелец ключа. Обращение, созданное другим вашим личным ключом или заведённое из кабинета, для вас своё: комментарий записывается как authorType: USER, поле resolution не переписывается, а переданный status игнорируется. Одно условие: такому ключу нужен скоуп vibe:feedback — без скоупа своим считается только тот ключ, которым обращение и создано. Правило не распространяется на ключи приложений и управляющие ключи — там владелец ключа и тот, кто пишет, разные лица.
Комментарий автора к обращению в статусе RESOLVED возвращает его в работу (NEEDS_REVIEW) и снимает resolvedAt / resolvedBy; текст решения при этом сохраняется. Статусы ARCHIVED и WITHDRAWN остаются закрытыми и по-прежнему отвечают 409 FEEDBACK_CLOSED.
Поле resolution заполняет только комментарий, закрывающий обращение (целевой статус RESOLVED или ARCHIVED). При любом другом статусе поле не меняется, а текст комментария по-прежнему доходит до автора письмом и виден в ленте.
У операции комментирования появился лимит темпа — 20 комментариев в минуту, как у той же операции в кабинете. Счётчик общий на ВЛАДЕЛЬЦА ключа: несколько своих ключей делят один бюджет. Превышение — 429 RATE_LIMITED с заголовком Retry-After.
Что делать интеграторам
Правок требуют пять мест, и найти их в своём коде стоит до обновления.
- Обработчик
409 FEEDBACK_CLOSED. На решённом обращении теперь приходит201, и обращение возвращается в работу. Если по этому коду вы решали «обращение закрыто, дальше не пишем», проверку надо перевести на статус из ответа: закрытыми остались толькоARCHIVEDиWITHDRAWN. - Ветвление по
authorType. Для второго ключа того же владельца значение сменилось сPLATFORMнаUSER. Код, который поPLATFORMрисует реплику как «ответ поддержки», начнёт показывать её как сообщение пользователя — и это верно, но если у вас на этой ветке висела логика, её надо пересмотреть. - Чтение
resolution. Как «последняя реплика команды» поле больше не работает: там лежит вердикт последнего закрытия, а на обращении, которое ни разу не закрывали, поле пустое. За последним ответом команды идите в ленту комментариев — последний элемент сauthorType: PLATFORM. - Закрытие своего обращения комментарием. Если вы закрывали своё обращение через
POST /commentsс полемstatus, этот способ больше не работает: у автораstatusигнорируется молча, ответ приходит201, а статус остаётся прежним. Отзыв обращения — через PATCH /v1/feedback/:id сstatus: WITHDRAWN. - Обработка
429на комментариях. Операция получила лимит темпа, которого у неё не было. Если ваш код шлёт комментарии пачкой или в цикле — добавьте обработку429 RATE_LIMITEDс паузой по заголовкуRetry-After. Бюджет общий на владельца ключа, поэтому выпуском второго ключа его не расширить.
Параллельная поддержка прежнего поведения не предусмотрена: прежнее заполнение resolution и было тем дефектом, ради которого правка сделана, — сохранять было бы нечего.
Затронутые эндпоинты: POST /v1/feedback/:id/comments, GET /v1/feedback/:id, GET /v1/feedback
BC-0807-6: сервер AI-агента больше не принимает выкладку приложения
Поддержка старого формата до: 07.09.2026
Было
POST /v1/infra/servers/{id}/deploy принимал архив на сервер, принадлежащий
AI-агенту. Выкладка проходила, код агента затирался чужим приложением, и агент
переставал отвечать. Той же машиной агент числился рабочим — ни в ответе, ни в
интерфейсе следов не оставалось. По этой же причине сервер агента мог быть
переиспользован под приложение при создании нового сервера с тем же именем и
при повторной привязке приложения.
Стало
Выкладка в такой сервер отбивается 403 с кодом AGENT_SLOT_DEPLOY_FORBIDDEN;
текст ошибки называет рабочую альтернативу — кнопку «Повторить» на карточке
агента или создание отдельного сервера под приложение. Пути повторного
использования сервер агента больше не выбирают: вместо перезаписи создаётся
новый. Управляемые ботов это не касается — они выкладывают свой код через тот
же вызов штатно.
Выписка ключа обслуживания на агента, чей сервер снесён, теперь отвечает 409
с кодом AGENT_SERVER_GONE вместо выдачи ключа, которым некуда идти. Чтение
состояния ключа осталось 200, в теле появилось поле reason со значением
SERVER_GONE.
NEW-0807-7: `GET /v1/cowork/state` сообщает о запланированном понижении тарифа
Было
Переход на более низкий тариф применялся сразу и обнулял оплаченный месяц, поэтому сообщать было не о чем: тариф в ответе менялся в тот же момент.
Стало
Понижение планируется на конец оплаченного периода, и объект subscription получил
поле pendingTier — код тарифа, на который сиденье перейдёт при следующем списании,
либо null. Дата перехода — уже имеющееся поле currentPeriodEnd.
Поле аддитивное: клиенты, которые его не читают, работают как раньше. Отменённая
подписка всегда отдаёт null — отмена сильнее плана, и обещать тариф закрывающемуся
сиденью нельзя.
FIX-0807-8: архив исходников галактического приложения добывает сама машина
Раньше архив на галактический хост доставляло служебное действие агента с жёстким потолком в полторы минуты. Теперь машина скачивает и распаковывает его сама, там же сверяя размер и контрольную сумму с сохранённой версией. Потолок снят, а причина отказа называется честно: протухшая ссылка, обрыв связи, нехватка места и битый архив больше не сливаются в одно сообщение о неудачной распаковке.
Коды отказа и поля ответа прежние; добавился UPLOAD_NO_SPACE для нехватки места на диске. Неизвестный формат архива и ссылки, присланные в теле запроса, идут прежним путём. Затронуто POST /v1/infra/servers/:id/deploy.
Отдельно: extractTo теперь проверяется на стороне платформы, а не только на машине. Набор допустимых путей не изменился ни на POST /v1/infra/servers/:id/deploy, ни на POST /v1/infra/servers/:id/upload, где своей проверки не было вовсе — отвергается то же, что и раньше, но сразу и с внятным кодом INVALID_EXTRACT_TO.
FIX-0807-9: деплой больше не падает на остановке медленного приложения
Было
Повторный POST /v1/infra/servers/:id/deploy поверх работающего приложения, которое не
завершается сразу по SIGTERM, падал на шаге stop_existing через ~45 секунд:
DEPLOY_TIMEOUT, Deploy step timed out: GATEWAY_TIMEOUT: no response within 45s. Новая
версия не выкатывалась. Платформа отводила остановке 15 секунд, а операционная система на
сервере — до 90, поэтому приложение, которому на завершение нужно от 45 до 90 секунд,
роняло обновление гарантированно. Подсказка при этом советовала ремонтировать туннель —
ложный след.
Стало
Шаг stop_existing ждёт остановку столько, сколько ей отведено на сервере (лимит шага —
105 секунд), и больше не прерывает деплой: если остановку не удалось
подтвердить — результат не пришёл ЛИБО сервер ответил, что остановить не смог, — шаг
возвращает warning с честным текстом «исход неизвестен» и деплой идёт дальше.
Раньше второй случай не показывался вообще: шаг отдавал ok, и о том, что остановка
не удалась, узнать было негде. Сюда же попал соседний шаг очистки каталога: он тоже мог отдать
ok, ничего не удалив, если сервер команду не выполнял, — и деплой падал двумя шагами
позже с сообщением, которое причины не называло. Теперь такой случай останавливает деплой
сразу и говорит причину. Когда одновременно занят порт, в
предупреждении остаются оба факта. Ответ по-прежнему success: true, статус шага виден в
data.steps[].
FIX-0807-10: переименование поля шаблона реквизитов проверяется до записи
Было
PATCH /v1/requisite-presets/:presetId/fields/:id с полем fieldName, которого нет
среди доступных, отвечал 200 и {"updated": true}, а несуществующее имя реально
сохранялось в строке шаблона. Метод обновления в Битрикс24, в отличие от метода
добавления, не проверяет имя и принимает любую строку — поэтому строка шаблона
оставалась с именем, за которым нет поля, и переставала отображать данные.
Стало
Если fieldName в теле запроса отличается от имени, которое строка уже несёт, имя
сверяется со списком GET /v1/requisite-presets/:presetId/fields/available
до записи. Имя вне списка — 400 с кодом INVALID_FIELD_NAME, обновление не
выполняется; ни одно поле строки не меняется. Имя, занятое другой строкой того же
шаблона, в списке доступных отсутствует и тоже отклоняется. Переименование в
свободное имя проходит как раньше, а регистр имени приводится к написанию, которое
вернул Битрикс24.
Влияние на интеграторов
Изменились три ответа. Мусорное имя вместо 200 даёт 400: прежний 200 записывал
имя, за которым нет поля, поэтому терялись и остальные поля того же запроса — просто
молча. fieldName, переданный не строкой, тоже даёт 400 INVALID_FIELD_NAME: раньше
такое значение уходило в Битрикс24 и оседало в строке словом Array. Запрос с
fieldName на несуществующую строку отвечает 404 до записи, а не ответом Битрикс24.
Запрос без fieldName работает как раньше. Запрос с тем же именем, что уже стоит в
строке, тоже проходит, но стоит на один вызов к Битрикс24 дороже: чтобы понять, что
имя не меняется, платформа сперва читает строку.
NEW-0807-11: справочник полей учёта времени задач
Добавлен GET /v1/task-time/fields — программный состав полей записи учёта времени. Для каждого из десяти полей отдаются тип, признак «только чтение», подпись и описание. Раньше состав полей был описан только текстом в документации, и клиент не мог получить его вызовом.
Схема одинакова для всех задач, поэтому путь плоский. Вложенный GET /v1/tasks/:taskId/time/fields по-прежнему возвращает 400 WRONG_PATH, но теперь называет в тексте ошибки правильный путь. Запрос требует скоуп task и не обращается к Битрикс24.
Числовые по смыслу поля — id, taskId, userId, seconds, minutes, source — объявлены строками, потому что именно строками они и приходят в ответах. Поле userId помечено createOnly: оно принимается при создании и отклоняется при обновлении.
Затронутые эндпоинты: GET /v1/task-time, GET /v1/task-time/fields.
FIX-0807-12: подписи и описания полей daysBeforeClose, fm и FILES в ответе /fields
Было
Три поля, которые Битрикс24 отдаёт живьём, приходили без пояснения, а два из них — ещё и с неудобной подписью. GET /v1/smart-processes/fields отдавал daysBeforeClose с подписью длиной в предложение вместо краткого названия. GET /v1/leads/fields отдавал fm с технической подписью «FM». GET /v1/timelines/fields отдавал FILES с подписью, но без описания, поэтому формат вложений приходилось искать на странице создания комментария.
Стало
У всех трёх полей есть description. У daysBeforeClose подпись сокращена до краткого названия, а прежний длинный текст перенесён в описание. У fm подпись заменена на понятную человеку, а описание отправляет к плоским полям phone и email, через которые те же данные удобнее читать и писать. У FILES подпись осталась той, которую прислал Битрикс24, — она зависит от языка портала, — а описание называет формат вложений на запись и на чтение.
Влияние на интеграторов
Менять ничего не нужно: тип поля (type) и признак «только для чтения» (readonly) по-прежнему приходят от Битрикс24 и не изменились. Если ваш код показывает пользователю значение label этих полей, текст станет другим — он берётся из ответа, а не хранится у вас.
FIX-0807-13: список обращений понимает фильтр в скобочной форме
Было
GET /v1/feedback?filter[status]=RESOLVED возвращал 200 и весь доступный список: скобочная форма фильтра разбиралась, но не читалась, поэтому и записи, и total приходили без фильтра. То же самое с filter[category]. Работала только плоская форма — ?status=RESOLVED.
Стало
Обе формы работают одинаково. GET /v1/feedback применяет filter[status] и filter[category] с той же проверкой, что и плоские параметры: регистр не важен, неизвестное значение отдаёт 400 INVALID_FILTER_VALUE вместо тихой выдачи всего списка. Если присланы обе формы, побеждает плоская — ответы на запросы, которые работали раньше, не меняются. Значение, которое не является одиночным (filter[status][]=NEW), тоже отклоняется с 400 INVALID_FILTER_VALUE.
FIX-0807-14: обновление заказа больше не теряет сумму, пометку и её причину молча
Было
PATCH /v1/orders/:id принимал price, marked и reasonMarked и отвечал 200, но Битрикс24 эти поля на обновлении не сохраняет. У marked и reasonMarked значение просто пропадало. У price было хуже: сумма пересчитывается из позиций корзины, поэтому запрос с ручной суммой её не менял, а при пустой корзине сохранённая сумма становилась 0 — то есть обновление, посланное с любым другим полем, обнуляло цену заказа. Ответ об этом не сообщал.
Стало
Все три поля отклоняются на обновлении с 400 READONLY_FIELD до обращения к Битрикс24 — на всех трёх поверхностях записи: одиночный PATCH, POST /v1/orders/batch с action: "update" и POST /v1/batch с action: "update". Создание не изменилось: POST /v1/orders и оба батч-создания по-прежнему принимают эти поля и передают их значения. В ответе GET /v1/orders/fields такое поле помечено readonlyOnUpdate: true, чтобы отличать его от readonly (нельзя и при создании) и от createOnly (значение неизменно после создания — про сумму заказа это неверно, её пересчитывает Битрикс24).
Влияние на интеграторов
Параллельная поддержка прежнего поведения не предусмотрена: прежнее поведение состояло в том, что значение молча терялось. Если запрос на обновление посылал эти поля — уберите их из тела, иначе он начнёт отвечать 400. Чаще всего так делают клиенты, читающие заказ целиком и отправляющие объект обратно: из такого тела нужно убрать price, marked и reasonMarked. Чтобы изменить сумму заказа, меняйте позиции корзины.
NEW-0807-15: деплой сообщает, что не применил displayName или description
Поля displayName и description деплой заполняет, но не переименовывает: displayName записывается, только пока он ещё равен техническому идентификатору сервера, description — только пока оно пусто. Раньше присланное значение, конфликтующее с уже заданным, отбрасывалось молча — ответ приходил со статусом 200 и без единого признака, что поле не записано.
Теперь такой ответ несёт дополнительную запись в warnings[]: она называет отброшенные поля, подтверждает, что сам деплой прошёл, и предупреждает, что повтор ничего не изменит. Там же — готовое тело запроса к PATCH /v1/infra/servers/{id} с уже подставленным текущим именем: у этой ручки displayName обязателен, поэтому образец избавляет от случайной перезаписи имени при правке одного описания. Запись добавляется в конец массива, поведение записи в базу не изменилось.
Заодно операция переименования появилась в машинном описании API (GET /v1/openapi.json) — раньше схема утверждала, что переименования в этом API нет.
Затронутые эндпоинты: POST /v1/infra/servers/{id}/deploy, PATCH /v1/infra/servers/{id}
FIX-0807-16: переоткрытие обращения больше не стирает текст резолюции
Было
PATCH /v1/feedback/:id с одним только статусом — например {"status":"REVIEWING"} — при возврате обращения из RESOLVED, WITHDRAWN или ARCHIVED обнулял поле resolution, хотя в теле запроса его не было. Ответ приходил 200, о потере в нём ничего не говорилось. Если текст ответа команды не был продублирован комментарием, восстановить его было нечем.
Стало
Возврат в активный статус обнуляет только resolvedAt и resolvedBy. Поле resolution не меняется, если вы его не передали: у обращения, возвращённого из ARCHIVED, сохраняется и причина архивации. Чтобы заменить текст — передайте resolution в том же запросе, чтобы очистить поле — передайте "resolution": null. Путь с комментарием (POST /v1/feedback/:id/comments) resolution не трогал и раньше — теперь обе поверхности ведут себя одинаково.
BC-0807-17: агрегации дел нужен сужающий фильтр
Поддержка старого формата до: 07.02.2027
Было
POST /v1/activities/aggregate принимал запрос без фильтра. На небольшом аккаунте он отвечал за секунду, на большом — не отвечал никогда: первое же обращение к Битрикс24 (подсчёт всех дел аккаунта) не укладывалось в отведённое на вызов время, и клиент получал 503 BITRIX_TIMEOUT с заголовком Retry-After и подсказкой «чтения можно повторять». Повтор давал тот же результат, потому что причина не была временной. Документация при этом прямо предлагала {} как «самый быстрый запрос».
meta.truncated означало ровно одно: «под фильтр попало больше 5000 записей». Если часть страниц записей до нас не дошла, ответ приходил с truncated: false — то есть числовые агрегации и группы были посчитаны по части записей, а ответ этого не сообщал.
Стало
Агрегация дел требует одного сужения из трёх: пара ownerTypeId + ownerId, либо responsibleId, либо граница по дате на createdAt / updatedAt / deadline. Требование включается платформой отдельно на каждый аккаунт. Пока оно выключено, поведение прежнее; после включения запрос без сужения получает 400 MISSING_REQUIRED_FILTER — в message перечислены допустимые сужения и готовый пример тела, обращения к Битрикс24 не происходит.
Независимо от этого переключателя запрос без сужения, на который Битрикс24 не ответил за отведённое время, теперь возвращает 422 AGGREGATION_LIMIT_EXCEEDED вместо 503: отказ терминальный, заголовка Retry-After нет, в тексте — что сделать вместо повтора. Запрос с сужением по-прежнему получает на таймауте 503 с Retry-After: там повтор — честный совет, потому что причину замедления мы не знаем.
Оба ответа приходят и на устаревший GET /v1/activities/aggregate — правило нельзя обойти, вызвав его.
meta.truncated теперь означает «часть записей до нас не дошла» и в прежнем случае (выборка шире 5000), и в новом (обработано меньше записей, чем всего под фильтр). Во втором случае рядом приходит meta.recordsShortfall — сколько записей не хватило. count и meta.totalRecords при этом остаются полными: неполны только data.groups и числовые агрегации. ⚠️ Эта половина изменения касается агрегации ЛЮБОЙ сущности, а не только дел: раньше в таком ответе приходило truncated: false, то есть неполнота не сообщалась нигде.
Список допустимых сужений и то, включено ли требование на аккаунте прямо сейчас, приходят в data.aggregateFilterRequirement ответа GET /v1/activities/fields: поле anchors — сужения, поле enforcement — enforced либо advisory. Статические сужения без состояния аккаунта есть и в GET /v1/guide.
Что делать интеграторам
Добавить в агрегацию дел одно из сужений — этого достаточно и до, и после включения требования. Если сегодня в коде есть ветка на 503 для этого эндпоинта, добавить ветку на 422 и не повторять запрос по ней. Если код читает meta.truncated, учесть, что теперь он поднимается и при потере записей, и посмотреть на meta.recordsShortfall.
FIX-0807-18: файл в поле CRM больше не упирается в 1 МБ, а код отказа стал понятным
Было
Значение пользовательского поля типа «Файл» едет в теле запроса как base64, а тело было ограничено 1 МБ на всех методах. Практический потолок одного файла — около 750 КБ: PATCH /v1/deals/{id} с файлом крупнее отвечал 413 и кодом FST_ERR_CTP_BODY_TOO_LARGE, которого нет ни в одной странице документации. Тот же потолок бил по загрузке файлов ботам и в чаты.
Стало
Тело ограничено 40 МиБ на создании и обновлении записей (POST /v1/{entity}, PATCH /v1/{entity}/{id}, включая POST /v1/items/{entityTypeId}), а также на POST /v1/bots/{botId}/files и POST /v1/chats/{chatId}/files — это чуть меньше 30 МиБ исходного файла после base64. Остальные методы сохраняют прежний потолок 1 МБ: поиск (POST /v1/{entity}/search), пакетные вызовы, служебные методы.
Код отказа 413 теперь PAYLOAD_TOO_LARGE на всех методах /v1/, кроме маршрутов AI (/v1/ai/*, /v1/chat/*, /v1/audio/*, /v1/models) — там сохраняется конверт ошибки, совместимый с OpenAI. Это тот же код, что уже возвращает пограничный слой на своём пороге. Замена затрагивает и потоковую загрузку исходников приложений (POST /v1/apps/{id}/sources) и серверов (POST /v1/infra/servers/{id}/sources), и отказ 413 на неверном Content-Type у публикации приложения, привязки мест встраивания и создания шаблона документа. Прежний внутренний код в ответах больше не встречается.
Порядок проверок изменился в пользу безопасности: на записи сущностей и на загрузке файлов ботам и в чаты ключ проверяется до чтения тела. Запрос без ключа или с неверным ключом теперь получает 401 там, где раньше мог получить 400 о неразобранном JSON или 413 о размере.
Появился новый отказ 429 с кодом LARGE_BODY_BACKEND_BUSY и заголовком Retry-After: 5: одновременно обрабатываемых тел крупнее 1 МБ ограниченное число. Он защищает память сервера — поднятый потолок сам по себе ничем не ограничен, а каждый ожидающий очереди вызов удерживает своё тело. Обычный клиент его не увидит; массовая заливка в несколько потоков — увидит, и правильная реакция на него та же, что на любой 429: подождать и повторить.
Учтите время: вызов в Битрикс24 ограничен 15 секундами и не повторяется, поэтому файл у самой границы на медленном аккаунте может отказать с BITRIX_TIMEOUT. Оставляйте запас или переносите крупные файлы в поле «Файл (диск)» через POST /v1/files/upload.
2026-08-06
NEW-0806-1: 402 при исчерпанной квоте Cowork/Code несёт заголовок Retry-After
Ответ 402 с кодом cowork_quota_exhausted на POST /v1/chat/completions теперь несёт заголовок Retry-After — число секунд до сброса исчерпанного окна квоты (5h, week или month). Раньше момент сброса был виден только в поле resetAt тела ответа; заголовок понимают и обычные HTTP-клиенты без разбора тела. Ответ 402 с кодом insufficient_balance заголовок не несёт — у пустого баланса нет времени сброса.
FIX-0806-2: Веб-поиск: статус ответа различает отказ ключа провайдером и сбой провайдера
Было
Любая ошибка поискового провайдера в POST /v1/search приходила как 502 UPSTREAM_ERROR — и отказ провайдера принять ключ (401/403), и его троттлинг (429), и настоящий сбой. Клиенты повторяли запросы, которые не могли пройти.
Стало
Для BYOK-ключа ответы провайдера 401 и 403 сохраняют свой статус — провайдер отверг ваш ключ, замените его. Ответ провайдера 429 сохраняет статус для любого ключа и несёт заголовок Retry-After. Код ошибки во всех случаях остаётся UPSTREAM_ERROR, а тело дополнительно несёт поле upstream_status с исходным статусом провайдера. Остальные ошибки провайдера, включая отказ ключа платформенного движка, по-прежнему приходят как 502. Дополнительно ограничена длина поля message в ответе 400 INVALID_REQUEST — присланное значение больше не отражается целиком.
Влияние на интеграторов
Обработчик, который повторял запрос на любой 5xx, продолжает работать. Если вы ветвились на 502 как на «любую ошибку провайдера», добавьте ветки для 401/403 (замените BYOK-ключ) и 429 (повторите по Retry-After); надёжный признак «это ошибка провайдера, а не авторизации» — поле upstream_status в теле.
FIX-0806-3: реестр операций в GET /v1/guide перечисляет то, что действительно работает
Было
Список операций сущности в GET /v1/guide расходился с набором работающих эндпоинтов сразу в двух направлениях.
Он молчал о рабочих операциях. Ни одна сущность не объявляла fields, хотя
GET /v1/{entity}/fields отвечает у 46 из 49 сущностей. У бронирований
отсутствовали list и search, хотя GET /v1/bookings и POST /v1/bookings/search
обслуживаются отдельными обработчиками, — робот читал сущность как «только на запись»
и не мог получить идентификатор. У конфигураций открытых линий отсутствовали
list, search, create, update и delete — в реестре оставались только
getById, aggregate и batch. Тем же механизмом были скрыты create шаблонов
документов, list и create адресов, search узлов оргструктуры и delete
пользователя, а у поиска адресов реестр печатал описание общего оконного поиска,
которого этот обработчик не выполняет.
И он обещал то, чего у сущности нет: пример пакетного вызова у восьми сущностей
называл действие create, которое для них возвращает 400 ACTION_NOT_SUPPORTED.
Стало
Операция объявлена ровно тогда, когда её маршрут действительно зарегистрирован.
Появились fields у сущностей, где этот маршрут есть, list и search у
бронирований (с обязательными параметрами dateFrom и dateTo в описании),
полный набор list / search / create / update / delete у конфигураций
открытых линий, create у шаблонов документов, list / search / create у
адресов, search у узлов оргструктуры и delete у пользователя. Описания этих
операций перечисляют параметры, которые читает их собственный обработчик, а не
общий контракт поиска: оконного поиска (autoWindow, windowCount) у них нет.
Пример пакетного вызова называет действие, которое сущность принимает, и ключ,
который читает это действие: ids для удаления, items для остальных записей,
calls для чтения. У сущности без доступных операций записи пример читающий.
Заметка у такой сущности больше не перечисляет набор чтения одной строкой-константой
«принимаются list, get, fields»: она называет действия, которые у ЭТОЙ сущности
действительно отвечают данными, и отдельно — что конверт делает с остальными. Причин
слепоты три: fields у сущности без метода схемы полей отвечает пустым объектом (и
заметка ведёт к GET /v1/{entity}/fields, если этот маршрут есть); get у сущности без
адресного метода чтения отдаёт первую запись коллекции, а не запрошенную; у сущности на
REST 3.0 пакетный подвызов вообще не доходит до метода и возвращает пер-вызовную ошибку
внутри 200; а list у конфигураций открытых линий уходит в Битрикс24 без конверта,
которого требует imopenlines.config.list.get, и отвечает 200 со всей коллекцией,
молча выбросив фильтр.
То же утверждение исправлено ещё в двух местах, где клиент его видит: тело отказа
400 ACTION_NOT_SUPPORTED больше не заканчивается фразой «Supported batch actions: list,
get, fields» (теперь там тот же вычисленный набор — прежде клиент, прочитавший честный
реестр и споткнувшийся об отказ, получал вводящий в заблуждение список назад), и описание
пакетного чтения конфигураций открытых линий в схеме OpenAPI: список принимаемых действий
там сохранён, но сказано, какие из них отвечают данными.
Там же исправлено описание select у списка конфигураций открытых линий: перечисление
через запятую (?select=id,name) читается, не читается только форма с индексом
(?select[0]=id).
Сущность, у которой операции нет, её и не получает: fields не появился у
комментариев к задачам, у разделов календаря и у почтовых ящиков. Осталось необъявленным и то, что
объявлять не следует: маршруты, существующие только чтобы отбить вызов с указанием
верного пути, и aggregate у сущности, где работает лишь подсчёт, — про него
по-прежнему сообщает описание поиска.
Влияние на интеграторов
Изменение аддитивное: прежние поля operations не переименованы и не удалены.
Клиент, который строил список доступных вызовов по этому реестру, теперь видит
операции, которые раньше приходилось угадывать или искать в документации. Клиент,
который копировал пример пакетного вызова как есть, перестанет получать
400 ACTION_NOT_SUPPORTED на сущностях только для чтения.
FIX-0806-4: служебные поля задачи принимаются на записи — так же, как их принимает сам Битрикс24
У задачи семь служебных полей: постановщик (createdBy), кто изменил (changedBy), кто закрыл (closedBy), кто изменил статус (statusChangedBy), а также даты создания, изменения и закрытия (createdDate, changedDate, closedDate). Битрикс24 принимает и сохраняет их все — и при создании задачи, и при обновлении. Вайбкод отклонял шесть из семи, то есть был строже платформы на ровном месте.
Было
POST /v1/tasks и PATCH /v1/tasks/:id отвечали 400 READONLY_FIELD на changedBy, closedBy, statusChangedBy, createdDate, changedDate, closedDate и до Битрикс24 не доходили. Так же отклонялись написания верхним регистром — CHANGED_BY и остальные. Отказ приходил и в подзапросе POST /v1/batch. Седьмое поле, createdBy, при создании работало, а при обновлении отклонялось.
Стало
Все семь принимаются на обеих операциях и на всех трёх поверхностях записи — одиночный маршрут, пакетный запрос сущности и общий пакетный запрос. Принимаются оба написания, createdBy и CREATED_BY. Значение применяется в пределах прав вызывающего пользователя: если Битрикс24 отказывает в правке задачи, отказ приходит как есть — 422 с его собственным текстом, без подмены на нашу ошибку и без ложного успеха.
Смену постановщика Битрикс24 пишет в журнал изменений задачи, и там остаётся настоящий вызывающий пользователь. Для остальных шести полей записи в журнале не предусмотрено. Ещё одна тонкость — у трёх дат значение без часового пояса в написании createdDate получает смещение из заголовка X-Vibe-Timezone, а в написании CREATED_DATE уходит как есть. Обе тонкости разобраны на странице PATCH /v1/tasks/:id.
Что НЕ изменилось: id по-прежнему отклоняется — Битрикс24 присваивает идентификатор сам и переданное значение игнорирует, поэтому явный отказ честнее молчаливой потери. dateStart, activityDate и realStatus тоже остаются закрытыми, но по другой причине: их поведение на записи мы не проверяли, а объявлять поле открытым без проверки не станем.
Передавайте только существующего сотрудника — во всех четырёх полях с идентификатором пользователя. Битрикс24 не проверяет значение на существование и запишет любое число, а задача с несуществующим постановщиком перестаёт управляться через API: отказ приходит и на дальнейшее обновление, и на удаление, причём даже ключу администратора и напрямую в Битрикс24, минуя нас. Предупреждение добавлено на страницу обновления задачи.
Влияние на интеграторов
Ничего менять не нужно: запросы, которые раньше отклонялись, теперь выполняются. Если ваш код полагался на 400 READONLY_FIELD как на защиту авторства и истории — эту роль он не играл: те же значения принимает сам Битрикс24 по своему интерфейсу, в обход нашего слоя. Ограничить подмену служебных полей может только модель прав Битрикс24 — это отдельная доработка на стороне платформы. У лидов и сделок поле автора закрыто по-прежнему, и это не изменилось: там Битрикс24 значение молча игнорирует, поэтому явный отказ остаётся честным ответом.
FIX-0806-5: карточка приложения в каталоге открывает подпуть, а не корень сервера
Было
Карточка приложения в каталоге Битрикс24 всегда открывала корень Black Hole-сервера. Приложение, которое отдаёт интерфейс из подкаталога, из каталога открыть было нельзя: переход отвечал HTTP 200 и показывал то, что живёт в корне того же сервера. Адрес приложения (appUrl) на это никак не влиял, а его правка через PATCH /v1/apps/:id до карточки не доходила.
Стало
Карточка ведёт по полному адресу связанного приложения вместе с подпутём, query и fragment, если этот адрес указывает на тот же поддомен Black Hole, что и сервер карточки. Во всех остальных случаях — прежний корень сервера: другой поддомен, свой домен, другая схема, пустой или неразбираемый адрес.
Правка appUrl через PATCH /v1/apps/:id теперь ставит карточку в очередь на обновление, поэтому новый адрес доезжает до Битрикс24 сам. Разовый проход по уже опубликованным карточкам выполняется на стороне платформы — от интегратора действий не требуется.
Влияние на интеграторов
Ничего менять не нужно. Приложение, отдающее интерфейс из корня, работает как прежде. Приложение в подкаталоге больше не требует ручного обхода: достаточно, чтобы appUrl содержал нужный подпуть.
FIX-0806-6: распознавание речи сообщает о временной паузе провайдера
Было
При временной недоступности кластера POST /v1/audio/transcriptions мог отвечать 502 ai_provider_unavailable, не сообщая клиенту, сколько ждать перед повтором.
Стало
В этом состоянии метод отвечает 429 ai_provider_cooldown с заголовком Retry-After в секундах. Запрос не выполняется и не списывает квоту или деньги. Дождитесь указанного интервала и повторите тот же запрос.
FIX-0806-7: распознавание речи действительно ждёт ответа заявленные 15 минут
Было
Для длинной записи POST /v1/audio/transcriptions мог ответить 503 ai_provider_timeout примерно через 5 минут, хотя в описании метода заявлено ожидание до 15 минут. В тексте ошибки при этом говорилось о сетевом таймауте.
Стало
Метод ждёт ответа весь заявленный срок — до 15 минут — и отвечает 503 ai_provider_timeout с заголовком Retry-After только по его истечении. Ограничение на длительность записи не изменилось: для файлов длиннее ~30 минут по-прежнему разбивайте запись на части.
FIX-0806-8: exec для galaxy-приложения обращается к контейнеру по его настоящему имени
Было
POST /v1/infra/servers/{id}/exec для приложения в галактике всегда обращался к контейнеру по имени из subdomain. Приложение, восстановленное из клона, работает под другим именем, поэтому команда уходила к контейнеру, которого нет: вызов завершался ошибкой, а в неудачном случае мог попасть в оставшийся от прежнего развёртывания контейнер. Остальной жизненный цикл галактики (развёртывание, переезд, остановка) уже учитывал переименование — расходился только exec.
Стало
Имя контейнера резолвится единым правилом для всех операций: используется имя на хосте, если приложение переименовано, иначе subdomain. Имя дополнительно проверяется перед подстановкой в команду; при непригодном имени возвращается 409 GALAXY_APP_NOT_READY с текстом invalid on-host name вместо запуска команды. Для приложений, которые не восстанавливали из клона, поведение не меняется.
BC-0806-9: V1: статус сервера в JSON всегда строчными буквами
Поддержка старого формата до: 06.09.2026
Было
GET /v1/infra/servers и GET /v1/infra/servers/:id отдавали status строчными (running, sleeping), а POST /v1/infra/servers/:id/wake, POST /v1/infra/servers/:id/refresh, поле currentState.status в ответах 422 у POST /v1/infra/servers/:id/start, POST /v1/infra/servers/:id/stop, POST /v1/infra/servers/:id/reboot и infra.unhealthyServers[].status в GET /v1/me — значение перечисления как в базе, ЗАГЛАВНЫМИ (RUNNING, SLEEPING, PROVISIONING). Клиент, выучивший status === 'running' по документации и GET, ломался на ответах пробуждения и обновления состояния.
Стало
Во всех перечисленных полях публичного V1 JSON значение статуса сервера — строчные литералы: provisioning, running, stopped, sleeping, error, deleted. Поле data у обновления состояния по-прежнему строка, а не объект: сравнивайте data === 'running', не data.status. Поле blackholeStatus не изменилось — оно остаётся ЗАГЛАВНЫМИ (CONNECTED, DISCONNECTED, NONE).
Влияние на интеграторов
Замените сравнения с 'RUNNING' / 'SLEEPING' / 'PROVISIONING' и остальными заглавными значениями на строчные либо сравнивайте без учёта регистра. Читайте статус из структурных полей (data, currentState.status), а не из текста message / userMessage — там статус по-прежнему может встречаться заглавными.
NEW-0806-10: деплой galaxy-приложения по ссылке и по сохранённой версии
Раньше galaxy-приложение принимало только встроенный архив: source.url и source.versionId отклонялись с 400 GALAXY_DEPLOY_CONTENT_ONLY. Теперь POST /v1/infra/servers/:id/deploy принимает обе формы там, где платформа включила выкладку по ссылке для вашего аккаунта Битрикс24; где не включила — код GALAXY_DEPLOY_CONTENT_ONLY возвращается как прежде, и встроенный source.content продолжает работать всегда.
Ссылку скачивает сам хост, поэтому архив не проезжает через тело запроса: потолок на встроенный архив (413 GALAXY_UPLOAD_TOO_LARGE) на этот путь не распространяется, и слот одновременных «толстых» запросов (429 DEPLOY_BACKEND_BUSY) он не занимает. source.versionId выкладывает версию, уже лежащую в хранилище исходников: платформа сама минтит подписанную ссылку и связывает версию с этим деплоем, поэтому в истории видно, что именно уехало в прод.
Создание сервера с источником (POST /v1/infra/servers) принимает source.url на тех же условиях. source.versionId там не принимается: сервера, чьё хранилище задавало бы область поиска версии, в момент создания ещё нет — выкладывайте версию вторым шагом, через /deploy.
Кабинетные маршруты остаются на встроенном архиве.
FIX-0806-11: справочник полей элементов смарт-процессов больше не показывает поле contacts
Было
GET /v1/items/:entityTypeId/fields показывал поле contacts с типом crm_contact. Значения по нему не приходило ни в списке, ни в карточке элемента, а записать его было нельзя: Битрикс24 принимал только пустой массив, а любое непустое значение отклонял ошибкой своего внутреннего слоя данных. Поле попадало в справочник сквозным пробросом схемы Битрикс24, а не объявлялось платформой.
Стало
Поле убрано из справочника. Привязанные контакты читаются и пишутся через contactId и contactIds — они не изменились. Фильтр и сортировка по contacts как и раньше отклоняются с кодом UNKNOWN_FILTER_FIELD и UNKNOWN_SORT_FIELD.
Влияние на интеграторов
Действий не требуется: значения по полю не существовало, поэтому клиент, читавший его, всегда получал пустоту. Если вы строили модель данных по справочнику — уберите contacts из неё и опирайтесь на contactIds.
FIX-0806-12: справочник полей страниц сайта сообщает, какие поля могут быть пустыми
Было
Ответ GET /v1/pages/fields не позволял отличить поле, у которого значение есть всегда, от поля, приходящего null. Вдобавок два описания обещали не то, что приходит: datePublic описывался как «приходит пустым объектом», а dateCreate, dateModify и datePublic — как дата фиксированного шаблона. Клиент, написавший разбор по этим описаниям, спотыкался на пустом значении, а фильтр по дате в чужом формате возвращал пустой список с кодом 200.
Стало
Девять полей, которые Битрикс24 заполняет не всегда, помечены признаком nullable: description, xmlId, tplId, tplCode, folderId, searchContent, initiatorAppCode, rule, datePublic. Набор получен замером по всей коллекции страниц живого портала, а не выведен из описания метода Битрикс24.
datePublic описан честно: обёртка отдаёт null, и это обычное значение даже для опубликованной страницы, поэтому факт публикации читайте из active или public. Описания dateCreate, dateModify и datePublic больше не обещают фиксированный шаблон: это строка в формате локали портала, одинаковая в списке и в карточке. Тот же формат нужен и в фильтре: значение в формате другой локали или в ISO Битрикс24 не распознаёт и возвращает пустой список с кодом 200.
Тем же признаком помечены поля в генерируемой схеме OpenAPI: там тип теперь записан как ["string", "null"], поэтому клиент, который валидирует ответ по схеме, больше не падает на пустом значении. Контракт записи не затронут.
Влияние на интеграторов
Действий не требуется: состав полей, типы и значения не изменились — добавился только признак nullable и уточнились описания. Если вы определяли публикацию страницы по наличию datePublic, переключитесь на active или public.
NEW-0806-13: отказ по лимиту ключей называет числа
POST /v1/apps при отказе KEY_LIMIT_REACHED (409) теперь кладёт в
error.details состояние квоты: limit — сколько ключей на человека разрешил
администратор портала, used — сколько занято сейчас.
Было
{
"success": false,
"error": { "code": "KEY_LIMIT_REACHED", "message": "Maximum number of API keys reached" }
}
Стало
{
"success": false,
"error": {
"code": "KEY_LIMIT_REACHED",
"message": "Maximum number of API keys reached",
"details": { "limit": 10, "used": 10 }
}
}
Поле аддитивное — клиенты, читающие только code, ничего не заметят. В used
входят и ключи, выписанные платформой (приложения, агенты, боты), поэтому число
может превышать длину списка из GET /v1/keys.
NEW-0806-14: справочники полей сайтов и сотрудников отдают подписи, описания и перечни значений
GET /v1/sites/fields теперь отдаёт подпись label и описание description у всех 22 полей — раньше они были только у поля type, а остальные 21 приходили с одним типом и признаком «только для чтения». Описания сообщают то, чего по типу не видно: что active через API не устанавливается, что code хранится в форме, обрамлённой слешами, что landingIdIndex/landingId404/landingId503 задаются только при обновлении, а dateCreate и dateModify приходят строкой в формате локали портала, а не в ISO 8601.
GET /v1/users/fields получил перечень допустимых значений enum у пола (personalGender: M, F) и у типа учётной записи (userType: employee, extranet, email) — каждое значение с английской подписью label и русской labelRu. Кроме этого подписи и описания появились у десяти полей рабочих сведений, у которых их не было в Битрикс24 вовсе и вместо подписи приходило само имя поля: WORK_FAX, WORK_PAGER, WORK_STREET, WORK_MAILBOX, WORK_STATE, WORK_ZIP, WORK_COUNTRY, WORK_PROFILE, WORK_LOGO, WORK_NOTES. Если портал такое поле подписал сам, его подпись сохраняется без изменений.
У пола появился ещё и признак nullable: true, а в схеме OpenAPI тип этого свойства объявлен как ["string", "null"]. Незаполненный пол приходит пустым (null) — на замеренном портале так отвечали 48 сотрудников из 50, — а схема без этого признака обещала строку и ничего кроме строки, поэтому клиент, проверяющий ответ по нашей же опубликованной схеме, получал ошибку почти на каждой записи. Признак описывает только чтение: в схеме тела запроса тип поля остался строкой.
Перечни значений приходят и в двух других машиночитаемых поверхностях — GET /v1/guide (блок fieldsDetailed сущности) и схема OpenAPI (x-enumValues у свойства). Подписи и описания объявленных полей схемы отдаёт только OpenAPI (title и description у свойства), поэтому клиент, сгенерированный по схеме, получает их без дополнительных вызовов; путеводитель подписи не несёт намеренно. Подписи и описания десяти полей рабочих сведений приходят только в самом справочнике полей.
Изменения аддитивные: ответы дополнены новыми ключами, набор полей и их значения те же, существующие интеграции продолжают работать без правок.
FIX-0806-15: разделы товаров: поле sort помечено «только для чтения» и «не возвращается»
Было
GET /v1/product-sections/fields объявлял sort записываемым, POST /v1/product-sections и PATCH /v1/product-sections/:id принимали его без ошибки, а Битрикс24 значение не сохранял. При этом ни один ответ на чтение — карточка, список, поиск, отклик создания — поле не содержал, даже если запросить его явно в select. Клиент получал «успех» и продолжал считать, что порядок задан.
Стало
Поле помечено только для чтения и notReturned: true. Запись sort в теле создания или обновления отклоняется с 400 READONLY_FIELD до вызова Битрикс24; в справочнике полей поле осталось видимым вместе с описанием причины, чтобы её можно было прочитать на месте. Упорядочивание по нему работает как раньше: ?sort=sort&order=asc и order=desc дают разный порядок. Фильтрация по sort по-прежнему отклоняется с 400 UNSUPPORTED_FILTER.
Влияние на интеграторов
Уберите sort из тела запросов создания и обновления разделов товаров — иначе запрос целиком получит 400 READONLY_FIELD вместо прежнего «успеха». Если код читал sort из ответа, его там никогда и не было: значение приходило undefined. Менять порядок разделов можно в интерфейсе Битрикс24, читать порядок — упорядочиванием списка по этому полю. Параллельная поддержка прежнего поведения не предусмотрена: прежнее поведение состояло в том, что значение молча терялось, — сохранять было бы нечего.
FIX-0806-16: у сайтов поле active стало только для чтения — включение идёт через публикацию
Было
GET /v1/sites/fields описывал active как обычное записываемое поле, и запрос с ним проходил: POST /v1/sites и PATCH /v1/sites/:id возвращали успех. Значение при этом терялось. Битрикс24 не принимает ACTIVE ни в landing.site.add, ни в landing.site.update — их контракт этого поля не объявляет, а новый сайт всегда создаётся неактивным. Живая проверка обоих вызовов подтвердила потерю: и создание с active: true, и обновление на active: true отвечали успехом, а признак оставался выключенным.
Стало
active помечено readonly. Передача его в теле создания или обновления отклоняется с 400 READONLY_FIELD до вызова Битрикс24. В ответе list/get и в справочнике /fields поле остаётся — читается оно по-прежнему, в том числе фильтрацией и группировкой по нему.
Активность сайта включается публикацией в интерфейсе портала Битрикс24.
Влияние на интеграторов
Если код передавал active в теле создания или обновления сайта — уберите его. Значение всё равно никогда не сохранялось, но теперь запрос отклоняется целиком, поэтому вместе с ним не применятся и остальные поля тела: название, символьный код, домен, описание. Параллельная поддержка прежнего поведения не предусмотрена: прежнее поведение состояло в том, что значение молча терялось, — сохранять было бы нечего.
FIX-0806-17: недоступный склад образов на сборке galaxy-приложения — временная ошибка, а не отказ
Было
Если публичный склад образов был недоступен в момент сборки, POST /v1/infra/servers/:id/deploy
отдавал 502 с сырым текстом Docker, а приложение помечалось как сломанное — повтор приходилось
запускать вручную, и подсказки в ответе не было.
Стало
Ответ несёт код GALAXY_BASE_IMAGE_UNAVAILABLE, признак retryable: true и поле hint с
инструкцией повторить тот же запрос через 2-3 минуты. Слот не помечается сломанным, поэтому
повтор ложится на него же. Для агентов платформа повторяет сама. Одношаговое создание с
исходниками (POST /v1/infra/servers с source) HTTP-ответа уже не держит, поэтому там
приложение по-прежнему помечается сломанным, но текст ошибки называет причину и просит повторить
развёртывание.
FIX-0806-18: отказ выписки при живой подписке больше не выдаёт «демо уже использована»
Было
Если Битрикс24 отказывал в выписке ключа или установке приложения по линии подписки, ответ строился по признаку «демо когда-то активировали». Признак остаётся поднятым всё время действия демо, поэтому портал с ДЕЙСТВУЮЩЕЙ демо-подпиской получал 403 B24_MARKET_TRIAL_USED и текст «демо-подписка уже использована, оформите платную» — при том, что подписка работала и покупать было нечего.
Стало
Пока подписка или демо действуют, отказ выписки отдаётся как 502 CONNECTOR_REST_UNAVAILABLE с текстом «повторите попытку / обратитесь в поддержку» и без предложения оформить подписку. В ответе есть error.details.reason (исходная причина отказа) и error.details.retryable: true. Если демо действительно израсходовано, ответ прежний — 403 B24_MARKET_TRIAL_USED; если подписки не было ни разу — 403 B24_MARKET_SUBSCRIPTION_REQUIRED. Затронуты POST /v1/apps и парные кабинетные маршруты выписки ключей и создания приложений. Дополнительно: отказ вида «REST недоступен» повторяется автоматически один раз — это снимает гонку сразу после активации демо.
FIX-0806-19: платформа сообщает приложению его порт в переменной PORT
Было
Платформа согласовывала порт приложения на трёх уровнях — проброс публичного трафика, EXPOSE в образе и проверка работоспособности, — но самому приложению его не сообщала. Приложение, написанное по общей конвенции облачных платформ (listen(process.env.PORT)), получало пустое значение, занимало случайный свободный порт, и на нужном порту никто не слушал: шлюз отдавал «Приложение не обнаружено», хотя POST /v1/infra/servers/:id/deploy рапортовал успех.
Стало
Платформа передаёт номер порта в переменной окружения PORT — всегда равной полю port запроса (по умолчанию 3000). На отдельной виртуальной машине это отдельный платформенный файл .vibe-platform.env, который systemd-юнит подключает после вашего .env; ваш .env не читается и не перезаписывается. В приложении галактики PORT приходит в окружение контейнера при запуске. В ответе появился шаг platform_env.
Ключ PORT теперь зарезервирован платформой: если прислать свой env.PORT, отличный от поля port, платформа его перекроет и скажет об этом строкой в warnings[] ответа. Прислать совпадающее значение можно — предупреждения не будет.
Влияние на интеграторов
В обычном случае менять ничего не нужно: приложение, слушающее process.env.PORT, теперь работает без явного env.PORT, а уже работающие приложения получат PORT при следующем деплое. Есть одно исключение, и его стоит проверить: если вы держали в env.PORT не порт своего приложения, а что-то другое (порт базы, внешнего сервиса), — переименуйте эту переменную, потому что теперь PORT принадлежит платформе и ваше значение до приложения не дойдёт. В ответе на деплой в таком случае приходит предупреждение в warnings[]. Если вы читаете файл .env напрямую, а не переменные окружения процесса, — читайте process.env.PORT: платформенное значение живёт в отдельном файле. Со своим systemd-юнитом (systemd: false) платформенный файл создаётся, но подключаете его вы — пока не подключили, побеждает ваш env.PORT, и предупреждение в ответе говорит именно об этом; как подключить, описано в POST /v1/infra/servers/:id/deploy.
BC-0806-20: расход квоты отдаётся только в процентах
Поддержка старого формата до: 06.02.2027
Было
GET /v1/ai/quota возвращал в data.byModel[] абсолютные счётчики расхода — tokensIn, tokensOut и audioSeconds — рядом с долей лимита pctOfLimit.
{ "modelId": "bitrix/bitrixgpt-5.5", "calls": 1240, "tokensIn": 800000, "tokensOut": 350000, "audioSeconds": 0, "pctOfLimit": 1.2 }
Стало
Три поля убраны. Расход квоты — как и сам лимит — раскрывается только относительной величиной: pctOfLimit (доля месячного лимита, израсходованная моделью) и calls (количество вызовов).
{ "modelId": "bitrix/bitrixgpt-5.5", "calls": 1240, "pctOfLimit": 1.2 }
Что делать интеграторам
Авторитетная цифра по аккаунту одна — data.pctUsed: именно она считается по леджеру списаний и учитывает скидку за время суток. Разбивка byModel[].pctOfLimit показывает, КУДА ушла квота, и считается пересчётом журнала вызовов по действующим сейчас ценам, поэтому суммировать доли по моделям и сравнивать сумму с pctUsed не нужно — величины разойдутся. Если вам нужны токены для собственного учёта, берите их из GET /v1/ai/usage или снимайте на вызове модели: ответ POST /v1/chat/completions по-прежнему содержит блок usage с prompt_tokens и completion_tokens.
FIX-0806-21: исчерпанная квота Cowork/Code больше не обслуживается подменной моделью
Было
При исчерпании квоты Cowork/Code запрос от ключа десктопа обслуживался резервной моделью, причём инструменты (tools) и системный промпт запроса передавались ей без изменений. Модель отвечала связно и могла сообщить о выполненной работе, которой не было. Ответ приходил с кодом 200 и заголовком X-Cowork-Fallback: true.
Стало
Ответ один для всех ключей: tools, tool_choice и response_format снимаются, модель сообщает об исчерпанном лимите и о том, когда он сбросится. Код ответа 200 при настроенной резервной модели, иначе 402 cowork_quota_exhausted — как раньше. Заголовок X-Cowork-Fallback: true и предупреждение COWORK_QUOTA_FALLBACK остаются, но означают теперь «лимит объявлен», а не «запрос обслужен другой моделью».
Влияние на интеграторов
Не ожидайте tool_calls и структурированный ответ при исчерпанной квоте: response_format снимается, поэтому вернётся текст, а не JSON. Признак состояния — заголовок X-Cowork-Fallback, предупреждение COWORK_QUOTA_FALLBACK или код 402.
Отдельно исправлена дата обновления месячного лимита на бесплатном тарифе. Раньше resetAt.month (GET /v1/cowork/me), windows.month.resetAt и subscription.currentPeriodEnd (GET /v1/cowork/state) отдавали дату из строки подписки, а у бесплатного места она не сдвигалась после окончания периода — то есть приходила дата в прошлом, и отсчёт до обновления показывал «меньше минуты» бесконечно. Теперь все три поля отдают период, в котором место находится фактически: месячный счётчик обнуляется при первом обращении после окончания периода. В теле отказа 402 cowork_quota_exhausted поле resetAt для месячного окна больше не приходит равным 1970-01-01.
Влияние на интеграторов
Если вы кэшировали currentPeriodEnd бесплатного места как неизменную дату — перечитайте её: на просроченном месте она сдвинется вперёд.
FIX-0806-22: починка сервера сообщает причину отказа и больше не оставляет агента выключенным
Было
GET /v1/infra/servers/:id/repair-status при неудаче возвращал error без причины —
SSH install failed (exit 255); serial fallback: Serial console install failed. По этому
тексту нельзя было отличить закрытый порт от недоступной машины или от сорвавшегося
скачивания. Кроме того, установка агента останавливала работающий сервис ДО того, как
скачивала новую версию: если скачивание не удавалось (нет выхода в интернет, недоступен
адрес раздачи), агент оставался выключенным, а следующая попытка починки повторяла то же
самое.
Стало
error несёт причину: для обычного входа — сообщение SSH (Connection refused,
Connection timed out и т.п.), для установки через аварийную консоль — короткий отрывок
вывода консоли (например curl: (6) Could not resolve host: …). Отрывок очищен от
секретов и ограничен по длине. Установка теперь сначала скачивает новую версию агента и
только потом останавливает сервис, а при отказе любого последующего шага возвращает
агента в работу.
FIX-0806-23: выписка ключа проверяет доступ к платформе
Было
POST /v1/keys и POST /v1/apps выписывали новый ключ любому порталу, прошедшему авторизацию, — даже если доступ к платформе у портала закрыт. Ключ при этом работал: выписка проверку доступа не спрашивала.
Стало
Перед выпиской проверяется доступ портала. Порталу без доступа приходит тот же код, который он уже получает на других поверхностях, — MARKETPLACE_REQUIRED, KZ_PAID_ONLY, UZ_PAID_ONLY или INT_TARIFF_REQUIRED, в зависимости от региона лицензии.
Уже выданные ключи продолжают работать. Проворот ключа, автоматическое восстановление и передача владения не затронуты: клиент с закончившимся доступом должен уметь закрыть свои дела.
Влияние на интеграторов
Обработайте отказ на выписке так же, как на остальных поверхностях: оформите доступ и повторите запрос. Ранее выписанные ключи менять не нужно.
NEW-0806-24: понятный отказ, когда личному ключу оставили только placement или entity
Личный ключ работает через входящий вебхук Битрикс24, а тот не хранит права placement
и entity — они требуют контекста приложения. Раньше такой запрос падал невнятно: при
создании ключа приходил 502 DEVKEY_MINT_FAILED с советом обратиться к администратору
портала, при правке прав — 502 DEVKEY_SCOPE_SYNC_FAILED.
Теперь оба случая отвечают 400 PERSONAL_KEY_WEBHOOK_SCOPES_INVALID с текстом, который
говорит, что делать: добавить хотя бы одно обычное право (например crm или user_brief)
либо создать OAuth-приложение, если нужны встраивания.
Отказ приходит, только когда после отбрасывания этих двух прав не остаётся ни одного,
которое Битрикс24 может привязать к вебхуку. Смешанный набор (placement + crm)
по-прежнему проходит.
Затронутые эндпоинты: POST /v1/keys, PATCH /v1/keys/{id}, POST /v1/keys/{id}/rotate
Смежное изменение ответа: у личного ключа в поле scopes ответа на создание и правку
больше не возвращаются placement и entity — вебхук их всё равно не несёт, и раньше
ответ обещал право, которого у ключа нет. Ключи приложений и системные ключи не затронуты.
FIX-0806-25: сборка Python-приложения в галактике больше не падает на существующей версии пакета
Было
Деплой с runtime: python311* и requirements.txt падал с No matching distribution found for <пакет> — на версии, которая существует и ставится. Причина не в версии: контейнер собирался в изоляции и не знал про каталог пакетов, доступный из нашего облака. Рядом с этим текстом платформа не показывала ничего, и ошибка читалась как ваша.
Стало
Платформа задаёт образу каталог пакетов по умолчанию. Ваш собственный --index-url в requirements.txt или в команде установки по-прежнему сильнее нашего.
Если каталог всё же не ответит, error.category теперь INSTALL_REGISTRY_UNAVAILABLE (было GENERIC), а error.buildHint и одноимённое поле в GET /v1/infra/servers/:id несут понятную причину вместо пустоты. Значение аддитивное: прежние коды не менялись. Отказ терминальный — автоматически платформа его не повторяет.
2026-08-05
FIX-0805-1: перевыпуск ключа больше не осиротит зарегистрированного им бота
Было
Бот, зарегистрированный через POST /v1/bots, запоминает ключ, которым его завели. После POST /v1/keys/:id/rotate эта привязка оставалась на старом ключе: новый ключ получал 403 BOT_ACCESS_DENIED на любой вызов по этому боту, а когда старый ключ истекал по окончании льготного периода, бот замолкал совсем — входящие события копились в очереди, но забрать их (GET /v1/bots/:id/events) было уже некому. Вернуть бота можно было только через POST /v1/bots/:botId/transfer.
Стало
При перевыпуске бот переезжает на новый ключ вместе с приложением: вызовы по боту и опрос событий новым ключом продолжают работать без вмешательства.
Что по-прежнему не так, как хотелось бы
Боты, которыми управляет ИИ-агент или managed-бот, этой перепривязкой не затрагиваются — у них свой путь смены ключа, и попытка перевести их отсюда была бы рассинхроном с их собственными полями. Для них поведение не изменилось.
Влияние на интеграторов
Менять ничего не нужно. Ручной POST /v1/bots/:botId/transfer после перевыпуска ключа больше не требуется — он остаётся только для передачи бота между разными ключами.
FIX-0805-2: после перевыпуска ключа контейнер на общем хосте больше не теряется
Было
Перепривязка при POST /v1/keys/:id/rotate обходила стороной серверы на общем хосте (kind=GALAXY_APP): такой контейнер оставался за старым ключом, и после окончания льготного периода деплой, выполнение команд, загрузка файлов и просмотр логов новым ключом переставали его находить.
Стало
Контейнер на общем хосте переключается на новый ключ вместе с остальными серверами. Исключение осталось ровно одно и узкое: контейнер не переводится на ключ авторизации приложения (vibe_app_) — такая привязка необратима и ломает выкладку.
Что по-прежнему не так, как хотелось бы
Ключ, зашитый в переменные окружения самого контейнера, платформа не подменяет: его значения задаются при запуске контейнера. Обновите переменную и разверните приложение заново — на это есть льготный период ключа.
Влияние на интеграторов
Менять ничего не нужно. Вызовы новым ключом к контейнеру на общем хосте теперь продолжают работать после окончания льготного периода.
FIX-0805-3: перевыпуск ключа больше не отрывает от него сервер и приложение
Было
После POST /v1/keys/:id/rotate сервер и приложение, созданные с помощью этого ключа, продолжали внутренне числиться за старым ключом. Когда старый ключ истекал по окончании льготного периода, вызовы деплоя, выполнения команд, загрузки файлов и просмотра логов такого сервера с новым ключом переставали находить сервер.
Стало
После перевыпуска сервер, приложение и его живые токены доступа (api-bearer, минтятся через POST /v1/infra/servers/:id/access-tokens) переключаются на новый ключ вместе с ним — вызовы деплоя/exec/upload/logs новым ключом продолжают находить сервер, а обновление такого токена (POST .../access-tokens/:tokenId/refresh) больше не отказывает из-за несовпадения ключей.
Что по-прежнему не так, как хотелось бы
Старый ключ теряет доступ к серверу и приложению НЕМЕДЛЕННО, в момент перевыпуска — а не по истечении льготного периода. Формально ключ ещё активен эти часы (KEY_GRACE_PERIOD_HOURS), но сервер и приложение уже переехали на новый, поэтому вызовы старым ключом к этому серверу перестают находить его сразу.
Влияние на интеграторов
Менять ничего не нужно. Клиент, ловивший пропажу сервера после ротации как постоянную проблему, теперь видит непрерывный доступ — кроме самого старого ключа, который перестаёт видеть сервер раньше, чем истекает формально.
BC-0805-4: описание полей задачи совпало с её ответом, числа стали числами
Поддержка старого формата до: 04.02.2027
Было
GET /v1/tasks/fields описывал 92 поля, из которых 66 приходили именами вида
MARK, NOT_VIEWED, STAGE_ID, CHAT_ID — в ответах GET /v1/tasks и
GET /v1/tasks/:id таких ключей не бывает никогда. При этом бо́льшая часть ключей
самого ответа в описании отсутствовала. Значения тоже расходились с объявленным
типом: id, status, priority, groupId, chatId, responsibleId,
createdBy, changedBy, closedBy, statusChangedBy, timeEstimate,
timeSpentInLogs объявлены числами, а приходили строками ("289", "2").
Признаки «да/нет» приходили строками "Y" и "N", а "N" в любом языке
истинна. Пустые tags, group, accomplicesData, auditorsData приходили
пустым массивом при объявленном объекте. Поля, реально приходящие пустыми, не были
помечены как допускающие пустое значение. chatId дополнительно менял тип между
поверхностями: строка в списке, число в карточке.
Клиент, сгенерированный по такому описанию, не работал.
Стало
Описание и ответ называют одни и те же поля. Объявлено 69 полей; сырые имена
верхним регистром из описания убраны (остаются только пользовательские поля
портала и CHECKLIST — он доступен через эндпоинты чек-листа). Объявленные
числами поля приходят числами, признаки «да/нет» — значениями true и false,
пустые tags, group, accomplicesData, auditorsData — пустым объектом.
27 полей помечены как допускающие пустое значение. chatId — число на обеих
поверхностях.
Что делать интеграторам. Проверьте в своём коде: сравнения значений со строками (status === "2",
id === "289"), проверки признаков на непустую строку и обращения к пустым
tags / group / accomplicesData / auditorsData как к массиву.
Поля subStatus (только в списке) и action, checklist, checkListTree,
checkListCanAdd (только в карточке) приходят по-прежнему и в описание не
включены — списки и карточки задач различаются составом ключей на стороне
Битрикс24. Поле realStatus служит только для отбора и сортировки и в ответе
не приходит — теперь это видно и машине, по признаку notReturned в описании.
FIX-0805-5: пауза при недоступной модели удлиняется, пока кластер не восстановится
Было
Пауза после отказа 429 ai_provider_cooldown всегда длилась около минуты. По её истечении платформа снова пускала весь поток в кластер моделей, и если тот ещё не восстановился, всё повторялось: минута ожидания — залп повторов — снова отказы. Значение Retry-After при этом всегда было одним и тем же, поэтому клиент, зашивший минуту константой, вёл себя так же, как читающий заголовок.
Стало
Первая пауза по-прежнему около минуты, но если по её истечении кластер всё ещё отвечает ошибками, следующая пауза удваивается — до четырёх минут максимум. Как только вызов проходит успешно, счёт сбрасывается и следующая пауза снова начинается с минуты. Заголовок Retry-After (и поле retryAfter в терминальном кадре потока) несёт актуальный остаток, поэтому берите время ожидания из ответа, а не из константы в своём коде.
NEW-0805-6: деплой предупреждает, когда проверенный путь приложения расходится с адресом для Bitrix24
Было
Деплой с healthPath, отличным от /, проверял приложение на подпути и возвращал 200, ничего не сообщая про адрес, по которому приложение открывает Bitrix24. Если приложение отвечало только на подпути, а appUrl оставался голым адресом сервера, плейсмент-фрейм открывал корень: деплой зелёный, приложение внутри Bitrix24 не открывается, и в ответе про это ни строчки.
Стало
POST /v1/infra/servers/{id}/deploy в таком сочетании добавляет строку в необязательный массив warnings (обычный JSON-ответ и событие done в SSE — так же, как уже устроены подсказки про displayName/description и про changelog). Подсказка называет обе половины расхождения — проверенный путь и открываемый адрес — и два выхода: раздавать сборку с / либо перенести подпуть в appUrl приложения через PATCH /v1/apps/{id}. Подпуть в appUrl поддерживается, запрета нет.
Подсказка не появляется, когда healthPath не задан или равен /, когда appUrl уже несёт путь, когда адрес приложения не на домене платформы и когда приложение ещё не связано с сервером. Форма ответа не меняется: warnings как был необязательным, так и остался.
FIX-0805-7: Привязка места встраивания называет причину отказа
Привязка места встраивания через ключ приложения больше не отвечает безымянным 502 BITRIX_UNAVAILABLE, когда Битрикс24 отклоняет регистрацию.
Было
POST /v1/placements/bind на любую причину отказа со стороны Битрикс24 отдавал один и тот же ответ — 502 BITRIX_UNAVAILABLE с текстом «Failed to register placement on Bitrix24 via dev key». Отличить «у приложения нет нужного права» от «не хватает обязательной настройки места» было нельзя: аккаунт возвращает оба случая одним непрозрачным кодом. Для чат-виджетов IM_SIDEBAR, IM_NAVIGATION, IM_TEXTAREA вызов без options.iconName попадал в тот же безымянный отказ.
Стало
Причина отказа называется:
403 PLACEMENT_APP_GRANT_MISSING— место недоступно приложению Битрикс24. Вdetailsприходят требуемое правоrequiredScope, места, которые приложению доступны (availablePlacements,availablePlacementsTotal), и путь расширения прав вremediation.400 PLACEMENT_OPTIONS_REQUIRED— не хватает обязательной настройки места, которую платформа не смогла подставить (missingвdetails).400 PLACEMENT_NOT_REST_BINDABLE— код в принципе не привязывается через API.502 BITRIX_UNAVAILABLEостаётся для остальных случаев и теперь несёт вdetailsпризнакplacementInAppList, а при недоступной диагностике —diagnostics("placement_list_skipped"или"placement_list_empty").
Значок чат-виджета больше не обязателен: если options.iconName не передан, платформа подставляет его сама и сообщает об этом в успешном ответе полем optionsDefaulted. Своё значение всегда важнее подставленного.
Справочник GET /v1/placements/available отдаёт по каждому коду три новых поля — requiredScope, requiresIconName, restBindable — и показывает десять кодов, которые привязывались, но в справочнике не значились, включая вкладки и панели задач. Блок placements.bindPrerequisite в данных ключа описывает требование права заранее.
Влияние на интеграторов
Менять вызовы не нужно. Если вы разбираете отказы привязки по коду — добавьте три новых кода; если полагались на обязательность options.iconName — поле стало необязательным, поведение с переданным значением не изменилось.
FIX-0805-8: туннель переживает перезапуск приложения, а автоопределение порта больше не уводит цель
Было
Агент в режиме автоопределения порта сбрасывал найденную цель по одному неудачному наблюдению. Перезапуск приложения (1-3 с) или ответ медленнее 1,5 с — и туннель отдавал страницу-заглушку ещё около 5 секунд после того, как приложение снова отвечало. Отдельно: цель пересчитывалась на каждом успешном скане, поэтому служебный процесс, поднявшийся на меньшем порту, забирал туннель у полностью здорового приложения — ответ HTTP 200 с чужим содержимым, без единой ошибки.
data.warning у PATCH /v1/infra/servers/:id/port и шаг tunnel_routing у
POST /v1/infra/servers/:id/deploy обещали, что автоопределение сойдётся «за ~30 с».
Стало
Агент различает два сигнала. Пока порт приложения присутствует среди слушающих, цель удерживается; освобождается она только после нескольких подряд идущих наблюдений, что порт исчез (~15 с), либо — если порт слушает, но не отвечает — примерно через 2 минуты. Отвечающая цель больше не пересчитывается, кроме случая, когда текущая цель — 80/443, а ответил настоящий порт приложения.
Тексты data.warning и шага tunnel_routing переписаны честно. Автоопределение
подтверждает новый порт примерно за минуту, если прежний порт освободился; если на
прежнем порту остался живой отвечающий процесс, сканер намеренно удерживает его и сам не
переключится — задайте порт явно через PATCH /v1/infra/servers/:id/port либо остановите
тот процесс. POST /v1/infra/servers/:id/repair для этого случая не подходит: ремонт
переустанавливает агента в режиме автоопределения, и выбор порта начнётся заново — с тем
же результатом, если прежний процесс всё ещё отвечает.
Эффект приезжает на сервер вместе с обновлением агента до 1.3.7.
FIX-0805-9: подсказка MISSING_FIELDS у привязок реквизитов называет имена полей, а не отправляет за ними на другой эндпоинт
Было
Отказ 400 MISSING_FIELDS у POST /v1/requisite-links сообщал, что вместо camelCase принимаются и «сырые» имена в UPPER_SNAKE, и предлагал взять их из GET /v1/requisite-links/fields. По этому адресу их нет: ответ /fields отдаёт имена в camelCase. Читатель отказа шёл за списком туда, где список в другой нотации, и возвращался ни с чем.
Стало
Сообщение перечисляет шесть имён прямо в тексте: ENTITY_TYPE_ID, ENTITY_ID, REQUISITE_ID, BANK_DETAIL_ID, MC_REQUISITE_ID, MC_BANK_DETAIL_ID. Обе нотации по-прежнему принимаются на запись, код отказа и условие не изменились.
FIX-0805-10: схема API описывает обмен сессии и слоты встраивания, и честно говорит о своём покрытии
Было
Схема GET /v1/openapi.json описывалась как полная, а GET /v1/guide советовал нарезать её по областям, чтобы она поместилась в контекст ИИ-агента. Часть живых методов при этом в схему не попадала: агент, который добросовестно так и делал, приходил к выводу, что метода нет. Конкретный случай — обмен контекста встраивания на сессию: метод работал и был описан в документации, но в схеме под /v1/oauth/ находились только начало авторизации, обратный вызов, обмен кода и отзыв, поэтому к нам пришла просьба добавить то, что уже давно работало.
Стало
В схему добавлены обмен контекста встраивания на сессию, опрос результата авторизации для окружений без обратного вызова, а также все четыре метода работы со слотами встраивания: список зарегистрированных, справочник доступных кодов, регистрация и снятие. У изменяющих методов указана область доступа, которую проверяет сам обработчик.
Главное: схема больше не обещает полноты, которой у неё нет. Пути сущностей строятся из живого реестра и полны, а рукописные разделы ещё дополняются — поэтому и в описании схемы, и в GET /v1/guide теперь прямо сказано: отсутствие пути не означает отсутствия метода, и как проверить наличие метода за один вызов (действительно отсутствующий путь отвечает ROUTE_NOT_FOUND, а живой — ошибкой проверки данных). Там же названы разделы, которых в схеме не будет никогда: входящие обработчики, которые платформа принимает, а не предоставляет, подсказки о неверном пути и разделы, доступные только управляющему ключу.
FIX-0805-11: клиент на элементе смарт-процесса записывается, а при выключенном блоке «Клиент» приходит отказ вместо мнимого успеха
Было
Поле contactIds у элементов смарт-процессов (PATCH /v1/items/:entityTypeId/:id) и у предложений (PATCH /v1/quotes/:id) было помечено только для чтения, поэтому запись отклонялась с 400 READONLY_FIELD. Основанием считалось, что привязка контактов меняется не через crm.item.update; проверка на реальном портале это не подтвердила — метод набор привязок меняет.
Вторая половина той же истории: если у смарт-процесса выключен блок «Клиент», Битрикс24 принимает contactId, contactIds и companyId, отвечает успехом и значение не сохраняет. Платформа этот успех передавала как есть — вызывающая сторона получала 200 на запись, которой не произошло, и узнать об этом можно было только повторным чтением элемента.
Стало
contactIds доступно на запись у элементов смарт-процессов и у предложений. Передавайте полный список: набор привязок заменяется целиком, а не дополняется, — первый контакт списка становится основным. Поле contacts (развёрнутые объекты, а не идентификаторы) остаётся только для чтения.
Запись клиента в смарт-процесс с выключенным блоком «Клиент» теперь отклоняется до обращения к Битрикс24 — 400 с кодом CLIENT_BLOCK_DISABLED. Сообщение называет поле, из-за которого отказ, и GET /v1/smart-processes/:entityTypeId, где в поле isClientEnabled видно состояние блока. Правило распространяется на все три поля клиента — contactId, contactIds, companyId, — потому что блок гейтит их одинаково.
Правило действует на всех поверхностях записи, включая пакетные: и POST /v1/items/:entityTypeId/batch, и POST /v1/batch. На пакетной поверхности сущности отказ относится ко всей пачке и называет индекс элемента, на общей — приходит по конкретному подвызову и остальные подвызовы не затрагивает.
Пустые значения под правило не попадают: 0, '', [] и null означают «клиента нет», а не запись клиента. Это важно для сценария «прочитать элемент, поменять одно поле, отправить объект целиком»: у элемента с выключенным блоком клиентские поля читаются именно такими и возвращаются в каждом обновлении. Если метаданные типа получить не удалось, запись пропускается — сбой чтения настроек не блокирует обновление.
Влияние на интеграторов
Запрос, который писал клиента в смарт-процесс с выключенным блоком, раньше получал 200, а теперь получит 400 CLIENT_BLOCK_DISABLED. Это и есть исправление: сохранения не было ни тогда, ни сейчас, но теперь об этом видно сразу. Либо включите блок «Клиент» у типа смарт-процесса, либо не отправляйте клиентские поля. Запросы к типам с включённым блоком не затронуты.
FIX-0805-12: фильтр в виде JSON-объекта применяется, а попытка ИЛИ получает свой код ошибки
Было
У параметра filter в списочных запросах было две формы записи, и вторая молча не работала. Скобочная (?filter[id]=3) применялась. Форма JSON-объектом (?filter={"id":3}) — та, которую показывают примеры в документации, — не распознавалась: параметр отбрасывался, запрос возвращал 200 и всю коллекцию целиком. Отличить работающий фильтр от отброшенного по ответу было нельзя.
Отдельная проблема — попытка выразить ИЛИ. Разборщик строки запроса поддерживает два уровня вложенности скобок, поэтому ?filter[$or][0][id]=1 до фильтра не доходил вовсе и читался как имя поля. На сделках это давало UNKNOWN_FILTER_FIELD с именем «поля» filter[$or][0][id] — ответ отправлял разбираться с именами полей вместо того, чтобы сказать, что ИЛИ одним фильтром не выражается. Более короткие написания при этом отвечали правильным INVALID_FILTER_OPERATOR, то есть одна и та же ошибка получала два разных ответа.
Стало
Обе формы filter равноправны: скобочная и JSON-объектом. Значение, которое не является ни тем, ни другим (строка не разбирается как JSON, разобралась в число, массив или null, либо параметр пришёл массивом — форма ?filter[]=), отклоняется с 400 и кодом INVALID_FILTER — отказ происходит до обращения к Битрикс24. Пустое значение ?filter= по-прежнему означает «без фильтра». Повторный ?filter=a&filter=b массивом не приходит: разборщик оставляет последнее значение, и оно отклоняется как не-JSON.
Тем же кодом INVALID_FILTER отклоняется запрос, в котором смешаны обе формы — ?filter={"id":3}&filter[amount]=5. Разборщик строки запроса пишет их в одно и то же место, поэтому вторая форма замещает первую и половина условий теряется, а ответ выглядит корректно отфильтрованным. Восстановить потерянную половину нельзя, поэтому запрос отклоняется.
Логические ключи $or, $and, $not и logic теперь отклоняются с 400 INVALID_FILTER_OPERATOR при любой глубине вложенности и на любой сущности, одним и тем же текстом: он называет $in для ИЛИ по одному полю, пакетный запрос для ИЛИ по разным полям и напоминает, что И — поведение по умолчанию. Поле, чьё имя лишь начинается с такого ключа (например logicGroup), под правило не попадает.
Влияние на интеграторов
Если запрос присылал filter в нераспознаваемой форме, раньше он получал 200 и всю коллекцию, а теперь получит 400 INVALID_FILTER. Это и есть исправление: прежний ответ выглядел успешным, но данные приходили неотфильтрованными. То же касается запросов, где смешаны обе формы: раньше применялась половина условий, теперь такой запрос отклоняется — соберите фильтр в одной форме. Работающие запросы — целиком скобочные или целиком JSON — не затронуты.
FIX-0805-13: причина падения сборки в галактике больше не подменяется справкой npm
Было
Приложение без файла блокировки зависимостей проходит через автоматическую доустановку: платформа пробует npm ci, и если тот отказывается — ставит зависимости обычным способом. Сам шаг при этом завершается успешно, но отказ npm ci остаётся в журнале сборки, а его последней строкой идёт справка вида «Run npm help ci for more info».
Если сборка потом падала по совсем другой причине — например на сборщике интерфейса или на проверке типов — короткое поле provisionError показывало именно эту справку. Она выглядит как совет по установке зависимостей, поэтому реальная причина не читалась: разработчик пересобирал приложение снова и снова, разбираясь с шагом, который в действительности прошёл.
Стало
Служебные и справочные строки npm («Run npm help … for more info», «command failed», «command sh -c …») больше не могут стать заголовком ошибки — они отбрасываются наравне с уже отбрасываемыми ссылками на файл журнала.
Заодно платформа научилась узнавать отказы сборщиков интерфейса и проверки типов: сообщение о неудачном преобразовании файла, строка «ожидалось одно, встретилось другое», диагностика компилятора типов, неудачное разрешение импорта. Когда конкретной строки нет, берётся сообщение самого инструмента сборки — оно хотя бы называет, что упало. Полный журнал по-прежнему доступен в buildLog.
FIX-0805-14: справочник сотрудников доступен через личный ключ владельца сервера
Было
GET /v1/infra/servers/:id/b24-users у сервера, привязанного к ключу авторизации приложения, отдавал пустой список с подсказкой до тех пор, пока приложение не будет авторизовано на портале — даже если у владельца сервера был рабочий личный ключ.
Стало
Если ключ сервера и связанное приложение не дают доступа к порталу, справочник берётся через активный личный ключ владельца сервера. Формат ответа не изменился; подсказка приходит только когда ни один источник не сработал.
FIX-0805-15: на self-hosted портале отказ модуля по подписке снова завершает выписку
Было
Опубликованная 4 августа правка делала отказ модуля по подписке неокончательным:
установка приложения (POST /v1/apps) на self-hosted портале повторялась через
ключ разработчика вместо того, чтобы вернуть 403.
Стало
Правка отозвана. Отказ снова окончателен: запрос отвечает 403 с кодом
B24_MARKET_SUBSCRIPTION_REQUIRED, второй способ выдачи не пробуется. Это то же
поведение, что действовало до 4 августа. Облачные порталы не затронуты ни той
правкой, ни её отзывом.
Влияние на интеграторов
Если вы опирались на заметку от 4 августа — повтор через ключ разработчика
больше не выполняется, и ответ определяется наличием подписки на портале.
Клиентам, которые видят этот 403, нужна активная подписка Маркетплейса.
NEW-0805-16: регион при создании сервера стал необязательным
Было
Создание отдельного сервера требовало тройку provider + plan + region. Запрос без региона отклонялся с 400 INVALID_REQUEST и сообщением о том, что все три поля обязательны. То же самое действовало на создание галактики.
Стало
region можно не присылать — платформа сама подставит регион по умолчанию для указанного провайдера (сначала предпочтительный, иначе первый доступный из каталога). Обязательными остаются provider и plan. Присланный регион по-прежнему уважается: тот, кто указывает его явно, получает ровно то, что просил. Если у провайдера нет ни одного региона, ответ — 400 INVALID_REGION с указанием провайдера.
Изменение затрагивает POST /v1/infra/servers и создание галактики.
NEW-0805-17: поля шести справочников приходят с названием и описанием
Справочник полей — это ответ /fields, по которому клиент или ИИ-агент понимает, что за поле перед ним. У шести сущностей он не отвечал на этот вопрос: поле описывалось только типом и признаком «только для чтения», а что именно в нём лежит, приходилось искать в документации.
Теперь название (label) и описание (description) есть у всех объявленных полей: GET /v1/payments/fields — 44 поля, GET /v1/basket-items/fields — 27, GET /v1/pages/fields — 27, GET /v1/catalog-sections/fields — 10, GET /v1/items/:entityTypeId/fields — 34, а у GET /v1/statuses/fields подпись получило служебное поле extra, единственное из одиннадцати, которое её не имело.
В описаниях названы вещи, на которых легко ошибиться. У оплат: datePayBefore Битрикс24 помечает устаревшим, companyId принимает и не использует, psStatus — это флаг Y/N, а не текст статуса, priceCod и externalPayment относятся к коробочной версии. У страниц прямо сказано, какие поля приходят строкой "Y"/"N" (deleted, public, sys, sitemap, folder) — в отличие от булева active. У позиций корзины названы коды единиц измерения и то, что properties и reservations приходят только в карточке, а в списке их нет.
Заодно объявлены поля, которые API уже возвращал в данных, но в справочнике не описывал: у элементов смарт-процессов — entityTypeId и блок UTM-меток (utmSource, utmMedium, utmCampaign, utmContent, utmTerm), у компаний — одиннадцать полей: разложенные по типам телефоны и адреса e-mail (phoneWork, phoneMobile, phoneMailing, emailWork, emailHome, emailMailing), контакт открытой линии imol, фактический и юридический адреса, entityTypeId и служебная строка поиска searchContent — у последней в описании прямо сказано, что её состав может меняться без предупреждения и опираться на неё не стоит.
Ключи добавляются к описанию поля, прежние type и readonly у ранее описанных полей не менялись — ради самих подписей менять ничего не нужно. Но в этом же выпуске есть записи FIX, где запись части полей ужесточилась: у компаний, у элементов смарт-процессов и у поля «активна» страниц сайта попытка записать то, что Битрикс24 всё равно не сохраняет, теперь отклоняется вместо ложного успеха. Если ваш код передаёт эти поля в теле, прочитайте те записи — там сказано, что убрать.
FIX-0805-18: у объявленных полей компаний пустое значение приходит как null, а запись в них больше не игнорируется молча
Одиннадцать полей компаний и шесть полей элементов смарт-процессов раньше приходили в данных, но справочник /fields их не описывал. Пока поле не описано, платформа пропускает его значение как есть и не проверяет запись — отсюда два следствия, которые видны клиенту.
Было
У полей компаний emailWork, emailHome, emailMailing, phoneWork, phoneMobile, phoneMailing, imol, address, addressLegal и searchContent незаполненное значение приходило пустой строкой "". Запись любого из них — как и entityTypeId, как и UTM-меток у элементов смарт-процессов — принималась с успехом и молча ничего не меняла: Битрикс24 эти поля из тела запроса не сохраняет. Так вели себя и создание, и обновление, и оба пакетных запроса: POST /v1/companies с полем address возвращал 201, компания создавалась, а адрес терялся.
Стало
Незаполненное значение этих полей приходит как null — так же, как у всех остальных строковых полей платформы, поэтому проверка if (value) работает единообразно. Запись любого из них в теле отклоняется с 400 READONLY_FIELD — и при создании, и при обновлении, и в подзапросах пакетных запросов: молчаливого «успеха без результата» больше нет. Заодно у компаний заработали фильтр и сортировка по этим полям — например filter[phoneWork] — раньше запрос отклонялся как обращение к неизвестному полю. Это следствие описания поля, а не отдельная возможность: у служебной строки searchContent фильтр тоже стал приниматься, но её состав может меняться без предупреждения, поэтому опираться на него не стоит. Записываются значения по-прежнему: телефоны и адреса e-mail — через мультиполя phone и email, адреса — в реквизитах компании, тип сущности задаётся адресом запроса.
Влияние на интеграторов
Если код читает эти поля компаний и рассчитывает на строку (например берёт длину или зовёт trim), добавьте проверку на null. Если код передавал любое из этих полей в теле создания или обновления — уберите его: значение всё равно никогда не сохранялось, а теперь запрос отклоняется целиком, поэтому вместе с ним не применятся и остальные поля тела. Параллельная поддержка прежнего поведения не предусмотрена: прежнее поведение состояло в том, что значение молча терялось, — сохранять было бы нечего. Записи всех остальных полей и форма ответа list/get для описанных ранее полей не изменились.
FIX-0805-19: у страниц сайта поле active стало только для чтения — включение идёт через публикацию
Было
GET /v1/pages/:id/fields описывал active как обычное записываемое поле, и запрос с ним проходил: POST /v1/pages и PATCH /v1/pages/:id возвращали успех. Значение при этом терялось. Битрикс24 не принимает ACTIVE ни в landing.landing.add, ни в landing.landing.update — их контракт этого поля не объявляет, а новая страница всегда создаётся неактивной. Клиент получал «готово» и неопубликованную страницу, а расхождение обнаруживал, когда приходил смотреть на сайт.
Стало
active помечено readonly. Передача его в теле создания или обновления отклоняется с 400 READONLY_FIELD. В ответе list/get и в справочнике /fields поле остаётся — читается оно по-прежнему.
Публикация и снятие с публикации выполняются отдельными вызовами, которые действительно работают: POST /v1/pages/:id/publication и POST /v1/pages/:id/unpublish.
Влияние на интеграторов
Если код передавал active в теле создания или обновления страницы — уберите его и вызовите публикацию отдельным запросом. Значение всё равно никогда не сохранялось, но теперь запрос отклоняется целиком, поэтому вместе с ним не применятся и остальные поля тела: заголовок, символьный код, описание. Параллельная поддержка прежнего поведения не предусмотрена: прежнее поведение состояло в том, что значение молча терялось, — сохранять было бы нечего.
NEW-0805-20: создание сервера с кодом внутри запроса встало в общую очередь тяжёлых запросов
Запрос на создание сервера может нести архив с кодом прямо в теле — в поле source.content. Такой запрос дорого обходится по памяти, и раньше он был единственным из тяжёлых, кто шёл без очереди: POST /:id/deploy и POST /:id/upload уже ограничивали число одновременных, а создание — нет.
Было
Одновременные создания серверов с архивом внутри запроса ничем не ограничивались. Ответ всегда шёл по существу — либо успех, либо ошибка проверки полей.
Стало
Такое создание встало в тот же счётчик, что и загрузка кода. Когда предел выбран, приходит 429 с кодом DEPLOY_BACKEND_BUSY и заголовком Retry-After: 30. Запросы без source (обычное создание сервера) и запросы со ссылкой вместо архива ограничения не касаются.
Чтобы не зависеть от очереди совсем, создавайте сервер без source, а код загружайте отдельным запросом со ссылкой — {source: {url: ...}}.
FIX-0805-21: repair восстанавливает туннель, даже когда входящий SSH недоступен
Было
POST /v1/infra/servers/:id/repair отчитывался об успехе шага serial_console, затем падал на
ssh_install с SSH install failed (exit 255) примерно через 10 секунд, и сервер оставался
DISCONNECTED — то есть документированное восстановление до CONNECTED не происходило, а
deploy и exec на таком сервере оставались заблокированы. Отдельно: у сервера без публичного
IP установка агента через serial console не срабатывала никогда.
Стало
Шаг serial_console больше не подтверждает успех, если открыть файрвол не удалось. Установка
агента через serial console исправлена и теперь работает как для сервера без публичного IP, так и
как резервный путь: если попытка по входящему SSH не удалась, repair доустанавливает агента
out-of-band через serial console (агенту нужен только исходящий коннект). Имена шагов в
GET /repair-status не изменились; при провале обоих путей
поле error содержит обе причины через ; serial fallback:. Резервная попытка добавляет до
двух минут к уже неуспешному вызову.
FIX-0805-22: встраивание: Битрикс24 назвал причину — называем её и мы
Было
Когда Битрикс24 отказывал в привязке места встраивания словами «приложение не найдено» или «доступ запрещён», POST /v1/placements/bind отвечал 502 BITRIX_UNAVAILABLE. Названную причину было видно только в служебных полях ответа, а по коду ответа отличить «приложения нет на аккаунте» от «нет прав на установку» было нельзя.
Стало
Два отказа приходят со своим кодом:
404 B24_EMBEDDING_APP_NOT_FOUND— Битрикс24 не знает идентификатор приложения: локальное приложение удалили или переустановили. Лечение: создать локальное приложение заново и вызватьPOST /v1/apps/:id/relink-oauthс новымиbitrixClientIdиbitrixClientSecret.403 B24_EMBEDDING_INSTALL_DENIED— отказ доступа, устоявший против проверки активной подписки: у пользователя, чьим ключом разработчика идёт вызов, нет права ставить локальные приложения и/или нет доступа к самому приложению.
Второй код приходит только там, где состояние подписки удалось подтвердить как активное. Не удалось — отказ остаётся 502: неизвестное не выдаётся за конкретную причину.
Третий отказ — нехватка права у служебного ключа — раньше на коробочном аккаунте выдавался за требование прав администратора, хотя выдача прав администратором ничего не меняет: право фиксируется при выписке ключа. Теперь он приходит как 403 BOX_WEBHOOK_NOT_DEVELOPER_KEY — тем же кодом, что уже отдают разделы кабинета, и до проверки подписки.
Влияние на интеграторов
Клиент, который ветвился на 502 для этих причин, теперь получает 4xx — обработку ошибок стоит перевести на коды. Новые коды перечислены в placements.bindPrerequisite.errorCodes у GET /v1/me, причём ровно те, что аккаунт реально может получить.
Затронутые эндпоинты: POST /v1/placements/bind, GET /v1/me
FIX-0805-23: личный ключ без вебхука Битрикс24 объясняет, чего ему не хватает
Было
Личный ключ (vibe_api_*) без вебхука Битрикс24 отвечал на каждый вызов сущности
401 TOKEN_MISSING с текстом «API key has no OAuth tokens configured. Key may need
re-authorization.» У такого ключа OAuth нет в принципе — он ходит в портал по вебхуку,
поэтому совет про повторную авторизацию вёл не туда. GET /v1/me при этом отвечал 200
и выглядел здоровым, а список ключей не отличал рабочий ключ от нерабочего.
Стало
Текст для личного ключа называет отсутствующий вебхук и адресует за причиной в /v1/me.
Код ответа не изменился (TOKEN_MISSING), появилось необязательное поле error.details
с машиночитаемой причиной: B24_MARKET_SUBSCRIPTION_REQUIRED, B24_MARKET_TRIAL_USED,
INT_TARIFF_REQUIRED, VIBE_SCOPES_ONLY или WEBHOOK_NOT_CONFIGURED — плюс paywallCode
и upgradeUrl, когда причина тарифная. details приходит на /v1/{сущность} и POST /v1/batch.
GET /v1/me для личного ключа несёт блок b24Credentials — ready, а при ready: false
ещё reason, paywallCode, upgradeUrl и подсказку hint, когда состояние доступа стоит
перечитать. Список и карточка ключа (GET /v1/keys, GET /v1/keys/{id}) отдают признак
b24Ready: true — на ключе есть креды для вызовов портала, false — их нет, null — к
ключу неприменимо (ключ авторизации или управленческий ключ). Секреты в ответах не появились.
FIX-0805-24: субдомен приложения отвечает машине JSON, а не страницей, и переживает короткий разрыв туннеля
Было
Пока сервер просыпался или его туннель переподключался, любой запрос к субдомену приложения получал HTML-страницу пробуждения со статусом 503. Браузер её опрашивал и в итоге попадал в приложение, а вебхук или интеграция получали разметку вместо ответа: тело, метод и путь запроса отбрасывались, и понять по ответу, применилось ли действие, было нельзя. Событие Битрикс24, пришедшее в это окно, терялось целиком.
Стало
Вызывающий, который не браузер (есть Authorization, X-Api-Key, Accept: application/json, X-Requested-With, Sec-Fetch-Dest: empty, либо это POST/PUT/PATCH/DELETE), получает обычный конверт ошибки с кодом и заголовком Retry-After: BH_SERVER_WAKING (503), BH_TUNNEL_CONNECTING (503), BH_TUNNEL_DISCONNECTED (502), BH_APP_STARTING (503), BH_SERVER_ERROR (500), BH_SERVER_NOT_FOUND (404), BH_WAKE_BLOCKED (402). У первых четырёх заголовок Retry-After и поле error.retryAfter совпадают.
Кроме того, короткий разрыв туннеля на уже работающем сервере теперь переживается незаметно: запрос удерживается до 15 секунд, и если туннель успевает вернуться, он доставляется в приложение и вызывающий получает настоящий ответ. Холодный старт в это окно не укладывается — там по-прежнему нужен повтор со стороны вызывающего.
Браузерные страницы пробуждения, запуска и ошибок не изменились, включая их опрос. Опрос страницы (?_bh_poll=) не затронут.
FIX-0805-25: лимит на создание обращения считается по вашему ключу, а не общий на всех
Было
POST /v1/feedback отвечал 429 RATE_LIMITED, даже когда ваш ключ отправил меньше пяти обращений в минуту: счётчик был общим для всех вызывающих сразу, поэтому чужой поток обращений исчерпывал ваш лимит. Наблюдалось это как редкий необъяснимый отказ на первом же вызове.
Стало
Счётчик ведётся отдельно для каждого ключа авторизации: чужой поток обращений ваш лимит больше не расходует.
Влияние на интеграторов
Менять ничего не нужно. Отказы, вызванные чужим потоком обращений, на этом методе исчезают. Пороговое число оставьте прежним: обработку 429 RATE_LIMITED с повтором по заголовку Retry-After стоит сохранить — она по-прежнему единственный надёжный способ узнать свой лимит.
FIX-0805-26: пагинация конфигураций открытых линий: окно больше не смещается дважды
Было
GET /v1/openline-configs и POST /v1/openline-configs/search применяли limit и offset дважды: сначала их учитывал Битрикс24, затем обёртка повторно вырезала окно из уже готовой страницы. Клиент получал пустую или сдвинутую выборку без ошибки: при limit=3&offset=3 ответ приходил пустым, при offset=2 первая запись оказывалась четвёртой, а не третьей. Поле hasMore считалось по той же урезанной странице и на полной странице всегда приходило false, из-за чего обход страниц завершался на первой.
Отдельно: дробное значение limit меньше единицы (например limit=0.5) обнулялось при округлении, и нижележащий метод трактовал нулевой лимит как «без ограничения». Ответ приходил пустым с hasMore: true — обход по этому признаку не завершался никогда.
Стало
Окно вырезает Битрикс24, обёртка его больше не двигает. Запрос уходит на одну запись шире запрошенного лимита — по её наличию и определяется hasMore; на потолке limit=200 признак выводится из того, что страница пришла заполненной целиком, поэтому за последней полной страницей возможен один пустой ответ. Дробный limit меньше единицы приводится к значению по умолчанию 50, как это уже происходило с limit=0 и нечисловыми значениями.
Влияние на интеграторов
Менять код не нужно. Обход страниц по offset и hasMore начинает возвращать полную выборку — прежде часть записей терялась молча. Семантика total не изменилась: это по-прежнему число записей в текущем окне, а не во всей выборке, и цикл постраничного чтения ограничивают по hasMore.
BC-0805-27: агрегат по крупной воронке: счётчики по стадиям без выгрузки сделок, отказ вместо усечения
Поддержка старого формата до: 05.02.2027
Оба изменения выкатываются выключенными и включаются платформенным администратором по порталам.
Было
POST /v1/{entity}/aggregate, которому для ответа нужны строки (числовые операции
и/или groupBy), при total > 5000 всё равно выгружал первые 5000 записей и помечал
ответ meta.truncated: true. На крупной воронке эта выгрузка не успевала — клиент ждал
двадцать секунд и получал обрыв связи вместо ответа.
Стало
С включённым режимом отказа такой запрос сразу отвечает 422 AGGREGATION_LIMIT_EXCEEDED
и не выгружает ни одной строки. В тексте ошибки — что делать: сузить фильтр, запросить
только количество, либо (для сделок) взять количество с группировкой по стадии, которое
считается без чтения строк.
С включённой группировкой по стадиям POST /v1/deals/aggregate с groupBy: ["stageId"]
или ["stageSemanticId"] и скалярным categoryId в фильтре отвечает на воронке любого
размера: количество по каждой стадии берётся отдельными дешёвыми подсчётами на стороне
портала. В ответе meta.recordsProcessed: 0, meta.truncated: false,
meta.aggregatePath: "fanout" и meta.stageCountDelta — расхождение между общим числом
и суммой по стадиям (0, когда разбиение полное). Числовые операции по стадиям остаются
доступны, пока суммарный размер групп укладывается в 5000.
Что не изменилось: запрос только количества без группировки (aggregate: [{"function": "count", "field": "*"}]) отвечает как раньше — одним подсчётом, на любом объёме; выборки
до 5000 записей обрабатываются как прежде.
2026-08-04
BC-0804-1: V1 /wake и /start будят galaxy-приложение через хост
Поддержка старого формата до: 22.08.2026
Было
POST /v1/infra/servers/:id/wake и /start для kind=GALAXY_APP отвечали 422 VM_MISSING (у приложения нет cloud VM) и советовали редеплой, даже когда контейнер просто спал после idle. Периодические задачи после первого сна не могли подняться через API.
Стало
Для размещённого galaxy-приложения оба verb'а вызывают host-mediated wake (как кабинет): будят общий хост при необходимости и стартуют контейнер. Успех — HTTP 200. ?wait=true на galaxy не ждёт RUNNING до таймаута / WAKE_TIMEOUT — после cold wake приложение может остаться SLEEPING; опрашивайте GET. Хост с preventWake блокирует и /wake, и /start (403 SERVER_WAKE_BLOCKED или 402 paywall-коды MARKETPLACE_REQUIRED / … — не маркеры TRIAL_EXPIRED). Слот без host/galaxyId → 404 GALAXY_HOST_NOT_FOUND (не 422 VM_MISSING). Для STANDALONE VM_MISSING / override /start без изменений.
BC-0804-2: тип архива при сохранении версии сверяется с его первыми байтами
Поддержка старого формата до: 03.08.2026
Было
Сохранение версии исходников — POST /v1/infra/servers/:id/sources и POST /v1/apps/:id/sources — верило заголовку Content-Type на слово. Архив zip, отправленный с Content-Type: application/gzip, принимался, сохранялся как gzip и получал имя с расширением .tar.gz; выкладка такой версии на сервер падала на распаковке с невнятной ошибкой чтения архива. Автосохранение версии после выкладки записывало формат как gzip всегда, независимо от того, что было в теле запроса.
Стало
При приёме читаются первые байты архива. Прямое противоречие между заявленным типом и содержимым — заявлен application/gzip, а байты zip, или наоборот — отвергается с кодом 415 UNSUPPORTED_ARCHIVE_FORMAT; тело ответа содержит error.hint.declared и error.hint.detected. Типы application/x-tar и application/octet-stream под этот отказ не попадают никогда: их сигнатура не читается в первых восьми байтах, поэтому противоречие с ними установить нечем. Нераспознанное содержимое принимается как раньше.
Автосохранение после выкладки отказа не даёт вообще: там тип архива клиент не заявляет, и распознанные байты просто записываются честно — zip сохраняется как zip и при повторной выкладке уходит в нужный распаковщик.
Что делать интеграторам
Отправляйте Content-Type, соответствующий архиву (application/gzip для tar.gz, application/zip для zip), либо application/octet-stream, если тип неизвестен. Клиенты, которые уже отправляют корректный заголовок, ничего не меняют.
NEW-0804-3: названия и описания полей у разделов товаров, шаблонов реквизитов и банковских реквизитов
Что нового
GET /v1/product-sections/fields, GET /v1/requisite-presets/fields и GET /v1/bank-details/fields возвращали только type и readonly — ни одно поле не имело человекочитаемого названия, поэтому по справочнику нельзя было понять, что означает, например, rqAccNum. Теперь названия объявлены у всех полей: 7 у разделов товаров, 11 у шаблонов реквизитов, 34 у банковских реквизитов. У шаблонов реквизитов и банковских реквизитов вместе с названием приходит и описание.
Ответ дополняется ключами label и description рядом с уже существующими type и readonly — прежние поля ответа не изменились.
Динамический справочник Битрикс24 эти названия дать не мог: он дополняет только те поля, которых нет в статической схеме, поэтому у объявленных полей его подписи отбрасывались. Операция aggregate у банковских реквизитов остаётся отключённой.
NEW-0804-4: `limit=0` больше не игнорируется молча
Что нового
limit=0 не является размером страницы: параметр отбрасывается, и применяется значение по умолчанию. Раньше это происходило без единого признака — ответ приходил с кодом 200 и полной страницей записей, как будто параметр учли. Теперь в такой ответ добавляется предупреждение в meta.warnings:
{
"meta": {
"warnings": [
{
"code": "LIMIT_ZERO_IGNORED",
"field": "limit",
"message": "limit=0 is not a page size and was ignored. Valid range: 1..5000; pass an explicit limit (e.g. 5000) to read the whole collection."
}
]
}
}
Работает на GET /v1/{entity} и POST /v1/{entity}/search для всех сущностей. Само значение не изменилось — прежние вызовы продолжают возвращать столько же записей, сколько и раньше; чтобы прочитать всю коллекцию, передайте явный limit (максимум 5000).
FIX-0804-5: банковские реквизиты: `entityTypeId` помечен как невозвращаемое поле
Было
GET /v1/bank-details/fields описывал entityTypeId обычным числовым полем — читаемым и записываемым. Про то, что Битрикс24 это значение принимает при создании, но никогда не возвращает при чтении, было сказано только в тексте описания поля. Клиент, который строит модель по машиночитаемому справочнику, а не по прозе, добавлял поле в тип чтения, а на месте числа получал пустоту.
Стало
У поля появился признак notReturned: true — в GET /v1/bank-details/fields, в GET /v1/guide и в OpenAPI-схеме (там это аннотация x-notReturned и фраза в описании). Тот же признак этот выпуск ввёл для serverName у телефонных линий.
Поле остаётся доступным для записи: POST /v1/bank-details по-прежнему принимает entityTypeId (всегда 8 — владелец-реквизит). Признак говорит только про чтение.
Влияние на интеграторов
Ничего менять не нужно — признак аддитивен. Если вы генерируете типы по справочнику, entityTypeId можно исключить из модели чтения и оставить в модели создания.
FIX-0804-6: discover называет настоящий идентификатор в пути
Было
GET /v1/guide и OpenAPI-схема показывали путь одиночной записи как /:id у всех сущностей. У четырёх это неправда: smart-processes адресуется публичным entityTypeId (1030+), telephony-lines — номером линии, а bizproc-robots и bizproc-activities — кодом (code), причём собственного id у них нет вовсе. Клиент, прочитавший {id}, подставлял собственное поле id записи и получал 404 SMART_PROCESS_NOT_FOUND, где то же значение названо entityTypeId — притом что опубликованная документация уже писала :entityTypeId и :code.
Стало
GET /v1/guide показывает /v1/smart-processes/:entityTypeId, /v1/telephony-lines/:number, /v1/bizproc-robots/:code и /v1/bizproc-activities/:code; у остальных сущностей путь остался /:id. В OpenAPI имя параметра осталось id: имя path-параметра обязано совпадать с плейсхолдером в шаблоне пути, а сам адрес не менялся — вместо этого описание параметра теперь называет настоящий идентификатор. Там, где у сущности нет собственного поля id (телефонные линии и оба bizproc-справочника), описание так и говорит, а не отправляет сверяться с полем, которого не существует. Описание поля id у smart-processes тоже уточнено.
Влияние на интеграторов
Ничего менять не нужно: маршруты и коды ответов не изменились, изменились только описания. Если вы подставляли в путь smart-processes внутренний id записи, теперь понятно, почему приходил 404 — используйте entityTypeId; для роботов и действий бизнес-процессов — code.
FIX-0804-7: телефонные линии: `name` объявлен nullable, `serverName` помечен как невозвращаемый
Было
GET /v1/telephony-lines/fields объявлял name обычной строкой, хотя у линии без названия приходит null — модель, построенная по справочнику, ломалась на первом же таком значении. Поле serverName выглядело в справочнике обычным читаемым полем, хотя Битрикс24 его не хранит и никогда не возвращает.
Стало
У name появился признак nullable: true — и в GET /v1/telephony-lines/fields, и в GET /v1/guide, и в OpenAPI-схеме (там это форма type: ["string","null"]). У serverName появился признак notReturned: true в тех же местах, а в OpenAPI — аннотация x-notReturned и фраза в описании. Поле осталось в справочнике намеренно: запись в него по-прежнему отклоняется с 400 READONLY_FIELD, и клиент должен иметь возможность найти поле и прочитать причину.
Влияние на интеграторов
Ничего менять не нужно — признаки аддитивны. Если вы генерируете типы по справочнику, name станет string | null, а serverName можно исключить из модели чтения.
FIX-0804-8: рабочие группы: работают `limit > 50` и точное смещение
Было
GET /v1/workgroups?limit=500 возвращал первые 50 записей независимо от того, сколько групп доступно, и meta.hasMore не помогал дочитать остальное. Причина — метод списка у этой сущности называется не .list, а sonet_group.get, и признак «это список» у неё не был объявлен, поэтому ни limit, ни авто-пагинация до Битрикс24 не доходили. Смещение при этом округлялось вниз до границы страницы: offset=30 отдавал записи с первой, а не с тридцать первой.
Стало
limit доходит до Битрикс24, и при limit > 50 платформа сама читает нужное число страниц — как это давно работает у пользователей, отделов и хранилищ. Смещение стало точным: offset=30 начинает выдачу с 31-й записи. То же поведение на POST /v1/workgroups/search и в списочном подзапросе POST /v1/batch.
Влияние на интеграторов
Если вы листали рабочие группы вручную и компенсировали округление смещения на своей стороне (например, отбрасывали первые записи страницы), эту компенсацию нужно убрать — иначе записи будут пропускаться дважды. Для глубокого листания надёжнее курсор: фильтр {">id": последнийId} с сортировкой по id.
FIX-0804-9: удаление приложения доводит снятие регистрации с портала до конца
Было
DELETE /v1/apps/:id снимал приложение с портала одной попыткой. Если портал был недоступен, права отозвали или у автора приложения не оказалось ключа разработчика, попытка молча пропадала: приложение удалялось у нас, но оставалось установленным на портале Битрикс24 — его пункт оставался в меню. Встройки при этом отвязывались только у опубликованных в каталоге приложений и только на облачных порталах.
Стало
Исход попытки сохраняется, и незакрытое снятие повторяется в фоне с нарастающей паузой, пока портал не подтвердит удаление. Встройки снимаются у любого приложения независимо от того, публиковалось ли оно в каталоге.
Влияние на интеграторов
Ответ эндпоинта не изменился — по-прежнему 204 сразу после удаления на нашей стороне.
Изменился результат: пункт приложения на портале теперь исчезает и в тех случаях, когда
раньше оставался навсегда.
NEW-0804-10: Отказ по таймауту шлюза на выполнении команды несёт подсказку по восстановлению
Отказ с кодом GATEWAY_TIMEOUT у POST /v1/infra/servers/:id/exec теперь дополнен объектом hint с полями reason, recovery и recoveryAction — так же, как это уже сделано для EXEC_TIMEOUT и агентского EXEC_BUSY. Подсказка говорит главное: код возврата не получен, поэтому исход команды неизвестен и она может продолжать выполняться на сервере. Повторный запуск вслепую способен создать вторую копию поверх первой, поэтому сначала стоит выяснить фактическое состояние — прочитать логи или выполнить короткую read-only команду. Для работы, которая заведомо не укладывается в предельное время, подсказка направляет на фоновую задачу.
Поля code и message не изменились — подсказка добавлена аддитивно, и прежние вызовы продолжают работать. Приходит в обоих режимах ответа: в JSON-конверте и SSE-событием error.
FIX-0804-11: отказ при недоступной модели — 429 с выдержкой вместо 502
Было
Когда доступ к моделям временно закрывался из-за череды неудачных вызовов, POST /v1/chat/completions и POST /v1/embeddings отвечали 502 с кодом AI_PROVIDER_UNAVAILABLE, а в error.message приходила внутренняя служебная строка вместо объяснения. Заголовка Retry-After не было, поэтому клиенту нечего было ждать — типовая реакция библиотеки на 5xx — повторить сразу, что продлевало недоступность.
Стало
Тот же отказ приходит как 429, error.type: "rate_limit_exceeded", error.code: "ai_provider_cooldown" (в потоковом кадре — то же имя в верхнем регистре, как у остальных кодов этого семейства), с заголовком Retry-After в секундах; в потоковом ответе то же значение приходит полем retryAfter внутри терминального кадра ошибки. Значение — остаток окна ожидания, не меньше секунды. error.message больше не содержит служебных строк и адресов. Отказ временный: дождитесь Retry-After и повторите тот же запрос.
FIX-0804-12: деплой архива формой multipart сохраняет версию исходников даже при неудачном деплое
Было
POST /v1/infra/servers/:id/deploy с телом multipart/form-data сохранял архив в хранилище исходников только после успешного деплоя. Если деплой падал, версия не появлялась вовсе — разбирать было нечего, а повторная выкладка требовала заново отправить те же байты.
Стало
Архив помещается в хранилище исходников до старта деплоя, и деплой продолжается по ссылке на эту версию. Версия остаётся в истории при любом исходе: при успехе она получает deployStatus: "success", при неудаче — "failed". Блок data.source в ответе не изменился: autoSaved, savedVersionId, sha256 и newVersion заполняются как раньше, повторная отправка тех же байт по-прежнему дедуплицируется и новой версии не создаёт.
Появился новый код отказа SOURCE_DEPOT_UNAVAILABLE (502): хранилище исходников не приняло архив, деплой не стартовал, запрос можно повторить без изменений. Раньше такой сбой приходил как VALIDATION_ERROR (400), то есть выглядел ошибкой запроса.
Влияние на интеграторов
Действий не требуется. Список версий (GET /v1/infra/servers/:id/sources) у клиентов, деплоящих формой multipart, теперь может содержать версии неудавшихся выкладок — они помечены deployStatus: "failed".
NEW-0804-13: поля адресов приходят с человекочитаемыми подписями и описаниями
Раньше GET /v1/addresses/fields у шести полей из четырнадцати отдавал в title имя поля Битрикс24 — TYPE_ID, ENTITY_TYPE_ID, ENTITY_ID, COUNTRY_CODE, ANCHOR_TYPE_ID, ANCHOR_ID. Показать такую подпись пользователю нельзя, а значения кодов приходилось искать в документации.
Теперь у этих шести полей в title приходит подпись на языке портала, а у полей, которым есть что добавить к подписи, появился ключ description с назначением поля и расшифровкой кодов: типы адреса (все двенадцать, с 1 по 12 — какие из них доступны, зависит от страновой зоны портала) и типы владельца (1 — лид, 3 — контакт, 4 — компания, 8 — реквизит). У поля countryCode описание не обещает формат: в документации REST Битрикс24 это поле помечено как неиспользуемое и оставленное для обратной совместимости. У кода 1 расшифровка дополнена подписью из англоязычного интерфейса Битрикс24 — Street address: сам Битрикс24 называет этот тип по-разному в русской и английской версиях, и без оговорки словарь расходился бы с подписью, которую пользователь видит в интерфейсе.
Подписи, которые Битрикс24 отдаёт сам, не изменились. Рядом с title та же подпись теперь приходит и в ключе label — так же, как у остальных сущностей, поэтому читать подписи можно одним способом на любой сущности. Ключи description и label добавляются к описанию поля, прежние ключи остаются на месте, поэтому менять ничего не нужно.
Заодно исправлены расшифровки кодов типа адреса в документации операций создания, чтения, изменения и удаления адреса: там были указаны неверные значения, а код 13 не существует вовсе — набор типов заканчивается на 12, и какие из них доступны, зависит от страновой зоны портала.
NEW-0804-14: поля товаров приходят с подписями, описаниями и словарём формата описания
GET /v1/products/fields описывал двадцать одно поле товара только типом и признаком «только для чтения» — ни подписи, ни описания. Понять по такому ответу, что measure это единица измерения, а vatId — ставка НДС, было нельзя.
Теперь каждое из двадцати одного поля несёт label на языке портала, а поля, у которых есть что добавить к подписи, — ещё и description: где взять список допустимых значений (GET /v1/currencies, GET /v1/product-sections, GET /v1/catalogs, GET /v1/users), как работает сортировка и что задаёт формат описания. У поля descriptionType появился словарь enum со значениями text и html.
Ключи добавляются к описанию поля, прежние type и readonly не меняются, поэтому менять ничего не нужно. Свойства каталога PROPERTY_<N> по-прежнему приходят с подписью из настроек портала.
NEW-0804-15: все 45 полей предложения приходят с подписью и описанием
GET /v1/quotes/fields отдавал подпись у тридцати полей из сорока пяти. Пятнадцать оставшихся — ровно базовые: id, title, dealId, contactId, companyId, amount, currency, assignedById, createdBy, comments, isManualOpportunity, beginDate, closeDate, createdTime, updatedTime — описывались только типом и признаком «только для чтения». Описания (description) не было ни у одного поля.
Теперь подпись есть у всех сорока пяти полей, и у каждого появилось описание: назначение поля, где взять список допустимых значений (GET /v1/currencies, GET /v1/users, GET /v1/deals и другие), поведение при записи. Отдельно названы расхождения имён, на которых легко ошибиться: сумма в Битрикс24 называется opportunity, валюта — currencyId, а даты начала и закрытия — begindate и closedate целиком в нижнем регистре.
У поля stageId словаря значений нет намеренно: набор стадий настраивается на портале, поэтому в описании стоит ссылка на справочник GET /v1/statuses?filter[entityId]=QUOTE_STATUS — статический словарь устарел бы.
Ключи добавляются к описанию поля, прежние type и readonly не меняются, поэтому менять ничего не нужно.
NEW-0804-16: поля товаров каталога приходят с подписями и описаниями
GET /v1/catalog-products/fields описывал все сорок два поля товара каталога только типом и служебными признаками — ни подписи, ни описания. Понять по такому ответу, чем purchasingCurrency отличается от валюты цены, а quantityTrace — от canBuyZero, было нельзя.
Теперь каждое из сорока двух полей несёт label на языке портала, а тридцать восемь полей — ещё и description. В описаниях названо то, что раньше приходилось выяснять на практике: iblockId задаётся только при создании и не даёт перенести товар между каталогами; iblockSection принимается только при записи, а на чтении основной раздел приходит скаляром iblockSectionId; available и bundle вычисляет Битрикс24; recurSchemeLength, recurSchemeType и trialPriceId работают только в коробочной версии Битрикс24 при продаже контента. Где значение берётся из справочника, указан эндпоинт — GET /v1/catalogs, GET /v1/catalog-sections, GET /v1/currencies, GET /v1/users.
У полей previewTextType и detailTextType набор значений приходит машиночитаемым словарём enum (text и html) — так же, как у формата описания товара, а не прозой внутри описания.
Ключи добавляются к описанию поля, прежние type, readonly, createOnly и nullable не меняются, поэтому менять ничего не нужно.
FIX-0804-17: схема полей шаблона реквизитов объявляет inShortList булевым
Было
GET /v1/requisite-presets/:presetId/fields/schema описывал поле inShortList типом char, хотя чтение строк того же шаблона отдаёт true/false, а запись принимает true/false. Схема противоречила данным, которые она описывает, и клиент, полагавшийся на объявленный тип, готовился разбирать односимвольную строку.
Стало
В том же ответе inShortList.type приходит как boolean. Остальные ключи описания поля (isRequired, isReadOnly, title и прочие) не изменились, типы остальных полей — тоже.
Влияние на интеграторов
Менять ничего не нужно: данные и раньше приходили булевыми. Проверка, сравнивающая inShortList.type со строкой char, перестанет совпадать — сверяйте с boolean.
NEW-0804-18: скачивание записей звонков и вложений таймлайна
Появились два эндпоинта для файлов CRM, до которых нельзя было добраться через скачивание файла Диска.
GET /v1/activities/:activityId/files/:fileId/download отдаёт файл дела, в том числе запись звонка. До этого сделать это через API было нельзя: файл дела не является объектом Диска, его идентификатор живёт в отдельном пространстве, пересекающемся с идентификаторами Диска, а ссылка из ответа дела приходит с пустым параметром авторизации — запрос по ней возвращает страницу входа с кодом 200. Эндпоинт добавляет авторизацию сам, проверяет, что файл действительно принадлежит названному делу, и отдаёт поток байтов.
GET /v1/timelines/:commentId/files/:fileRef/download отдаёт вложение комментария таймлайна. В fileRef принимается любой из двух идентификаторов: ID привязки, который показывает интерфейс портала, и ID объекта Диска — ключ объекта в поле files ответа комментария. Первый считает доступ через сам комментарий, поэтому дотягивается до вложений, которые скачивание файла Диска отдавать отказывается; второй идёт через личные права на Диске. Сначала читается сам комментарий, затем пробуется ID объекта Диска, и только если его нет в списке файлов комментария — ID привязки; указывать выбор не нужно.
Оба эндпоинта требуют скоуп crm и никогда не возвращают адрес для скачивания: в нём содержится код авторизации, поэтому наружу уходит только содержимое. Файл, принадлежность которого названному делу или комментарию не подтверждается, получает 404 — это касается и вложения, которое висит на записи другого типа, например на задаче с тем же номером, — без этой проверки эндпоинт позволял бы перебирать файлы портала, поскольку сам Битрикс24 в таком случае отвечает страницей под кодом 200, а не отказом.
Проверка адреса распространяется и на перенаправления: эндпоинт проходит их сам, сверяя каждый шаг, ограничивает цепочку по длине и по времени и не идёт по перенаправлению на внутренний адрес — такой ответ становится 502. Это же касается скачивания файла Диска, которое пользуется той же обвязкой.
404 на скачивании вложения означает именно неверную ссылку. Временная причина — лимит запросов Битрикс24, недоступность портала, сработавшая защита от повторяющихся ошибок — приходит как она есть: 429 либо 502/503 с заголовком Retry-After. Разница практическая: по 404 повторять запрос бессмысленно, по 429/5xx — нужно, с задержкой. Срок в Retry-After считается по тому вызову Битрикс24, который действительно упёрся в лимит.
Контракт скачивания файла Диска не менялся: те же параметры, те же ответы. Общей обвязкой оно унаследовало только проверку адреса и перенаправлений, описанную выше.
FIX-0804-19: сообщение бота без текста отбивается понятной ошибкой, а не мнимым успехом
Было
POST /v1/bots/:botId/messages с текстом в неузнанном поле — например {"dialogId": "…", "text": "привет"} — уходил в Битрикс24 без содержимого, и тот отвечал 422 с кодом EMPTY_MESSAGE и текстом «Message can't be empty». Понять из такого ответа, что дело в имени поля, было нельзя: текст-то передан.
Хуже вело себя обновление. PATCH /v1/bots/:botId/messages/:messageId в той же ситуации отвечал 200 {"result": true}, а текст сообщения не менялся — мнимый успех, после которого интегратор считал правку применённой.
Стало
Оба запроса проверяют содержимое до обращения к Битрикс24 и при его отсутствии отвечают 400 с кодом MESSAGE_REQUIRED. В тексте ошибки перечислены нераспознанные ключи тела и указано, что текст сообщения кладётся в поле message. Пустой массив в attach и пустая строка в message содержимым не считаются; блок attach без текста — считается, как и число (0 это текст «0»).
Обёртка fields — способ передать тело в родном виде Битрикс24, и «обёртка передана» теперь понимается одинаково на всех шагах обработки. Значение, обёрткой не являющееся (false, 0, пустой массив), обёрткой и не считается: {"dialogId": "…", "fields": false} получает тот же 400 с кодом MESSAGE_REQUIRED, а {"message": "привет", "fields": []} отправляет текст, а не теряет его.
Это тот же код и та же формулировка, что у соседнего POST /v1/chats/:dialogId/messages: один контракт на одинаковую ошибку в двух родственных эндпоинтах.
Влияние на интеграторов
Запрос с текстом в поле message работает как раньше. Запрос, который раньше получал 422 EMPTY_MESSAGE, теперь получает 400 MESSAGE_REQUIRED с указанием, что исправить. Поле text синонимом message не становится: на чтении содержимое сообщения действительно называется text, но принимать оба имени молча значило бы развести контракт с эндпоинтом чатов.
2026-08-03
FIX-0803-1: переоткрытие архивированного тикета тоже очищает штамп решения
Было
Переоткрытие тикета из статуса ARCHIVED в активный (NEW/REVIEWING/AWAITING_USER/NEEDS_REVIEW) — через PATCH /v1/feedback/:id или POST /v1/feedback/:id/comments — не сбрасывало resolvedAt/resolvedBy, если тикет ранее был решён и затем заархивирован. На чтении (GET /v1/feedback/:id) такой тикет выглядел активным и решённым разом. Сброс срабатывал только для источников RESOLVED/WITHDRAWN.
Стало
ARCHIVED присоединён к RESOLVED/WITHDRAWN: переоткрытие из любого закрытого статуса в активный очищает resolvedAt/resolvedBy. На PATCH-пути очищается и resolution (в том числе причина архива) — активный тикет решения не несёт; явный resolution в том же запросе имеет приоритет. На пути комментария resolution равен телу коммента. Переход в ARCHIVED штамп по-прежнему сохраняет (архивирование хранит историю решения).
FIX-0803-2: пропавшие байты вложения отдаются как 404, а не как оборванный ответ
GET /v1/feedback/{id}/attachments/{attId}/file и .../thumb теперь проверяют наличие байтов до отправки заголовков. Если запись о вложении есть, а самих байтов в хранилище нет (последствие ручной уборки или каскадного удаления), ответом будет обычный 404 NOT_FOUND.
Было
Ответ начинался как 200, а затем обрывался посреди тела: клиент получал усечённую картинку или пустой поток с уже отправленным успешным статусом, и отличить это от медленной сети было нечем.
Стало
404 { "success": false, "error": { "code": "NOT_FOUND", "message": "Not found" } } — тот же код, что и для чужого или удалённого вложения. Клиентам, которые уже обрабатывают 404 на этих маршрутах, менять ничего не нужно.
Изменение сопровождает перенос файлов вложений в объектное хранилище: раздача вложений больше не зависит от того, какая именно машина приняла загрузку. Формат ответов и адреса маршрутов не изменились.
FIX-0803-3: создание сервера честно сообщает, что вернуло уже существующий сервер приложения
Если ключ вызова принадлежит приложению, у которого слот сервера уже занят, POST /v1/infra/servers возвращает этот сервер вместо создания нового. Так было и раньше, но узнать об этом из ответа было почти нечем: единственным признаком было недокументированное поле reused, а имя в ответе принадлежало существующему серверу, а не запрошенному.
Теперь такой ответ несёт полное раскрытие: data.reusedReason со значением APPLICATION_ALREADY_HAS_SERVER, data.requestedName с эхом переданного вами name (всегда, даже если оно совпадает с именем существующего сервера) и массив warnings рядом с data минимум с одной записью — она называет существующий сервер и предупреждает, что деплой заменит работающий на нём код. Поля reused и deploying теперь описаны в схеме и в документации.
Точно так же раскрывается и вторая молчаливая потеря: переданные displayName и description переиспользование не применяет — сервер сохраняет собственные имя и описание. Теперь это видно по data.metaIgnored и отдельному предупреждению; переименовать сервер можно осознанно через PATCH /v1/infra/servers/:id.
Если в запросе был передан source, а развернуть его в переиспользуемый сервер нельзя (у galaxy-приложения уже есть работающий контейнер, либо это отдельная виртуальная машина), архив отбрасывается — и теперь ответ говорит об этом полем data.sourceIgnored и отдельным предупреждением. Раньше архив отбрасывался молча.
data.next в reuse-ответе теперь приходит только тогда, когда перезаписывать нечего. Раньше поле приходило на любом двухшаговом reuse — в том числе когда возвращённый сервер уже выполнял чужой код, и машинное «следующий шаг — деплой» противоречило подсказке в том же теле. Если сервер может выполнять код, next отсутствует намеренно: сначала убедитесь, что сервер тот. Отсутствие next — не ошибка.
Два прежних поля изменились по смыслу, хотя менять клиентский код не нужно: data.hint на reuse-ответе переписан целиком — вместо «задеплойте сюда» он теперь начинается с REUSED — no new server was created и объясняет, чем это чревато; а описание data.next в схеме исправлено — раньше оно называло единственной причиной появления поля пустой galaxy-слот, хотя next приходит и на reuse-ответе. Прежние поля и коды не изменились, менять ничего не нужно. Но перед вызовом POST /v1/infra/servers/:id/deploy читайте reused: деплой заменяет то, что уже работает на сервере.
NEW-0803-4: справочник значений списочных свойств каталога
Появилась сущность catalog-product-property-enums — все возможные варианты свойства-списка торгового каталога: GET /v1/catalog-product-property-enums, GET /v1/catalog-product-property-enums/:id, POST /v1/catalog-product-property-enums/search и GET /v1/catalog-product-property-enums/fields. Сущность только для чтения: записывающие операции и агрегация не зарегистрированы и отвечают 404, а data.batch приходит пустым массивом.
Раньше значение списочного свойства у товара можно было получить только как идентификатор варианта: семейство /v1/products отдаёт в PROPERTY_<N> объект с числовым value, а GET /v1/products/fields описывает свойство только именем — развернуть идентификатор в текст было нечем. Теперь список вариантов запрашивается напрямую: GET /v1/catalog-product-property-enums?filter[propertyId]=166&limit=1000 возвращает пары «идентификатор — читаемый текст», по которым строится карта String(id) → value.
Фильтр filter[propertyId] обязателен: справочник читается по одному свойству за раз, запрос без него отклоняется с 400 MISSING_REQUIRED_FILTER до обращения к Битрикс24 — на списке и поиске. Проверка смотрит на наличие ключа и не распространяется на подвызовы пакетного запроса. Перечисление есть только у свойств с propertyType: "L" — у свойства любого другого типа ответ будет пустым списком, а не ошибкой. Пагинация обычная: limit + offset, конец выборки — по meta.hasMore. Потолок — 5000 записей за вызов. Нужен скоуп catalog.
Заодно уточнены существующие страницы. GET /v1/catalog-products/:id теперь показывает захваченный ответ со списочным свойством и разбор тройки value / valueEnum / valueId, а также форму значения при listType: "C" — голый скаляр "Y"/"N", то есть состояние галочки, а не id варианта. GET /v1/products/:id объясняет, что пустое значение свойства означает либо незаполненное свойство, либо свойство, обслуживаемое только каталожным семейством, и как различить эти случаи одним перекрёстным вызовом. GET /v1/products/fields прямо говорит, что дескриптор PROPERTY_<N> несёт только название и куда идти за значениями. В справочнике ошибок исправлен перечень сущностей с обязательным фильтром.
FIX-0803-5: Обращения в поддержку работают при заморозке баланса
Было
На аккаунте, замороженном из-за баланса, все ручки /v1/feedback возвращали 402 ACCOUNT_FROZEN — сообщить о проблеме из продукта было нельзя ровно в тот момент, когда это нужнее всего.
Стало
По ключу доступна вся переписка: POST /v1/feedback (создать), GET /v1/feedback (список), GET /v1/feedback/{id} (открыть обращение) и POST /v1/feedback/{id}/comments (ответить команде). Все четыре читают и пишут только ваши же данные. Запись вложений (POST /v1/feedback/attachments), их скачивание и PATCH /v1/feedback/{id} остаются под гейтом заморозки, как и остальные /v1-эндпоинты.
FIX-0803-6: include возвращает полную связанную запись, а не только метаданные
Было
При ?include=<relation> во вложенном объекте приходили только метаданные связи — без id и полей связанной записи.
Стало
include возвращает полную связанную запись (id + поля), как и описано в контракте — отдельный запрос за связанной сущностью больше не нужен.
FIX-0803-7: методы дашборда Открытых линий в раскатке возвращают METHOD_NOT_YET_AVAILABLE
Было
На портале, куда обновление ещё не приехало, методы дашборда Открытых линий отдавали сырой 422 BITRIX_ERROR — по нему нельзя было отличить «метод раскатывается» от реальной ошибки интеграции.
Стало
Такой ответ распознаётся и возвращается как 422 METHOD_NOT_YET_AVAILABLE с версией релиза — понятный сигнал, что метод пока не доступен на этом портале, а не сбой интеграции. Ответ не меняется и при регулярном опросе: такие вызовы больше не засчитываются в защиту от петли ошибок, поэтому вместо понятного 422 не приходит 429 ERROR_LOOP_DETECTED (для методов, которые не раскатываются, защита работает как раньше).
FIX-0803-8: запись исходников в хранилище возвращает точные коды ошибок вместо общего 500
Было
Клиентские сбои записи в хранилище исходников маскировались общим 500 SOURCE_STORAGE_ERROR — по нему нельзя было понять, что делать.
Стало
Причина различима: при недостатке средств на балансе — 402 BILLING_INSUFFICIENT, при временном сбое выдачи ключей доступа к хранилищу — 503 STORAGE_STS_UNAVAILABLE (можно повторить запрос). Таблица кодов на странице source-storage дополнена.
FIX-0803-9: список значений в фильтре statuses отклоняется чистым 400, а не 500
Было
GET /v1/statuses со списком значений в фильтре ({поле: {$in: [...]}} или массив) уходил в Bitrix24, и метод справочника отвечал по-разному в зависимости от поля: по id и name — внутренней ошибкой, которая доезжала до клиента как 502 BITRIX_UNAVAILABLE, по entityId, statusId, semantics и sort — ошибкой «значение должно быть строкой», а по categoryId — успешным ответом с записями чужой воронки.
Стало
Список значений отклоняется до вызова Bitrix24 с 400 UNSUPPORTED_FILTER по любому полю фильтра: метод справочника не поддерживает его нигде. Принимается одно точное значение ({поле: значение}); несколько значений запрашивайте отдельными вызовами или через POST /v1/batch.
FIX-0803-10: /v1/tasks/:taskId/time принимает ключ со скоупом task
Было
Эндпоинт учёта времени задачи возвращал 403 INSUFFICIENT_SCOPE ключу со скоупом task — работал только tasks, хотя это алиасы одного разрешения.
Стало
task и tasks трактуются как алиасы (как на всех остальных task-эндпоинтах) — ключ с любым из них проходит.
NEW-0803-11: одношаговое создание galaxy-приложения принимает `healthPath`
Тело POST /v1/infra/servers с полем source теперь принимает необязательное поле healthPath — путь, по которому проверяется готовность приложения внутри контейнера. Валидация та же, что у POST /v1/infra/servers/{id}/deploy: строка до 500 символов, начинается со /. Значение по умолчанию — /.
Раньше healthPath был объявлен только в теле деплоя, поэтому одношаговый вызов, который платформа сама рекомендует в GET /v1/me, отвечал 400 UNKNOWN_PARAM — а описание в /v1/me при этом называло healthPath поддерживаемым на галактическом пути. Поле принято именно там, где рекомендация его обещает; на создании отдельного сервера оно игнорируется.
Влияние на интеграторов
Ничего менять не нужно — поле необязательное. Если раньше приходилось разбивать вызов на два шага только ради healthPath, теперь достаточно одного.
FIX-0803-12: `/start` и `/wake` у galaxy-приложения объясняют, почему они не применимы
Было
POST /v1/infra/servers/{id}/start и POST /v1/infra/servers/{id}/wake проверяли статус раньше типа сервера, поэтому galaxy-приложение вне списка разрешённых статусов (например RUNNING, ERROR или STOPPED) получало текст про standalone-сервер: «Server is RUNNING; /start requires one of SLEEPING, ERROR, PROVISIONING». Формально верно и бесполезно: перечисленные статусы тоже не помогли бы, а о том, что у приложения-контейнера вообще нет облачной машины, не говорилось ничего.
Стало
Проверка типа идёт первой — как в /reboot. Для galaxy-приложения в таком статусе message называет причину («это galaxy-приложение, у него нет облачной машины»), а userMessage называет операции, которые действительно работают: POST /v1/infra/servers/{id}/deploy (годится в любом из этих статусов) и POST /v1/infra/servers/{id}/reboot (только для запущенного или упавшего приложения). Код ошибки не меняется — по-прежнему SERVER_WRONG_STATE (422) с полями currentState и availableActions.
Влияние на интеграторов
Ничего менять не нужно: HTTP-статус и код те же, изменился только текст. Ответ для статусов ИЗ списка разрешённых остался прежним побайтово — в частности, /start и /wake для спящего galaxy-приложения по-прежнему отвечают задокументированным VM_MISSING.
FIX-0803-13: причина падения galaxy-приложения называется прямо в `provisionError`
Было
Когда приложение собиралось, запускалось и тут же падало, provisionError содержал только обобщённый вывод: «приложение не удержалось, смотрите логи» или, если сработал признак нехватки памяти, «вероятно, превышен лимит памяти галактики». Настоящая причина — например TypeError: webidl.util.markAsUncloneable is not a function из-за несовместимой версии среды — лежала в buildLog, а в списке серверов виден только provisionError. Поэтому по короткому тексту нельзя было понять, дело в памяти или в коде, и совет «перейдите на отдельный сервер» отправлял по ложному пути.
Стало
К той же формулировке дописывается строка из логов контейнера: … Actual cause from the container logs: <строка>. Строка выбирается из хвоста, который платформа снимает при отказе: сначала типизированное исключение или код ошибки (TypeError: …, EADDRINUSE, FATAL ERROR: … heap out of memory), затем известные причины сборки, затем последняя строка с признаком ошибки. Хвост проходит ту же очистку, что и buildLog — внутренние пути хоста заменяются на <build-context>. Если полезной строки в хвосте нет, текст остаётся прежним. Формулировка про память сохраняется и дополняется причиной: признак oom иногда срабатывает и там, где память ни при чём, и тогда приклеенная строка — единственная правда, которую видит читатель.
Влияние на интеграторов
Ничего менять не нужно. Прежние подстроки в тексте сохранены, поэтому клиент, который сопоставлял их, продолжает работать; появился только дописанный хвост. Полный лог по-прежнему доступен в buildLog (GET /v1/infra/servers/{id}).
FIX-0803-14: `/reboot` спящего galaxy-приложения запускает починку хоста и говорит об этом
Было
У спящего galaxy-приложения, чей хост потерял туннель, не было пути обратно. Задокументированный способ разбудить приложение — деплой, а деплой на недостижимый хост падает. Починка туннеля хоста при этом уже запускалась из перезапуска приложения, но запуск лежал ЗА проверкой статуса, которая спящее приложение отклоняет, — то есть до починки дело не доходило.
Стало
Перед тем же отказом 422 SERVER_WRONG_STATE платформа запускает фоновую починку туннеля хоста и, если починка действительно началась, добавляет в ответ необязательный объект hint с полями reason, recovery (какую операцию повторить) и retryAfterSeconds (нижняя граница ожидания, не обещание). Если починка не началась — сработал рубильник, туннель на самом деле жив, ремонт уже идёт или машина заблокирована для пробуждения, — hint отсутствует: сообщать о запуске, которого не было, нельзя. То же поведение добавлено в перезапуск из кабинета.
Влияние на интеграторов
Ничего менять не нужно: код и HTTP-статус те же, hint аддитивен. Клиенту, который читает hint, достаточно повторить POST /v1/infra/servers/{id}/deploy через названное время.
FIX-0803-15: zip-архив в исходниках galaxy-приложения больше не падает на распаковке
Было
Платформа определяет формат архива по его первым байтам и заявляет .zip поддерживаемым, но на галактическом пути (POST /v1/infra/servers с полем source и POST /v1/infra/servers/{id}/deploy для galaxy-приложения) архив передавался на распаковку без подготовки хоста. Если распаковщик на хосте отсутствовал, деплой падал с текстом вида exec: "unzip": executable file not found in $PATH — из него не следовало ни что делать, ни что тот же архив в .tar.gz прошёл бы.
Стало
Перед загрузкой zip-архива платформа доустанавливает распаковщик на хосте (тот же шаг уже выполнялся на отдельном сервере). Шаг идемпотентен: если распаковщик уже есть, он ничего не делает, и на повторных деплоях времени не занимает. Если установить не удалось, архив не загружается вообще, а деплой завершается отказом с честной причиной и подсказкой прислать тот же исходник как .tar.gz; текст доступен в buildLog и в provisionError.
Влияние на интеграторов
Ничего менять не нужно. Деплои с .tar.gz идут прежним путём без изменений.
NEW-0803-16: Версия исходников принимает до 500 МБ, а деплой по нашей ссылке линкуется к версии на любом ключе
Было
Архив можно было сохранить версией только до 200 МБ, при том что тот же архив разрешалось передать прямо в теле деплоя до 500 МБ. Крупный проект деплоился единственным способом — целиком в теле запроса.
Отдельно: деплой формы {"source": {"url": "…"}} по ссылке, полученной из GET /v1/infra/servers/:id/sources/:versionId/download, не связывался с версией, если сервер принадлежит персональному ключу vibe_api_*. В ответе приходило data.source.autoSaved: false и skippedReason: "external-url-or-toggles-off", а у версии оставались пустыми linkedDeployId и deployStatus.
Стало
Потолок версии исходников — 500 МБ на обоих приёмных эндпоинтах: POST /v1/infra/servers/:id/sources и POST /v1/apps/:id/sources. Тело по-прежнему принимается потоком, поэтому размер архива не влияет на скорость приёма. Заявленная в Content-Length длина сверх потолка отбивается кодом 413 до чтения тела. Значение публикуется в capabilities.apps.sourceStorage.limits.maxBlobBytes у GET /v1/me — теперь 524288000.
Деплой по нашей ссылке связывается с версией независимо от типа ключа-владельца сервера: ответ несёт data.source.autoSaved: true и savedVersionId, а у версии заполняются linkedDeployId и deployStatus.
Затронутые эндпоинты: POST /v1/infra/servers/:id/sources, POST /v1/apps/:id/sources, POST /v1/infra/servers/:id/deploy, GET /v1/me
FIX-0803-17: отзыв доступа гасит все ключи связки, а не только последний
Было
Когда пользователь повторно проходил согласие для одного и того же приложения, платформа выдавала новый ключ, но прежний оставался рабочим. Отзыв доступа гасил только ключ последней авторизации — прежние ключи продолжали ходить в API, хотя пользователь считал, что доступ закрыт.
Стало
Отзыв доступа гасит все ключи, которые пользователь выдал приложению для этого портала, включая ключи прежних авторизаций. Ключ, переживавший отзыв, теперь отвечает 401 KEY_INACTIVE. Ключи других сотрудников того же портала не затрагиваются. Партнёру достаточно штатной обработки 401 KEY_INACTIVE — предложить пользователю повторно авторизоваться.
2026-08-02
FIX-0802-1: подкоманда batch со своим start=-1 больше не отдаёт выдуманный total
Подкоманда POST /v1/batch со своим params.start: -1 больше не получает выдуманное количество записей. Значение -1 — это инструкция Битрикс24 «не считай коллекцию», поэтому счёта в ответе портала нет; конверт же брал за размер то, что оказалось под рукой — эхо result_total от методов, которые в этом режиме отдают 0 рядом с полной страницей, либо просто длину первой страницы. Затронуты обе ветки: подкоманда с limit до 50 и подкоманда с limit больше 50, которая читается отдельной автопагинацией.
Было
{"entity":"activities","action":"list","params":{"start":-1}} → meta.<id>.total: 0 и data.totals.<id>: 0 рядом с 50 записями, meta.<id>.hasMore: false.
{"entity":"deals","action":"list","params":{"limit":5000,"start":-1}} → 50 записей, meta.<id>.total: 50, meta.<id>.hasMore: false — то есть на запрос 5000 записей приходил уверенный ответ «их всего 50».
Стало
На такой подкоманде ключей total нет ни в meta.<id>, ни в data.totals — счёт не заказывался, и придумывать его нечем. hasMore определяется полнотой страницы: заполненная до запрошенного лимита → true, короткая → false. Полнота считается против того ограничения, которое реально ушло в Битрикс24, поэтому {"limit":10,"start":-1} при 10 записях в ответе — это полная страница, и hasMore там теперь true, а не false. Обход не строит план дочитывания из выдуманного размера и возвращает непрерывный префикс.
Признак «счёт не заказан» читается по нормализованному значению: start приводится к целому числу так же, как это делает Битрикс24 (усечение к нулю, строка читается по числовому префиксу), поэтому -1, "-1", -1.5, "-1abc", "-1 x" — одно и то же. Положительное смещение (start: 100) — обычный счётный курсор, total по нему приходит как раньше.
Значение, которое числом не является вовсе ("abc", пустая строка, null, объект, массив, true), в Битрикс24 больше не уходит: оно читается как непереданное. Страница от этого не меняется — «начать с нуля» и «start не передан» это одна и та же страница, — но такая подкоманда попадает под общий выбор платформы и может прийти без total.
Отдельно: подкоманда, завершившаяся ошибкой, больше не публикует data.totals.<id> рядом с этой ошибкой.
Влияние на интеграторов
Клиент, который передавал start: -1 и читал total, получал заведомо неверное число: у методов формы «эхо result_total: 0» это был ноль, у остальных — длина страницы. Если цикл чтения останавливался по meta.hasMore, он обрывался на первой странице. Проверяйте наличие ключа total (meta.<id>.total !== undefined), а продолжение обхода ведите по meta.<id>.hasMore. Нужен точный счёт — не передавайте start: -1: этим значением подсчёт отменяет сам клиент, поэтому вернуть по такой подкоманде нечего, и withTotal: true его не восстановит.
BC-0802-2: доступ коробочного портала больше не выдаётся безусловно
Поддержка старого формата до: 30.01.2027
Было
Коробочный (self-hosted) портал считался коммерческим всегда: отметка ставилась при подключении портала, а не по факту оплаты. Ни подписка Маркетплейса Битрикс24, ни её окончание на доступ к Вайбкоду не влияли.
Стало
Доступ коробки определяется её подпиской Маркетплейса Битрикс24 — как у облачного портала. Ответы меняются там, где раньше доступ был всегда: POST /v1/infra/servers у портала без действующей подписки отвечает 402 с кодом отказа вместо создания сервера, а capabilities.servers.create в GET /v1/me приходит недоступной, с причиной. Те же правила действуют на создании агентов и ботов, которые заводят сервер.
Платная лицензия Битрикс24 сама по себе доступ к Вайбкоду не даёт: лицензия — про коробку, подписка — про Маркетплейс. Портал с оплаченной коробкой, но без подписки, попадает под отказ.
Пока состояние подписки прочитать не удалось, доступ не ограничивается: отсутствие сигнала не приравнивается к отсутствию подписки.
Отказ в доступе включается отдельным решением, не выкладкой: до включения ответы прежние. Два поля меняются раньше — на выкладке:
wasEverCommercialв GET /v1/me у коробочных порталов перестаёт быть односторонним: у портала, за которым не нашлось ни подписки, ни платежей, значение однократно меняется сtrueнаfalse— прежнее ставилось при подключении, а не по наблюдению.placements.bindPrerequisiteв том же ответе у коробки начинает описывать подписочную модель (другой наборerrorCodes, другойnote) — раньше портал без определённого региона описывался как международный.
Что делать интеграторам
Проверять capabilities.servers.create в GET /v1/me перед созданием инфраструктуры и обрабатывать 402 на создании — код и текст отказа приходят в теле ответа. Если подписка оформлена, а отказ приходит, поможет принудительное обновление: GET /v1/me?refresh=tariff. Не полагаться на монотонность wasEverCommercial для коробочных порталов.
2026-08-01
FIX-0801-1: тело ответа AI-эндпоинтов не несёт служебных полей платформы
Было
В ответах POST /v1/chat/completions на моделях Битрикс24 поле system_fingerprint отдавало служебный идентификатор инфраструктуры платформы. Рядом с объявленными полями приходили и другие служебные поля, которых нет в документации, — как в самом конверте ответа, так и внутри choices и choices[].message. У потоковых ответов и у POST /v1/embeddings эти поля не наблюдались, но проверка там тоже не стояла.
Стало
system_fingerprint отдаётся нейтральным значением vibecode. Если апстрим отпечатка не прислал — поля в ответе нет, как и раньше. Служебные поля убраны из конверта ответа и из объектов choices; внутри choices[].message убран контейнер provider_specific_fields. В служебном событии ошибки внутри потока объект error сохраняет поля message, type, param, code, retryAfter и retryable; служебные поля рядом с ними убраны.
Объявленный контракт не изменился: id, object, created, model, choices, usage у чата и object, data, model, usage у эмбеддингов приходят как прежде — включая расширения провайдера внутри usage, поля рассуждения внутри choices[].message и вызовы инструментов. Служебное событие ошибки внутри потока по-прежнему приходит и по-прежнему несёт error. Клиенту менять ничего не нужно.
Правка касается только тела ответа моделей Битрикс24. В служебном событии ошибки внутри потока текст error.message теперь приводится к публичному имени модели, а если апстрим положил в message не строку — поле не возвращается вовсе. Текст ошибок в обычных ответах платформы (4xx, 5xx) не изменился.
FIX-0801-2: деплой galaxy-приложения больше не сообщает об обрыве после фактически успешного обновления
Было
Если соединение с площадкой обрывалось в середине POST /v1/infra/servers/:id/deploy, платформа перепроверяла состояние приложения один раз и, если ответ на эту проверку не приходил, отдавала 502 GALAXY_DEPLOY_INTERRUPTED. В типичном случае обновление в этот момент ещё шло и завершалось успешно — уже через несколько секунд приложение отвечало на health, работало на новой версии и с новыми переменными окружения. Отличить такой мнимый отказ от настоящего можно было только вручную, повторный вызов деплоя разворачивал ту же версию заново.
Стало
После обрыва платформа перепроверяет состояние приложения не однократно, а в течение ограниченного времени, и если новая версия поднялась — отвечает success, как если бы обрыва не было. 502 GALAXY_DEPLOY_INTERRUPTED остаётся только для случаев, когда за это время подтверждения так и не появилось. Время ожидания подобрано под окно, в течение которого платформа обязана ответить, поэтому длительность запроса в худшем случае не растёт.
Тело этой ошибки дополнительно несёт error.retryable: true — признак, по которому клиент может отличить её от неустранимого отказа, не разбирая текст. Прежние поля (error.code, error.message, error.hint) не изменились.
BC-0801-3: публикация проверяет авторизацию приложения раньше свежести снапшота
Поддержка старого формата до: 30.01.2027
Было
POST /v1/apps/:id/publish сначала проверял свежесть снапшота исходников, и только потом — авторизацию приложения. У вызывающего без авторизации ответ зависел от постороннего условия: при устаревшем снапшоте приходил 409 SNAPSHOT_REQUIRED, при свежем — 400 NO_USER_TOKEN. Чередование читалось как «проверка токена то проходит, то нет», хотя авторизации не было ни в одном из случаев.
Стало
Наличие авторизации проверяется до гейта свежести. Приложение без авторизации получает 400 NO_USER_TOKEN сразу, независимо от состояния снапшота. Последовательность стала монотонной: сначала закрываете авторизацию, дальше остаётся только требование к снапшоту.
Изменение затрагивает случаи, где раньше отвечал не тот код: нет авторизации И снапшот устарел или отсутствует — раньше 409 SNAPSHOT_REQUIRED, теперь 400 NO_USER_TOKEN; нет авторизации И итоговое имя для каталога длиннее лимита — раньше 400 TITLE_TOO_LONG_FOR_CATALOG, теперь 400 NO_USER_TOKEN (статус тот же, код другой). Если авторизация есть, но токен не удалось продлить, ответ по-прежнему 400 и приходит после гейта свежести. Публикация на self-hosted-аккаунте через ключ разработчика авторизации приложения не требует и этой проверкой не затронута.
Что делать интеграторам
Если ваш обработчик реагировал только на 409 SNAPSHOT_REQUIRED и повторял сохранение исходников в цикле, добавьте ветку на 400 NO_USER_TOKEN — в ней нужно авторизовать приложение, а не сохранять исходники ещё раз. Поле error.hint в этом ответе описывает действие.
NEW-0801-4: подсказки в ответах публикации: hint при NO_USER_TOKEN и presentedAt при SNAPSHOT_REQUIRED
Ответ 400 NO_USER_TOKEN у POST /v1/apps/:id/publish теперь несёт объект error.hint с полями requiredAction (что именно сделать, чтобы авторизовать приложение), docsUrl и oauthDocsUrl. Форма объекта совпадает с error.hint у 409 SNAPSHOT_REQUIRED на этом же эндпоинте.
Ответ 409 SNAPSHOT_REQUIRED дополнен полем error.hint.lastSnapshot.presentedAt — время, когда версию последний раз предъявили сохранением. Именно от него считается ageMinutes, поэтому по паре полей видно, почему версия признана устаревшей. Поле timestamp рядом по-прежнему означает время создания версии.
Оба поля аддитивные: прежние вызовы работают без изменений.
FIX-0801-5: повторное сохранение тех же исходников открывает публикацию
Было
Проверка свежести перед POST /v1/apps/:id/publish отсчитывала окно от времени создания версии. Повторное сохранение тех же байтов возвращало HTTP 201 с deduplicated: true, но новую версию не создавало и время создания не двигало, поэтому publish продолжал отвечать 409 SNAPSHOT_REQUIRED. Вызывающий, у которого исходники не менялись, попадал в цикл publish → 409 → POST /v1/apps/:id/sources → publish → 409, который не завершался: единственным способом обновить снапшот было изменить содержимое архива.
Стало
Сохранение отмечает версию как заново предъявленную, и окно свежести отсчитывается от этой отметки. Дедуплицированное сохранение открывает публикацию наравне с настоящим. Время создания версии (data.timestamp) не меняется, поэтому имя файла в депо и место версии в политике хранения остаются прежними.
Влияние на интеграторов
Менять ничего не нужно. Рецепт из подсказки к 409 — «сохрани исходники и повтори» — теперь работает и когда исходники не менялись.
BC-0801-6: оборвавшийся exec больше не отвечает успехом с exitCode -1
Поддержка старого формата до: 01.02.2027
Было
Если поток POST /v1/infra/servers/:id/exec завершался, не прислав статус выхода, ответ приходил как success: true с exitCode: -1. Исход команды на сервере при этом неизвестен, то есть успехом такой ответ не был. В потоковом режиме (?stream=true) поток в этом случае просто молча закрывался.
Стало
На отдельной виртуальной машине (kind: "STANDALONE") такой ответ приходит как success: false с кодом EXEC_NO_EXIT и объектом hint, указывающим на проверку состояния сервера. В data возвращается накопленный к моменту обрыва вывод (stdout, stderr); полей exitCode, duration и truncated там нет — их значения неизвестны, и подставлять вместо них нули значило бы утверждать то, чего платформа не знает. В потоковом режиме приходит событие error с этим же кодом. У galaxy-приложения этот случай пока приходит по-старому.
Что делать интеграторам
Обработайте EXEC_NO_EXIT наравне с прочими кодами ошибок. Если код читал data.exitCode без проверки success, теперь он получит undefined вместо -1 — ветвитесь по success. Команду можно повторить, если она идемпотентна; если нет, сначала посмотрите состояние сервера через GET /v1/infra/servers/:id/logs.
FIX-0801-7: тело JSON-ответа /exec и /deploy начинается с открывающей скобки
Было
В JSON-режиме (без ?stream=true) POST /v1/infra/servers/:id/exec и POST /v1/infra/servers/:id/deploy удерживают соединение, отправляя пробелы каждые 15 секунд. Эти пробелы шли перед JSON-документом, поэтому у команды длиннее 15 секунд тело ответа начиналось с пробелов. Клиенты со строгой проверкой формата отказывались его разбирать — команда на сервере при этом отрабатывала успешно.
Стало
Пробелы удержания соединения идут внутри уже открытого JSON-объекта, поэтому тело с первого байта — {. Набор полей ответа не изменился.
Влияние на интеграторов
Клиенты, которые разбирали ответ обычным JSON-парсером, изменений не заметят — обе формы тела валидны. Клиентам, которые снимали ведущие пробелы вручную, это больше не нужно.
FIX-0801-8: дата-время без часового пояса больше не сдвигается для порталов вне Москвы
Было
Значение даты-времени без явного смещения — например deadline 2026-07-15T13:00:00 — уходило в Битрикс24 как есть и читалось в часовом поясе владельца веб-хука портала, а не клиента. Интеграция из Берлина, записавшая 13:00, получала в хранилище 10:00 UTC вместо 11:00 UTC: минус час летом и минус два зимой. Значение выглядело правдоподобно, поэтому порча дат платежей, дедлайнов и встреч замечалась не сразу.
Стало
Клиент может объявить свой часовой пояс заголовком X-Vibe-Timezone (IANA-имя, например Europe/Berlin; браузер получает своё значение из Intl.DateTimeFormat().resolvedOptions().timeZone). Даты-время без смещения штампуются смещением этого пояса, действовавшим на саму дату значения, — переходы на летнее время учитываются по каждому значению. Это касается полей, которые действительно хранят время суток: часть полей Битрикс24 называет датой-временем, а хранит как дату, и там смещение сдвинуло бы сам день, поэтому такие поля не трогаются. Значения с явным Z или смещением не переписываются никогда. Без заголовка (или с нераспознанным поясом) поведение прежнее — существующие интеграции не затронуты.
Заголовок влияет только на запись. Битрикс24 отбрасывает суффикс часового пояса внутри фильтра, поэтому платформа его срезает и значения фильтра всегда читаются в поясе портального аккаунта. С включённым заголовком запись «2026-07-15T13:00:00» и последующий фильтр по тому же литералу не совпадут: передавайте в фильтр тот момент времени, который хотите сравнить, либо задавайте диапазон с запасом на смещение.
FIX-0801-9: удаление бота идемпотентно: «бота уже нет» — это успех, а не 502
Было
DELETE /v1/bots/:botId отвечал 502 BOT_DELETE_PARTIAL и сохранял запись в базе Вайбкод при ЛЮБОЙ ошибке Битрикс24 — в том числе когда Битрикс24 сообщал, что такого бота у него уже нет. Целевое состояние было достигнуто, а вызов считался провалившимся, и запись оставалась в GET /v1/bots навсегда: обычным удалением её было не убрать, помогал только ?force=true.
Стало
Если Битрикс24 отвечает, что бота у него нет, удаление считается успешным: запись удаляется, ответ 200 содержит новое поле data.alreadyAbsentOnB24: true. Дополнительно, когда Битрикс24 отклоняет отмену регистрации по иной причине, Вайбкод один раз проверяет, есть ли бот на портале: если бот уже снят — тоже успех, если снятие подтвердить не удалось — прежний 502 BOT_DELETE_PARTIAL с сохранением записи. При ?force=true ответ на «бота нет» теперь тоже несёт alreadyAbsentOnB24: true вместо forced: true — сироты на стороне Битрикс24 в этом случае не возникает. В теле 502 появилось поле error.incidentCode — шестизначный код для обращения в поддержку.
Влияние на интеграторов
Менять ничего не нужно. Сценарий «удалил бота, получил ошибку, бот остался в списке» больше не возникает; ?force=true остаётся только для порталов, которые недоступны навсегда. Если код ветвится на data.forced, учтите, что в случае «бота на Битрикс24 уже нет» теперь приходит data.alreadyAbsentOnB24.
FIX-0801-10: сообщения об ошибках Битрикс24 приходят без HTML-разметки
Было
Валидационный текст, полученный от Битрикс24, форвардился в поле error.message вместе с прицепленным тегом <br>. Клиент, выводящий сообщение как текст — а JSON-контракт ровно это и предполагает, — показывал пользователю буквальный тег после каждой ошибки, на его родном языке. То же касалось поля error.validation[].message.
Стало
Разметка убирается на границе ответа: <br> в любом написании становится переводом строки. Именно переводом, а не пробелом — Битрикс24 склеивает через этот тег ошибки по разным полям, и граница между ними сохраняется. Локализация не меняется: текст остаётся на языке портала и не переводится. Угловые скобки внутри пользовательских данных (адрес почты в тексте ошибки, знак сравнения) не затрагиваются — убирается именно тег переноса строки, а не разметка вообще. То же касается ответов POST /v1/batch, где ошибка приходит по каждому подвызову. Если вы вырезали <br> на своей стороне, эту обработку можно снять.
Заодно введены два технических ограничения: сообщение длиннее 8192 символов усекается, а из массива error.validation отдаётся не больше 100 элементов. Оба нужны потому, что и текст, и число полей приходят от портала и ничем не ограничены; на реальных ответах ни то, ни другое не срабатывает.
Охват: error.message и error.validation[] во всех конвертах ошибок V1, ответы POST /v1/batch и POST /v1/{entity}/batch, meta.pageErrorSample при автопагинации, POST /v1/bots. У ответа POST /v1/chats/messages/bulk при этом поля ошибок приведены к общей форме {code, message} — раньше объект ошибки портала форвардился как есть, со своими ключами error/error_description.
Отдельно: счётные фразы в письмах на французском и португальском теперь считают по правилам CLDR — ноль относится к единственному числу («0 jour», не «0 jours»). И англоязычная подсказка про модуль «Универсальные списки» больше не содержит русского названия на международных порталах.
NEW-0801-11: место встраивания CALL_CARD — панель в карточке звонка
Было
GET /v1/placements/available не отдавал CALL_CARD, а POST /v1/placements/bind
отвечал VALIDATION_ERROR на этот код — хотя Битрикс24 его поддерживает.
Стало
CALL_CARD есть в справочнике и принимается на привязку. Приложению нужен скоуп
telephony: Битрикс24 отдаёт это место только приложениям с таким правом, иначе
привязка отклоняется с «Placement not found». Тот же скоуп теперь требуется и для
соседнего TELEPHONY_ANALYTICS_MENU — раньше он был в справочнике без права, и
привязка молча не удавалась на стороне Битрикс24.
FIX-0801-12: четыре тихих отказа: дела, склады, файлы, пользовательские поля
Было
Четыре запроса отвечали кодом 200 и возвращали не то, о чём их просили.
Дела: поля providerParams и settings объявлены типом object, но пустое значение приходило пустым массивом. Тип поля зависел от наполнения, поэтому клиент, собранный по схеме, падал на десериализации именно тех записей, где значение пустое.
Склады: GET /v1/warehouses и GET /v1/warehouses/:id/stock со смещением, не кратным 50, отдавали первую страницу — offset=1, offset=2 и offset=3 возвращали одни и те же записи, при этом hasMore сообщал, что дальше есть ещё. Обход по смещению зацикливался на первой странице.
Файлы и папки: фильтр по полю, которое Битрикс24 не умеет фильтровать (createdBy, size, updatedBy), молча отбрасывался, и вместо отобранных записей приходило всё содержимое папки. Несуществующее имя поля вело себя так же.
Пользовательские поля: DELETE /v1/userfields/:entity/:id и DELETE /v1/items/:entityTypeId/userfields/:id с заголовком Content-Type: application/json и пустым телом отвечали ошибкой FST_ERR_CTP_EMPTY_JSON_BODY — запрос не доходил до обработчика. Без заголовка тот же запрос работал.
Стало
Дела: пустое значение providerParams и settings приходит пустым объектом, объявленный тип верен всегда. Непустые значения не изменились.
Склады: смещение построчное — offset=1&limit=3 возвращает вторую, третью и четвёртую записи. Если запрошенное окно не покрывается одной страницей Битрикс24, data придёт пустым, а в meta.warnings — код OFFSET_BEYOND_FETCHED_PAGE.
Файлы и папки: фильтр по неподдерживаемому полю отвергается ошибкой 400 UNSUPPORTED_FILTER со списком полей, по которым фильтровать можно: id, name, code, storageId, type, folderId (для папок — parentId), deletedType, createdAt, updatedAt, deletedAt. Навигация по дереву не затронута: родительская папка остаётся в списке разрешённых, поэтому обе формы — параметром ?folderId= и фильтром ?filter[folderId]= — работают как раньше.
Пользовательские поля: удаление принимается с заголовком Content-Type: application/json и без него. Некорректный JSON по-прежнему отвергается, с кодом INVALID_JSON_BODY.
Влияние на интеграторов
Ничего менять не нужно. Три оговорки на случай, если ваш код полагался на прежнее поведение: проверка Array.isArray больше не отличает пустое значение дела от заполненного (проверяйте число ключей); обход складов по смещению теперь честно двигается по строкам, а не по страницам; фильтр файлов и папок по полю вне списка выше теперь вернёт ошибку вместо всего содержимого папки.
NEW-0801-13: служебные поля задачи получили названия, описания и словарь допустимых значений
GET /v1/tasks/fields теперь отдаёт человекочитаемое label и description для двадцати служебных полей Битрикс24, у которых раньше вместо названия стояло само имя поля: NOT_VIEWED, DURATION_TYPE, GUID, CHAT_ID, CHECKLIST, FAVORITE, IS_MUTED, IS_PINNED, IS_PINNED_IN_GROUP, ALLOW_CHANGE_DEADLINE, ALLOW_TIME_TRACKING, NEW_COMMENTS_COUNT, SERVICE_COMMENTS_COUNT, FORUM_ID, FORUM_TOPIC_ID, EXCHANGE_ID, EXCHANGE_MODIFIED, OUTLOOK_VERSION, SITE_ID, XML_ID.
Вместе с этим в схеме полей появились два ключа, которые Битрикс24 присылал и раньше, а мы отбрасывали: values — словарь допустимых значений в виде массива [{ "value": "Y", "label": "Да" }], и default — значение, которое портал подставит, если поле не передано. Они приходят у всех полей, где портал их отдаёт, а не только у перечисленных выше — например словарь Y/N теперь виден и у MULTITASK, TASK_CONTROL, SUBORDINATE, ADD_IN_REPORT, REPLICATE. Подписи в values формирует сам портал и локализует своими настройками, поэтому у поля с кодовыми значениями подписи может не быть — так у DURATION_TYPE приходят только коды secs, mins, hours, days, weeks, monts, years (опечатка monts — со стороны Битрикс24, портал принимает именно это написание).
values — отдельный ключ, он не заменяет items: items по-прежнему отдаёт сырой перечислимый справочник у полей типа enumeration в формате [{ "ID": "1", "VALUE": "Первый" }]. Гарантия — на уровне ключа: у каждого всегда своя форма, поэтому читайте нужный по имени. На сегодняшних порталах у одного поля приходит только один из двух, но одновременное присутствие не запрещено.
Прежние вызовы работают без изменений: новые ключи аддитивны, набор полей и их типы не менялись. Пять полей — FAVORITE, IS_MUTED, IS_PINNED, NEW_COMMENTS_COUNT и NOT_VIEWED — описывают отношение к задаче того пользователя, от имени которого работает ключ, а не свойство самой задачи: другой ключ на том же портале увидит другие значения.
2026-07-31
BC-0731-1: приём исходников требует Content-Length
Поддержка старого формата до: 30.01.2027
Эндпоинты приёма исходников — POST /v1/apps/{id}/sources и POST /v1/infra/servers/{id}/sources — теперь принимают тело только с заголовком Content-Length. Запрос без него (передача по частям, Transfer-Encoding: chunked) получает 411 с кодом MISSING_CONTENT_LENGTH.
Причина: тело архива больше не собирается в память целиком, а отправляется в хранилище на пролёте, и для этого длина обязана быть известна заранее.
Подавляющее большинство клиентов заголовок и так шлют: его проставляют curl --data-binary, fetch с телом-буфером и любой HTTP-клиент, отправляющий файл целиком. Затронуты только клиенты, которые сознательно стримят тело неизвестной длины.
Заодно повторное сохранение одного и того же архива теперь стоит дороже: совпадение по содержимому определяется после приёма тела, поэтому ответ приходит чуть медленнее, а объём засчитывается в операции хранилища. Результат прежний — deduplicated: true и та же версия.
Что делать интеграторам
Отправлять архив целиком (--data-binary @file в curl, буфер или файл в теле запроса), а не потоком неизвестной длины. Если клиент стримит тело сам — посчитать размер заранее и выставить Content-Length.
FIX-0731-2: имя тарифа в GET /v1/me — из справочника платформы и по-русски
Было
Поле data.tariff.name бралось из сохранённого снимка тарифа: имя лицензии, как его отдал портал, а если портал имени не дал — значение внутреннего словаря без учёта языка. На части порталов это не было названием тарифа вовсе: приходило либо русское «Демо» на нерусскоязычном портале, либо сам код лицензии, например pro100, выданный за человекочитаемое название.
Стало
Название разрешается в момент ответа. Если код тарифа известен справочнику редакций платформы, поле берётся оттуда и по-русски — Демо-период; имя, сообщённое порталом, при этом не используется, поэтому подпись может измениться и там, где всё было в порядке. Если код справочнику неизвестен, поле по-прежнему несёт имя от портала — как есть, на его языке. И только когда имени нет вовсе либо вместо него приходит сам код лицензии, поле равно null: код лицензии в качестве названия больше не подставляется никогда. Тип поля не менялся — строка или null, сам код по-прежнему в data.tariff.code.
Подписи известных редакций заодно уточнились: Демо → Демо-период, Проект → Проект — архивный бесплатный, NFR → NFR — партнёрская лицензия.
FIX-0731-3: поиск пользователей сервера возвращает только активных сотрудников
Было
GET /v1/infra/servers/:id/b24-users мог возвращать неактивных пользователей и пользователей, которые не являются сотрудниками.
Стало
Эндпоинт возвращает только пользователей, которые подтверждены как активные сотрудники Битрикс24. Изменения со стороны интеграций не требуются.
NEW-0731-4: приложение само решает, включать ли авто-высоту iframe во встройке
У приложения появилось необязательное поле placementResizeEnabled (по умолчанию false). Оно управляет тем, отдаёт ли платформа при открытии встройки страницу-обёртку, которая подстраивает высоту iframe под контент вашего приложения.
Поле возвращается в GET /v1/apps и GET /v1/apps/:id и принимается в PATCH /v1/apps/:id. Прежние вызовы работают без изменений: у всех существующих приложений значение false, то есть поведение открытия встройки прежнее.
Включайте только вместе с правкой на стороне приложения. Обёртка открывает приложение во вложенном iframe на источнике платформы, поэтому у приложения появляется новый источник-предок. Если приложение отдаёт собственный заголовок Content-Security-Policy с директивой frame-ancestors, добавьте в неё источник платформы — иначе браузер откажется открывать приложение. Приложения без своей директивы frame-ancestors правок не требуют.
Если авто-высота у вашего приложения уже работала, включите поле, чтобы сохранить прежнее поведение. Учтите, что поле — необходимое условие, но не единственное: сама возможность раскатывается по порталам постепенно.
Чтобы приложение сообщало платформе свою высоту, оно постит родительскому окну сообщение { type: 'vibe:resize', height } — контракт описан в разделе Приложение в Битрикс24.
FIX-0731-5: пустое тело с заголовком JSON больше не отклоняется на маршрутах инфраструктуры
Было
Операция, которой тело не нужно, отвечала 400 с кодом FST_ERR_CTP_EMPTY_JSON_BODY, если клиент присылал заголовок Content-Type: application/json без тела. Так поступают клиенты, которые ставят этот заголовок на любой запрос — например axios и PowerShell Invoke-RestMethod. Отказ происходил на разборе тела, то есть раньше проверки ключа, поэтому по ответу нельзя было понять, что не так с доступом. Затрагивало DELETE /v1/infra/servers/:id/access-tokens/:tokenId, POST /v1/infra/servers/:id/wake и остальные операции сервера без тела.
Стало
Пустое тело принимается как {}, и операция отвечает по существу — 401 на неверном ключе, 404 на несуществующем сервере, 200 при успехе. Прежний обход, когда тело {} передавалось явно, продолжает работать. Заодно неразбираемое тело на этих маршрутах теперь даёт код INVALID_JSON_BODY вместо FST_ERR_CTP_INVALID_JSON_BODY — тот же код, что уже возвращали соседние операции того же сервера, включая deploy, exec и lock.
2026-07-30
NEW-0730-1: withTotal в списочных вызовах пакетного запроса
Списочный вызов внутри POST /v1/batch принимает в params параметр withTotal — тот же, что у одиночного GET /v1/{entity}. withTotal: false означает «количество не нужно»: подсчёт у Битрикс24 не заказывается, а data.totals.<id> и meta.<id>.total в ответе не приходят. Границей листания остаётся meta.<id>.hasMore.
Границы применимости стоит знать до того, как параметр окажется в коде. Он действует на вызовах с action: "list" — и на тех, у которых limit не больше 50 и offset равен нулю (именно такие уезжают в Битрикс24 одной командой пакета), и на тех, у которых limit больше 50. У action: "search" и у пакета одной сущности POST /v1/{entity}/batch параметр инертен — подсчёт заказывается как раньше, total приходит.
Правило присутствия total там же, что у одиночного вызова: если подсчёт не заказан, а страница пришла короче полной, точное количество всё равно приходит — оно известно из самой страницы.
У вызова, который подсчёт не заказывал, meta.<id>.hasMore выводится из полноты страницы, а не из отсутствующего количества: пришла полная страница — записи ещё есть, пришла короткая — коллекция закончилась. Цикл «читать, пока hasMore» доходит до конца и без total; если размер коллекции ровно кратен размеру страницы, последний вызов вернёт пустой список — это штатный признак конца, а не ошибка.
Вместе с этим у вызова, который подсчёт не заказывал, data.totals.<id> и meta.<id>.total перестают приходить и на позиционированной странице — при offset больше нуля. Так ведут себя и одиночные эндпоинты: количество не должно то появляться, то исчезать внутри одного обхода.
FIX-0730-2: meta.total приходит на короткой странице, даже когда подсчёт не заказывался
Было
Если вызов списка не заказывал подсчёт, meta.total в ответе отсутствовал всегда — в том числе там, где количество было известно точно. Страница короче запрошенного limit означает, что коллекция на ней и закончилась, то есть число записей равно числу отданных строк, — но поле всё равно не приходило.
Стало
В этом случае meta.total приходит и содержит точное число. Правило простое: total появляется только на вызове с offset = 0 и только если страница пришла короче запрошенного. Пустой результат — тоже число: "total": 0.
Явный withTotal=false в запросе по-прежнему означает «поля total не будет» — обещание про этот параметр не изменилось. Настройка totalDefault на API-ключе и платформенное умолчание точный total из короткой страницы не запрещают: про них такого обещания не давалось. Отсюда следствие, о котором стоит знать заранее: два одинаковых запроса от двух разных ключей могут вернуть ответы разной формы.
Правило действует и на списочных вызовах внутри POST /v1/batch.
Влияние на интеграторов
Менять ничего не нужно. meta.total остаётся необязательным полем: проверяйте его наличие в конкретном ответе, а не предполагайте. Границей цикла листания остаётся meta.hasMore — арифметика по meta.total для этого не годится ни до, ни после изменения.
FIX-0730-3: бесплатный тариф больше не получает 402 PLAN_NOT_ALLOWED_ON_TRIAL при деплое в галактику
Было
POST /v1/infra/servers с { name, source, runtime, start } на портале с galaxy-размещением возвращал 402 PLAN_NOT_ALLOWED_ON_TRIAL (details.allowedPlans: ["bc-micro"]), хотя GET /v1/me в capabilities.servers.create отдавал available: true с тем же bc-micro. Плана в запросе не было вовсе: платформа сама поднимала общий galaxy-хост на своём (не-bc-micro) плане, и этот внутренний выбор проверялся тем же списком планов, что и VM, создаваемая пользователем. Каждое galaxy-приложение при этом расходовало единственный слот бесплатного тарифа, поэтому второй деплой упирался в 402 TRIAL_PORTAL_LIMIT.
Стало
Одношаговый create-and-deploy работает на бесплатном тарифе. Общий galaxy-хост занимает единственный VM-слот, план ему выбирает платформа (минимальный стабильный план сегмента), а контейнеры-приложения на этом хосте слот не расходуют — их число ограничено только ёмкостью хоста. Список планов из capabilities.servers.create.limits.allowedPlans теперь относится только к VM, которую вы создаёте сами; deployment.galaxyApp._rules (правило QUOTA) описывает это явно. Для placement: "dedicated" ограничения бесплатного тарифа не изменились.
FIX-0730-4: /v1/me не предлагает эндпоинты токенов доступа, когда раздел выключен
Было
Блок data.infra.preview ответа GET /v1/me всегда содержал адреса mintUrl, listUrl, revokeUrl и refreshUrl — в том числе когда раздел токенов доступа выключен на платформе и все четыре эндпоинта отвечают 503 FEATURE_DISABLED. Соседние блоки data.capabilities.servers.preview и data.deployment.preview в том же ответе сообщали {"available": false, "reason": "FEATURE_DISABLED"}, то есть один ответ противоречил сам себе.
Стало
data.infra.preview следует тому же признаку, что и два соседних блока. При включённом разделе рядом с адресами приходит "available": true, при выключенном — {"available": false, "reason": "FEATURE_DISABLED"} без адресов. Правило data.api._rules про проверку деплоя через режим api-bearer называет эту проверку и запасной путь — шаги healthcheck и tunnel_routing в отчёте POST /v1/infra/servers/:id/deploy.
Влияние на интеграторов
Клиент, который читал адреса из data.infra.preview безусловно, при выключенном разделе получит блок без них. Проверяйте поле available перед обращением к адресам — вызовы по ним и раньше отвечали 503 FEATURE_DISABLED.
FIX-0730-5: заголовок X-Vibe-User-Id больше не смешивает идентификаторы
Было
Заголовок X-Vibe-User-Id, который платформа передаёт приложению на каждом запросе, документировался как числовой ID пользователя Битрикс24. На части маршрутов входа в него попадал внутренний идентификатор Вайбкода без какого-либо признака или значение 0 — заглушка «пользователь не опознан». Приложение отличить это от настоящего ID не могло. Опасен именно внутренний идентификатор, начинающийся с цифр: Битрикс24 приводит строку к числу, поэтому 47a4cff2-… превращалось в 47 — валидный ID постороннего сотрудника портала. Приложение, подставлявшее заголовок в DIALOG_ID метода im.message.add, отправляло личное сообщение не тому человеку.
Стало
Значение заголовка всегда одно из двух: либо строка из одних цифр — ID пользователя портала Битрикс24, либо значение с явным префиксом, если ID в портале у посетителя нет: net_ — пользователь Битрикс24 Нетворк вне портала, share: — анонимный посетитель по гостевой ссылке, vibe: — пользователь Вайбкода, чей ID в портале определить не удалось. Значение 0 не отправляется вовсе: если идентичность неизвестна, заголовка просто нет. При входе через кабинет Вайбкода платформа теперь определяет настоящий ID пользователя в портале и присылает именно его — там, где раньше приходил внутренний идентификатор.
Если приложение подставляет заголовок в параметры методов Битрикс24 (USER_ID, DIALOG_ID, RESPONSIBLE_ID и подобные) — проверяйте, что значение состоит только из цифр. Значение с префиксом означает, что у посетителя нет ID в портале, и передавать его в Битрикс24 нельзя. Для логов, аналитики и собственного ACL заголовок годится в любом виде. Полный контракт — Что приходит в приложение.
BC-0730-6: галактика не создаётся на бесплатном тарифе Битрикс24
Поддержка старого формата до: 30.08.2026
Было
Аккаунт Битрикс24 на бесплатном тарифе с включённым режимом галактик получал галактику под свои приложения. Одношаговое создание POST /v1/infra/servers с полем source разворачивало приложение контейнером на общем хосте, а GET /v1/me в блоке deployment описывал galaxy-контракт.
Стало
На бесплатном тарифе новая галактика не создаётся. Пока у аккаунта нет галактики, каждое приложение разворачивается на отдельной виртуальной машине, а создание с полем source возвращает 400 SOURCE_AT_CREATE_GALAXY_ONLY. GET /v1/me в этом состоянии не отдаёт deployment.galaxyApp, ставит deployment.primary в standalone и объясняет причину в новом поле deployment.placementNote.
Аккаунт, у которого галактика уже есть, продолжает разворачивать приложения в ней — ограничение касается только создания новой. Коммерческий тариф Битрикс24 снимает ограничение целиком.
Что делать интеграторам
Разворачивайте в два шага, как на обычном сервере: POST /v1/infra/servers с provider, name, plan, region (без source), дождитесь status: "running" и blackholeStatus: "CONNECTED", затем POST /v1/infra/servers/:id/deploy с source, runtime, start. Модель размещения определяйте по GET /v1/me — по наличию блока deployment.galaxyApp, а не по режиму портала.
FIX-0730-7: POST /v1/apps честнее сообщает о причине отказа на коробочном портале
Было
При установке приложения на коробочном портале без активной подписки «BitrixGPT + Маркетплейс» POST /v1/apps возвращал непрозрачный 502 CONNECTOR_APP_INSTALL_FAILED: причина отказа наружу не выдавалась вовсе, а сам код обещал временный сбой — повтор вызова выглядел осмысленным, хотя помочь не мог.
Стало
Отказ по подписке распознаётся и на коробочном портале: POST /v1/apps возвращает 403 с кодом, называющим причину. Аккаунт в подписочном регионе (Россия, Беларусь) без активной подписки получает B24_MARKET_SUBSCRIPTION_REQUIRED — вместе с понятным сообщением и ссылкой на оформление в error.details.upgradeUrl. Аккаунт на тарифной модели доступа, а также портал, на котором REST недоступен, получает INT_TARIFF_REQUIRED (нужен коммерческий тариф Битрикс24) без error.details.upgradeUrl: подписки, которую можно оформить, там нет. Прочие отказы установки через коннектор классифицируются как прежде.
Влияние на интеграторов
Менять ничего не нужно, успешные вызовы не затронуты. Тем, кто обрабатывал 502 CONNECTOR_APP_INSTALL_FAILED, стоит дополнительно ловить 403 B24_MARKET_SUBSCRIPTION_REQUIRED / INT_TARIFF_REQUIRED — первый приходит на аккаунте подписочного региона, второй на тарифной модели доступа и на портале с недоступным REST — и подсказывать пользователю оформить подписку на портале либо перейти на коммерческий тариф Битрикс24: этот отказ терминальный, повторять запрос бессмысленно.
NEW-0730-8: новый отказ 429 TIMEOUT_QUARANTINE: метод, который перестал отвечать, ставится на паузу
Если один и тот же метод несколько раз подряд не ответил вашему аккаунту Битрикс24 за отведённое вызову время, Вайбкод перестаёт отправлять к нему запросы и отвечает 429 с кодом TIMEOUT_QUARANTINE, заголовком Retry-After и полем error.retryAfter. Пауза действует на пару «аккаунт + метод»: остальные методы работают как обычно, и от того, каким ключом сделан вызов, она не зависит.
Это отказ на стороне Вайбкод, а не лимит Битрикс24: запрос до аккаунта не дошёл, поэтому ничего не изменилось — повтор безопасен даже для методов записи. Пауза снимается сама: время от времени один вызов пропускается для проверки, и первый успешный ответ снимает её немедленно. Вмешательство не требуется, отдельного эндпоинта для снятия нет.
В ответе появилось машиночитаемое поле error.scope: у этого отказа "portal" — пауза общая для ВСЕХ ключей аккаунта, включая чужие интеграции. У соседнего отказа OPERATION_TIME_LIMIT (Битрикс24 приостановил метод, исчерпавший бюджет рабочего времени) оно равно "apiKey" — там приостановлен только вызывающий ключ. Те же два значения error.scope уже приходят в отказе по квоте обращений FEEDBACK_QUOTA_EXCEEDED, словарь общий. Различие даёт ответ на вопрос «чинить свой код или ждать вместе с аккаунтом», не разбирая текст ошибки. Рядом приходит error.hint с действием — на русском. Оба кода теперь описаны в справочнике ошибок. Количество чужих ключей, их имена и объём их неудач в ответе не приходят — это данные других клиентов аккаунта.
Что делать: дождаться срока из Retry-After и повторить, добавив к паузе случайную задержку, — а не крутить повтор в цикле. Интервал повторов при этом не сокращайте: пока пауза действует, один вызов раз в 5 минут пропускается как проба восстановления, и агрессивный повтор занимает этот слот собой — метод остаётся закрытым для всего аккаунта дольше, чем если бы вы просто подождали. Если метод не отвечает стабильно, облегчите вызов: меньше полей в select, меньше страница, более узкий фильтр или более узкий интервал дат. Тяжёлый запрос и есть причина, по которой аккаунт не успевает ответить, — пауза снимется тем вызовом, который аккаунт сумеет выполнить.
Код может прийти на любом вызове, который Вайбкод проксирует в Битрикс24 под именем метода Битрикс24: на одиночных чтениях и записях — как 429 с заголовком, а на подвызовах батча, которые Вайбкод исполняет отдельными запросами (поиск и список с limit больше 50), — внутри ответа 200 строкой data.errors[<id>] вида { "code": "TIMEOUT_QUARANTINE", "message": …, "retryAfter": …, "scope": "portal", "hint": … }: конверт объединяет разные вызовы, поэтому заголовка Retry-After для отдельного подвызова у него нет, срок приходит полем. В батче одной сущности (POST /v1/{entity}/batch) отказ приходит так же внутри 200, но элементом массива data: { "error": { "code": "TIMEOUT_QUARANTINE", "message": …, "retryAfter": …, "scope": "portal", "hint": … } }. Сам конверт POST /v1/batch под паузу не попадает: он объединяет разные методы, и его собственная задержка не говорит о том, какие из них перестали отвечать. Опрос событий бота (GET /v1/bots/{botId}/events) тоже не попадает — у него свой ответ на таймаут, с подсказкой про восстановление подписки. В поиске с разбиением по датам (POST /v1/{entity}/search) отказ приходит либо тем же 429, либо — если часть окон успела прочитаться — в meta.windowErrorSample.code при ответе 200: это признак неполной выдачи.
Защита включается постепенно, по аккаунтам, поэтому этот код увидят пока не все.
FIX-0730-9: отказ OPERATION_TIME_LIMIT в батче приходит со своим кодом и сроком, а не под общим кодом
Было
Битрикс24 приостанавливает метод, исчерпавший бюджет рабочего времени, примерно на 5 минут, и Вайбкод отбивает такие вызовы на входе, зная, что пауза ещё действует. На одиночном вызове этот отказ приходил как 429 с кодом OPERATION_TIME_LIMIT, заголовком Retry-After и полями retryAfter и scope. А внутри батча тот же отказ терял и код, и срок: на подвызовах POST /v1/batch, которые Вайбкод исполняет отдельными запросами (поиск и список с limit больше 50), он приходил как data.errors[<id>] с общим кодом AUTO_PAGINATION_FAILED, а в батче одной сущности (POST /v1/{entity}/batch) — как data[i].error с кодом CALL_FAILED и текстом Internal error. Ответ был 200, поэтому отличить приостановленный метод от внутреннего сбоя было нечем, а срок повтора не приходил вовсе — оставалось повторять вслепую по методу, который аккаунт держит закрытым.
Стало
Обе батчевые поверхности отдают тот же отказ, что одиночный вызов: { "code": "OPERATION_TIME_LIMIT", "message": …, "retryAfter": …, "scope": "apiKey", "hint": … } — в data.errors[<id>] у POST /v1/batch и в data[i].error у POST /v1/{entity}/batch. Конверт объединяет разные вызовы, поэтому заголовка Retry-After для отдельного подвызова у него нет — срок приходит полем retryAfter. Поле scope равно "apiKey": приостановлена связка «ваш ключ + этот метод», другие методы работают, и другие ключи аккаунта тот же метод вызывать могут. Контраст — TIMEOUT_QUARANTINE со scope: "portal", где пауза общая для всего аккаунта. Локализованного поля userMessage в 200-конверте нет ни у одного отказа, поэтому нет и здесь. Ограничение снято и из справочника ошибок.
Влияние на интеграторов
Менять ничего не нужно: коды сузились с общих до конкретного, а поля добавились. Если ваш код ветвился на AUTO_PAGINATION_FAILED или CALL_FAILED, чтобы поймать приостановленный метод, теперь для этого есть OPERATION_TIME_LIMIT и срок в retryAfter — дождитесь его и повторите, а не крутите повтор в цикле.
NEW-0730-10: машинная схема описывает 51 эндпоинт, который работал, но в ней отсутствовал
Все они отвечали и раньше — но клиент, который строит вызовы по /v1/openapi.json (кодогенератор, ИИ-агент, наш собственный справочник), их не видел и не мог о них узнать.
Товарные позиции — работа с отдельной строкой и её схемой полей: GET|PATCH|DELETE /v1/{deals,leads,quotes,invoices}/{id}/products/{rowId} и GET /v1/{deals,leads,quotes,invoices}/{id}/products/fields. То же для смарт-процессов: /v1/items/{entityTypeId}/{id}/products/{rowId} и .../products/fields.
Схемы полей: GET /{сущность}/fields появился у 22 сущностей, у которых Битрикс24 не отдаёт метод схемы (заказы, документы, шаблоны документов, платежи, позиции корзины, статусы заказа, каталоги, разделы и цены каталога, свойства товаров, бронирования, события и группы, подразделения, сайты и страницы, шаблоны/активности/роботы бизнес-процессов, линии телефонии, конфигурации открытых линий, узлы оргструктуры). Ответ там — набор полей, объявленный обёрткой, без пользовательских (UF) полей: в описании операции это сказано прямо, чтобы никто не ждал большего.
Открытые линии: GET|POST /v1/openline-configs, PATCH|DELETE /v1/openline-configs/{id}, POST /v1/openline-configs/search и read-only POST /v1/openline-configs/batch (в батче доступны только list, get, fields — записи идут через перечисленные выше операции). У списка описан его настоящий конверт: total — размер выданного окна, а не всего набора, поэтому цикл постраничного чтения ограничивают по hasMore.
Бронирования: GET /v1/bookings и POST /v1/bookings/search. Окно дат (dateFrom, dateTo) описано как обязательное — без него метод Битрикс24 молча вернул бы пустой список, поэтому обёртка отклоняет такой вызов.
Конвертация лида: POST /v1/leads/{id}/convert — в описании операции сказано, что она не идемпотентна (повторный вызов создаст второй набор сущностей).
Затронутые эндпоинты: GET /v1/openapi.json
FIX-0730-11: текст ошибки проверки BYOK-ключа больше не содержит сам ключ
Было
Если провайдер отвечал на проверку учётных данных ошибкой и повторял в ней присланный ключ, этот текст возвращался вызывающему и сохранялся в поле lastError, которое отдаёт GET /v1/ai/credentials. Скрывались только ключи вида sk-…, адреса и пары «логин:пароль@хост», поэтому ключи прочих провайдеров попадали в ответ и в поле дословно — и были доступны любому участнику портала.
Стало
Присланный ключ и адрес прокси удаляются из текста ошибки по значению, независимо от их формата: и в ответе POST /v1/ai/credentials, PATCH /v1/ai/credentials/{id}, POST /v1/ai/credentials/{id}/test, и в сохраняемом lastError на их месте стоит редакция. Коды ошибок и статусы не изменились.
FIX-0730-12: многостраничный список отдаёт начало выборки вместо ошибки, когда подсчёт не удался
Было
Многостраничный вызов списка — GET /v1/{entity} и POST /v1/{entity}/search с limit больше 50, а также их списочные подзапросы в POST /v1/batch — начинался с подсчёта записей. Подсчёт коллекции стоит Битрикс24 несоразмерно дорого, и если он срывался (тайм-аут, лимит запросов, ошибка портала), вызов возвращал ошибку целиком — без единой записи, хотя первая страница уже была получена.
Параметр withTotal=false на таких вызовах не действовал: подсчёт заказывался всё равно, meta.total приходил.
Стало
Подсчёт выполняется, только когда без него не обойтись, и его срыв больше не отменяет ответ. Если записи получены, а подсчёт не удался, приходит 200 с непрерывным началом выборки: meta.hasMore равен true, meta.total отсутствует, а в meta.pageErrorSample лежат code и message с причиной. Два новых значения code — оба коды самого Вайбкод, а не Битрикс24:
PAGE2_COUNT_FAILED— подсчёт не удался: тайм-аут, лимит запросов или ошибка портала.LAZY_COUNT_NO_PROGRESS— обход остановлен: следующая страница не принесла ни одной новой записи, хотя в коллекции их больше, чем уже отдано.
В пакетном запросе то же самое приходит в data.meta.<id>: hasMore равен true, pageErrorSample заполнен, а total и data.totals.<id> отсутствуют — количество не сосчитано, и число отданных строк его не заменяет.
Заодно withTotal=false начал действовать при limit больше 50 — на GET /v1/{entity}, на POST /v1/{entity}/search и на вызовах action: "list" внутри POST /v1/batch: теперь meta.total не приходит ни при каком исходе. Границы применимости у action: "search" внутри пакета и у пакета одной сущности POST /v1/{entity}/batch не изменились. Без этого параметра количество приходит как раньше.
Оговорка про нагрузку: при limit больше 50 этот параметр — про форму ответа, а не про стоимость. Подсчёт платформе всё равно нужен, чтобы спланировать обход, поэтому дешевле вызов не станет, а точное количество, которое короткая первая страница отдаёт бесплатно, будет отброшено. Подсчёт параметр отменяет только при limit не больше 50.
Влияние на интеграторов
Проверьте, как код узнаёт о неполном ответе. Раньше признаком служила сама ошибка: цикл с повтором по 429 и 5xx срабатывал сам собой. Теперь неполнота уезжает в тело успешного ответа — обработчик ошибок не сработает, а заголовка Retry-After, который приходил вместе с 429, в таком ответе нет.
Признак неполноты — meta.pageErrorSample рядом с meta.hasMore. Продолжить обход можно двумя способами, и порядок между ними не произвольный.
Курсором — это первый выбор. meta.nextAfterId передаётся обратно как filter[>id], смещение при этом остаётся нулевым, и продолжение снова идёт тем же путём, на котором подсчёт заказывается не всегда. Приходит курсор не везде: только у сущностей с курсорным обходом и только при сортировке строго по id по возрастанию.
Смещением — запасной путь. Новый offset равен исходному плюс число отданных записей, но вызов с ненулевым offset уходит на счётный путь и заказывает ровно тот подсчёт, который только что не удался. После PAGE2_COUNT_FAILED это с высокой вероятностью тот же тайм-аут, поэтому повторяйте с паузой, а не в плотном цикле.
У GET /v1/tasks курсора нет — задачи идут без курсорного обхода, и nextAfterId в их ответах не приходит. Для них смещение остаётся единственным способом продолжить, со всеми оговорками выше.
Границей цикла листания по-прежнему остаётся meta.hasMore, а не арифметика по meta.total: поле было необязательным и до этого изменения.
FIX-0730-13: спека объявляет обязательные поля на создании и перестала требовать их на обновлении
Было
Создание через POST /v1/bizproc-robots, POST /v1/bizproc-activities и POST /v1/folders отклонялось, если не передать code, name и handler (для папки — name), а спека объявляла эти поля необязательными: клиент или SDK, сгенерированный по ней, получал отказ на первом же вызове. Обратная сторона той же причины: описание тела запроса было общим для создания и обновления, поэтому там, где обязательные поля всё же были объявлены — POST /v1/activities, POST /v1/documents, POST /v1/bizproc-templates — их требовал и PATCH, хотя частичное обновление одного поля API принимает.
Стало
Требование объявлено на операции создания, а не в общем описании тела. POST перечисляет поля, без которых откажет рантайм; PATCH их не требует и принимает частичное обновление, как и раньше на деле. Поведение не изменилось — изменилось описание, которое теперь соответствует обеим операциям. Те же поля помечены обязательными и на двух других описательных поверхностях: GET /v1/folders/fields с аналогами и GET /v1/guide.
2026-07-29
FIX-0729-1: комментарий к задаче больше не отбивается по правам ключа
Было
POST /v1/tasks/:taskId/comments на порталах с новой карточкой задач мог вернуть 403 с сообщением портала «Недостаточно прав доступа: отсутствует необходимый scope» — даже когда у ключа есть право task, а соседние вызовы задач в ту же секунду отвечают 200. Формулировка уводила в тупик: она читалась как «выдайте приложению доступ к задачам», хотя набор прав определяется при выпуске ключа, а не действиями пользователя.
Стало
Права на комментарий выдаются ключу в обеих формах, которых требуют старый и новый маршрутизаторы Битрикс24, поэтому вызов проходит. Ключам, выпущенным раньше, набор прав досылается на месте при первом таком отказе, и запрос повторяется — вмешательство не нужно. Если после этого портал всё равно отказывает, ответ 403 теперь прямо называет причину (у вебхука ключа набор прав уже, чем у самого ключа) и подсказывает переиздать ключ — вместо пересказа сообщения портала.
Влияние на интеграторов
Менять ничего не нужно. Клиент, ловивший этот 403 как постоянную ошибку, теперь получает 201.
FIX-0729-2: изменение доступа к приложению на общем хосте доезжает до его показа в Битрикс24
Было
Для приложения на общем хосте (kind=GALAXY_APP), встроенного в интерфейс Битрикс24, изменение списка доступа доезжало до показа не всегда. Пользователь, у которого доступ отозвали, мог продолжать видеть приложение на своём месте встройки — доступ к самому приложению при этом уже был закрыт.
Это касалось смены политики доступа и правки списка через PATCH /v1/infra/servers/:id/access-policy, POST /v1/infra/servers/:id/access и DELETE /v1/infra/servers/:id/access/:accessId.
Стало
Изменение доступа отражается на показе: приложение пропадает из интерфейса Битрикс24 у тех, кто доступ потерял, и появляется у тех, кому его выдали.
Влияние на интеграторов
Менять ничего не нужно. Тела запросов, ответы и коды ошибок прежние — изменился только наблюдаемый эффект вызова. Приложений на собственной виртуальной машине изменение не касается: там показ и раньше следовал за доступом.
FIX-0729-3: агенты и боты больше не засыпают по простою
Было
PATCH /v1/infra/servers/:id/sleep принимал любое значение sleepAfterMinutes, включая серверы, созданные под агента или бота (createdVia agent или bot).
Стало
Для сервера с createdVia agent или bot и ненулевым sleepAfterMinutes эндпоинт отвечает 400 с кодом AGENT_IDLE_SLEEP_FORBIDDEN. Значение null (никогда не засыпать) по-прежнему принимается.
Влияние на интеграторов
Спящий агент или бот перестаёт опрашивать Битрикс24 и не просыпается на новое сообщение, поэтому прежнее значение было нерабочим — менять в рабочем сценарии нечего. Для экономии по расписанию используйте Запланированное пробуждение.
BC-0729-4: сессия в Authorization должна принадлежать приложению из X-Api-Key
Поддержка старого формата до: 29.07.2026
Было
Сессия (vibe_session_*) выписывается одному приложению на одном аккаунте Bitrix24, но при вызове /v1/* эта привязка не проверялась. Ключ авторизации приложения B принимал сессию, выписанную приложению A, и запрос выполнялся под правами ключа B — то есть чужая сессия открывала доступ к данным через ключ другого приложения.
Стало
/v1/* проверяет, что сессия в Authorization: Bearer принадлежит тому же приложению и тому же аккаунту Bitrix24, что и ключ в X-Api-Key. Несовпадение — 403 с кодом SESSION_APP_MISMATCH. Ключ авторизации приложения, у которого приложение отвязано или удалено, сессию больше не принимает. Личных ключей vibe_api_* изменение не касается: сессию в Authorization они не читают — ни до него, ни после.
Вызовы без Authorization (только по ключу) не затронуты. Сессия, предъявленная с ключом своего приложения, работает как раньше.
Влияние на интеграторов
Проверьте, что оба заголовка относятся к одному приложению: X-Api-Key — ключ авторизации того приложения, которое выписало сессию через POST /v1/oauth/token. Если сервис обслуживает несколько приложений, храните пару «ключ + сессия» вместе и не берите их из разных мест.
Проверка включена с выпуска этого изменения, без переходного периода: она закрывает доступ к данным по чужой сессии. Ошибка 403 SESSION_APP_MISMATCH описана в справочнике ошибок.
FIX-0729-5: приложение на личном ключе снова получает X-Vibe-Authorization
Было
Приложение, размещённое в Black Hole на личном ключе (vibe_api_*), теряло заголовок X-Vibe-Authorization примерно через минуту после открытия плейсмента. Первые запросы проходили с сессией, дальше она пропадала и не возвращалась ни через перезагрузку страницы, ни через повторное открытие приложения — только через новое открытие плейсмента, и опять на минуту.
Причина: сессия, которую плейсмент выписывает пользователю, привязана к приложению через его адрес (appUrl), а восстанавливалась она только по цепочке «сервер → ключ → приложение». У личного ключа приложения нет, поэтому восстановление отвечало «сервер не найден», и Gateway запоминал этот отказ. X-Vibe-User-Id при этом продолжал приходить, поэтому со стороны приложения это выглядело как «пользователь есть, а токена нет».
Стало
Когда у ключа-владельца сервера нет приложения, оно ищется по адресу приложения: берутся приложения того же аккаунта Bitrix24, созданные владельцем ключа, чей appUrl указывает ровно на этот адрес приложения. Сессия восстанавливается, и заголовок продолжает приходить на всё время жизни сессии.
Приложения на ключе авторизации (vibe_app_*) работают как раньше — у них цепочка «сервер → ключ → приложение» есть, и она остаётся главной.
Влияние на интеграторов
Менять ничего не нужно. Если приложение обходило проблему повторным открытием плейсмента или собственным хранением токена — эти костыли можно снять.
NEW-0729-6: управляющий ключ владельца, аккаунт которого ждёт удаления, получает 503
Было
Заморозка аккаунта на время запроса на удаление данных действовала на веб-кабинет и на
обычные ключи приложения, но не на управляющие ключи: владелец с аккаунтом в состоянии
ожидания удаления продолжал выпускать, ротировать и удалять ключи через /v1/keys.
Стало
Управляющий ключ владельца, аккаунт которого находится в процессе удаления данных,
получает 503 с кодом ACCOUNT_PENDING_ERASURE и заголовком Retry-After: 3600 — так
же, как это давно работает для обычных ключей приложения. Если запрос на удаление
отменён, ключ начинает работать снова без перевыпуска.
FIX-0729-7: деплой galaxy-приложения проверяет доступность и отдаёт шаги
Было
Успешный деплой galaxy-приложения через POST /v1/infra/servers/:id/deploy возвращал success: true, status: "running" без data.steps[] и без проверки того, что приложение действительно отвечает по HTTP. Контейнер, который поднялся, но не слушал порт, всё равно рапортовался как успех — отличить рабочий деплой от сломанного было нельзя. Плюс GET /v1/infra/servers/:id для такого приложения показывал runtime: null и порт по умолчанию — развёрнутый рантайм и порт не сохранялись.
Стало
Успешный ответ несёт data.steps[]: шаг { step: "build", status: "ok" } и — когда проба выполнялась — шаг { step: "healthcheck", status: "ok" | "warning", httpCode, healthPath }. status: "ok" означает, что приложение ответило 2xx/3xx на data.appUrl; warning — ответило 4xx/5xx (всё ещё достижимо, деплой прошёл). Контейнер, который поднялся, но не ответил по HTTP на своём порту, теперь честно завершается 502 GALAXY_APP_START_FAILED (то же семейство, что крах после старта — в теле остаётся хвост buildLog), а не рапортуется как успех. Необязательное поле healthPath (по умолчанию /) в теле деплоя задаёт путь пробы. GET /v1/infra/servers/:id теперь отражает развёрнутый runtime и порт.
Влияние на интеграторов
Приложение, которое отвечает по HTTP на порту деплоя, ничего не заметит. Приложение, которое стартует, но не начинает отвечать в окне пробы, получит 502 GALAXY_APP_START_FAILED вместо ложного успеха — убедитесь, что оно слушает порт, указанный при деплое, и что healthPath возвращает ответ. Шаг build в data.steps[] и сохранение runtime/порта в GET доступны сразу; сама HTTP-проба (шаг healthcheck и 502 GALAXY_APP_START_FAILED) раскатывается — включается на платформе постепенно, поэтому до её активации деплой ведёт себя как раньше (без пробы).
FIX-0729-8: серверы просыпаются после погашения долга и на постоплатном счёте
Было
Портал с постоплатным биллингом, ушедший в минус до лимита овердрафта, глушил серверы и помечал их «заморожено биллингом». После пополнения счёта API снова отвечал 200, но серверы так и оставались выключенными: POST /v1/infra/servers/{id}/wake возвращал отказ (SERVER_WAKE_BLOCKED), и снять пометку можно было только обращением в поддержку. На предоплатном биллинге тот же сценарий отрабатывал нормально.
Стало
Как только баланс перестаёт быть отрицательным, пометка снимается и серверы поднимаются автоматически — одинаково на предоплате и постоплате. Частичное пополнение, оставляющее баланс в минусе, снова открывает API, но серверы держит выключенными: они возвращаются к работе, когда долг закрыт полностью. Серверы, остановленные по другим причинам (истёк доступ, остановка вручную), пробуждение не затрагивает.
FIX-0729-9: деплой не рапортует ложный успех после отката усиленного юнита
Было
Деплой на POST /v1/infra/servers/:id/deploy мог отрапортовать успех (healthcheck:ok, hardening:warning), обслуживая при этом ответ чужого процесса, занявшего порт приложения. Это происходило, когда усиленный (hardened) юнит падал, деплой автоматически откатывался на обычный юнит, но откат не освобождал порт — а на первой же проверке чужой держатель порта отвечал 200. В результате выкладка сообщала об успехе, хотя новая версия порт так и не заняла и в продакшене продолжала работать предыдущая.
Стало
Если после отката порт по-прежнему держит тот же процесс, что заблокировал усиленный юнит (откатанный юнит порт не занял), деплой завершается ошибкой healthcheck:error с явным сообщением о том, что порт всё ещё занят, вместо ложного healthcheck:ok. Штатный откат, при котором порт занимает уже новый экземпляр приложения, по-прежнему завершается успехом.
FIX-0729-10: кэш метаданных учитывает портал и схемы полей
Было
Кэш GET /v1/statuses был описан как привязанный к личному ключу, а схемы GET /v1/{entity}/fields не были отражены в разделе кэширования. Клиент не видел в документации, какие повторные запросы получают X-Cache: HIT и как запросить свежую схему полей.
Стало
GET /v1/statuses описан как кэш портала на 5 минут. GET /v1/{entity}/fields описан как кэш схемы полей на 5 минут с учётом портала, ключа авторизации, сущности, параметров пути, параметров запроса и языка ответа. Обход кэша через Cache-Control: no-cache доступен для всех этих чтений, а для /fields также доступен refresh=true.
Влияние на интеграторов
Код менять не нужно. Повторные чтения метаданных меньше нагружают очередь портала, а заголовки X-Cache и X-Cache-Bypass-Reason показывают, был ли использован кэш.
NEW-0729-11: дельта последних диалогов по параметру updatedAfter
GET /v1/chats/recent принимает updatedAfter — момент в формате ISO 8601, начиная с которого нужно вернуть изменённые диалоги. Это отдельный режим ответа: data приходит плоским массивом диалогов, а meta несёт mode со значением delta и returned с их числом.
Размером выборки в этом режиме управляет сервер — читается одна страница до 200 диалогов. Переданный limit на неё не влияет и возвращается обратно в meta.requestedLimit вместе с применённым meta.appliedLimit. Когда дельту не удалось подтвердить полной — Битрикс24 сообщил, что за отданной страницей есть ещё диалоги, либо форму ответа не удалось разобрать, — в meta приходит truncated со значением true: в этом случае не сдвигайте updatedAfter, а получите полный список постраничным режимом с курсором lastMessageDate. Число возвращённых строк признаком полноты не является.
Граница включительна — диалог, у которого dateUpdate равен переданному моменту, попадает в ответ. Дата обязана нести явное смещение или Z: значение вида 2026-06-29 10:00:00 читается по-разному в зависимости от часового пояса сервера и отклоняется с 400 INVALID_PARAMS. Тем же кодом отклоняется сочетание updatedAfter с offset или lastMessageDate — постраничная навигация и дельта используют разные курсоры.
NEW-0729-12: транзиентный 503 на границе платформы теперь говорит, через сколько повторять
Когда все реплики бэкенда на мгновение недоступны — например в момент редеплоя galaxy-приложения — граница платформы отвечает 503 SERVICE_UNAVAILABLE на префиксах /api/ и /v1/. В теле было только человекочитаемое «Retry in a few seconds», и машинного признака повторяемости не было: клиент не мог отличить секундную дыру от постоянной недоступности и либо ронял задачу, либо ретраил наугад.
Теперь этот ответ несёт HTTP-заголовок Retry-After: 5 и поле retryAfter: 5 внутри объекта error — рядом с code и message, ровно так же, как это делает сама платформа для своего транзиентного 503. Прежние вызовы не меняются: код SERVICE_UNAVAILABLE и статус остались теми же, добавлены заголовок и поле. Текст сообщения теперь дополнительно ссылается на заголовок — на него по-прежнему не следует опираться, ветвитесь по error.code. Полный список кодов — /docs/errors.
Тот же блок границы отвечает и на таймаут чтения от бэкенда. В этом случае запрос до бэкенда доехал и, возможно, всё ещё выполняется, поэтому для не-идемпотентных операций перед повтором сверьте состояние сущности.
Отдельно стоит сказать, чего это изменение НЕ делает: оно не устраняет причину, по которой апстримы бэкенда оказались недоступны. Оно делает ошибку честной и машинно-понятной, чтобы клиент корректно подождал и повторил.
BC-0729-13: пакетный запрос отклоняет сортировку у сущностей, чей метод Битрикс24 её не умеет
Поддержка старого формата до: 29.07.2026
Было
Одиночный список сущности и её POST /v1/departments/search уже отвечали 400 INVALID_SORT_FIELD, если метод Битрикс24 за списком не принимает порядок сортировки. Подзапрос общего POST /v1/batch этой проверки не имел: та же сортировка уходила в метод, тот её выбрасывал, и подзапрос возвращал успех с несортированным списком. Получалось расхождение на одном и том же запросе — через одиночный маршрут отказ, через пакетный молчание.
Стало
Подзапрос POST /v1/batch с действием list или search проверяет то же самое и отвечает 400 INVALID_SORT_FIELD в errors своего подзапроса; остальные подзапросы выполняются как обычно. Отказ приходит до обращения к Битрикс24. Проверяются оба написания — и sort, и order. Это касается двух сущностей: подразделений (departments) и телефонных линий (telephony-lines). Хранилищ это НЕ касается — их метод сортировку умеет, и отказ у них снят отдельной записью этого же выпуска.
Что делать интеграторам
Если подзапрос к одной из этих двух сущностей передавал сортировку — уберите её: она никогда не применялась, список приходил в порядке Битрикс24. Нужен свой порядок — сортируйте полученный список на своей стороне. Подзапросы без сортировки, а также limit, offset, select и фильтр, работают как раньше. Параллельная поддержка прежнего поведения не предусмотрена: прежнее поведение состояло в том, что параметр молча игнорировался, — сохранять было бы нечего.
BC-0729-14: `defaultOperatorData` у открытых линий стал объектом, пустое значение — `null`
Поддержка старого формата до: 27.01.2027
Было
Поле объявлялось массивом, и незаданное значение приводилось к []. Настоящий тип другой: Битрикс24 отдаёт объект вида { "NAME": …, "AVATAR": … }, когда данные оператора по умолчанию заданы. То есть GET /v1/openline-configs и GET /v1/openline-configs/:id обещали в fields массив, а при заполненном значении присылали объект.
Стало
Тип поля — object; незаданное значение приходит как null, а не как []. Заполненное значение приходит объектом, как и раньше. Соседние kpiFirstAnswerList и kpiFurtherAnswerList — настоящие массивы строк, они по-прежнему приводятся к [].
Что делать интеграторам
Код, который считал длину или перебирал это поле (defaultOperatorData.length, .map, .forEach), сломается на null — замените проверку на if (config.defaultOperatorData) { … } и читайте поля объекта напрямую. Если вы ориентировались на fields и ждали массив — сверьтесь с новым типом object.
BC-0729-15: телефонные линии: запись `serverName`, сортировка, фильтр и смещение больше не теряются молча
Поддержка старого формата до: 29.07.2026
Было
serverName в схеме числился обычным изменяемым полем, но Битрикс24 его не хранит и не возвращает: POST /v1/telephony-lines с этим полем отвечал 201, а значение исчезало; PATCH того же поля упирался в ошибку самого Битрикс24. Отбор, порядок и смещение вели себя так же тихо: метод Битрикс24 за этим списком не принимает входных параметров вовсе, поэтому ?order[number]=desc, ?name=…, ?filter[number]=…, ?offset=50 и те же значения в теле POST /v1/telephony-lines/search отбрасывались, а список приходил 200 — и выглядел отсортированным, отфильтрованным и перелистнутым, хотя не был ничем из этого. В подзапросе POST /v1/batch тем же образом терялась сортировка.
Стало
Все четыре случая стали явной ошибкой до обращения к Битрикс24. Запись serverName — 400 READONLY_FIELD; любая сортировка — 400 INVALID_SORT_FIELD; любой фильтр — 400 UNSUPPORTED_FILTER; ненулевое смещение — 400 UNSUPPORTED_OFFSET. Отказ фильтру приходит на списке, в поиске, в POST /v1/telephony-lines/aggregate и в подзапросах обоих пакетных запросов — общего POST /v1/batch и POST /v1/telephony-lines/batch; отказ сортировке и смещению — на списке, в поиске и в подзапросе общего пакетного запроса (агрегат этих параметров не читает). В общем POST /v1/batch отказ приходит в errors своего подзапроса, а остальные подзапросы выполняются как обычно; если отклонены все подзапросы, запрос отвечает 400, и разбор по подзапросам остаётся в errors. В POST /v1/telephony-lines/batch иначе: нарушение контракта отбора отклоняет весь пакет одним 400 с номером подзапроса в сообщении, ни один подзапрос не выполняется. Само поле serverName осталось видимым в GET /v1/telephony-lines/fields с признаком «только для чтения», так что понять его назначение по-прежнему можно.
Что делать интеграторам
Если вы передавали serverName при создании или обновлении линии — уберите поле: значение всё равно никогда не сохранялось. Если полагались на сортировку, фильтр или смещение — ни одно из них никогда не применялось; список внешних линий приложения приходит целиком одной страницей, поэтому сортируйте, отбирайте и разбивайте его на страницы на своей стороне. Обычный список без этих параметров, а также limit и select, работают как раньше. Параллельная поддержка прежнего поведения не предусмотрена: прежнее поведение состояло в том, что параметр молча игнорировался, — сохранять было бы нечего.
NEW-0729-16: список хранилищ можно сортировать
Сортировка по полям хранилища работает на GET /v1/storages и в POST /v1/storages/search: ?sort=-id, ?order[name]=desc и те же значения в теле поиска. Минус перед именем поля означает убывание.
Порядок выдачи меняют id, name, entityType, entityId, rootFolderId — по каждому из них проверено на живом аккаунте, что по возрастанию и по убыванию приходят разные выдачи. Поля code и module метод тоже принимает без ошибки, но на проверенных аккаунтах значение этих колонок одинаково у всех хранилищ, поэтому порядок по ним не меняется — не полагайтесь на них как на сортировку.
Раньше любая сортировка хранилищ отклонялась с 400 INVALID_SORT_FIELD. Отказ был ошибочным: он был выведен из описания метода Битрикс24, а метод порядок сортировки принимает и применяет — это проверено на живом портале, где выдача по возрастанию, по убыванию и без сортировки различаются. Если вы обходили этот отказ, сортируя список у себя, обходной путь можно убрать; он продолжает работать.
Заодно порядок выдачи стал устойчивым. К вашей сортировке добавляется id последним ключом, а список без сортировки теперь приходит по возрастанию id — раньше он приходил в неопределённом порядке аккаунта. Это не косметика: список отдаётся страницами по 50, и при сортировке по неуникальному полю (например по названию) строка с тем же значением могла на границе страниц попасть в две страницы сразу или пропасть из выдачи вовсе. Теперь порядок полный и однозначный, поэтому и постраничный обход повторяем. Если вы сортировали по id сами, ваше направление сохраняется — второй ключ не добавляется.
Разбиение на страницы у хранилищ идёт постранично по 50 записей, поэтому при сортировке запрашивайте нужный объём одним вызовом (limit до 5000), а не листайте страницы вручную.
FIX-0729-17: перезапуск galaxy-приложения через /reboot
Было
Для galaxy-приложения (kind=GALAXY_APP) у POST /v1/infra/servers/:id/reboot не было рабочего сценария: вызов возвращал 422 VM_MISSING (у контейнера нет собственной виртуальной машины), а единственным путём восстановления зависшего приложения оставалось удаление — оно стирает постоянный том /data.
Стало
/reboot перезапускает контейнер приложения как «пинок» самовосстановления — постоянный том /data при этом сохраняется. Вызов принимается в статусе running или error и возвращает совещательный вердикт: restarted (контейнер перезапущен) и healthy (контейнер запустился и перестал перезапускаться — проверка контейнера, не HTTP-ответа приложения), а при healthy: false — поле hint о том, как залить исправленную версию. Перезапуск не снимает состояние ошибки — крашащееся приложение авторитетно чинится редеплоем исходников через POST /v1/infra/servers/:id/deploy. Новые коды ошибок для этого сценария: GALAXY_APP_REBOOT_USE_AGENT_CONTROLS (409 — приложением агента или бота управляют из его панели), GALAXY_APP_BUSY (409 — на хосте выполняется другая команда, в ответе Retry-After), GALAXY_HOST_UNREACHABLE (502 — хост недоступен). Перезагрузка обычного сервера не изменилась.
NEW-0729-18: totalDefault — умолчание по meta.total на самом ключе
У API-ключа появилась настройка totalDefault: заказывать ли подсчёт количества спискам этого ключа, когда сам запрос не передал withTotal. Значение true — присылать meta.total, false — не присылать, null — наследовать платформенное умолчание. По умолчанию у всех ключей null.
Настройка правится в кабинете на странице ключей и через PATCH /v1/keys/:id полем totalDefault (управляющий ключ vibe_live_). Перевыпуск ключа через POST /v1/keys/:id/rotate настройку сохраняет — как и режим доступа. Изменение попадает в журнал аудита.
Действующее значение видно в GET /v1/me — блок totalDefault показывает всю цепочку: key (настройка ключа), platform (платформенное умолчание), effective (что получится, если запрос не передаст withTotal) и source — откуда взялось действующее значение.
Настройка нужна там, где менять код интеграции дороже, чем один раз переключить ключ: она задаёт умолчание сразу всем спискам этого ключа. Точечно её перекрывает параметр запроса withTotal.
NEW-0729-19: meta.nextAfterId — курсор следующей страницы при сортировке по id
Ответы GET /v1/{entity} и POST /v1/{entity}/search получили необязательное поле meta.nextAfterId — идентификатор последней отданной записи, строкой.
Поле приходит, когда выполнены три условия сразу: у сущности числовой идентификатор и поддержка курсорного листания, сортировка запроса — строго id по возрастанию, и meta.hasMore равен true. На последней странице поля нет: идти уже некуда. Сегодня условиям отвечают сделки, лиды, контакты, компании, предложения и элементы смарт-процессов.
Значение передаётся обратно тем же фильтром, которым курсорное листание делалось и раньше: filter[>id]=<nextAfterId> при сортировке id по возрастанию. Нового параметра запроса не появилось — поле лишь избавляет от чтения идентификатора из последней строки ответа вручную.
Такое листание не зависит от смещения и не дорожает к концу коллекции, поэтому для обходов в десятки тысяч записей оно предпочтительнее, чем растущий offset.
NEW-0729-20: withTotal — списку можно не заказывать подсчёт количества
Списочные вызовы приняли необязательный параметр withTotal. У GET /v1/{entity} это параметр запроса ровно с двумя допустимыми значениями — true и false; у POST /v1/{entity}/search — поле тела с булевым значением. Любая другая запись читается как «параметр не передан», ошибки не будет.
withTotal=false — просьба не считать количество. Там, где платформа может её выполнить, подсчёт у Битрикс24 не заказывается и meta.total в ответе отсутствует. Там, где обойтись без подсчёта нельзя, параметр не действует и meta.total приходит как раньше. Поэтому наличие поля проверяйте, а не предполагайте.
Листать надо по meta.hasMore — он считается по полноте страницы и доводит цикл «читай, пока hasMore» до конца независимо от того, был ли подсчёт. При сортировке строго по id по возрастанию ответ дополнительно несёт meta.nextAfterId, который передаётся обратно в filter[>id].
Если параметр не передан, значение берётся из настройки ключа, а при её отсутствии — из платформенного умолчания. Действующее сейчас значение и всю эту цепочку показывает блок totalDefault в GET /v1/me.
Когда точное количество действительно нужно, спрашивайте его прямо: POST /v1/{entity}/aggregate с функцией count отдаёт число одним вызовом, без выгрузки записей. Считать количество постраничным обходом коллекции не надо — это десятки вызовов вместо одного и самый дорогой способ узнать одну цифру.
FIX-0729-21: meta.hasMore в списках считается по полноте страницы, а meta.total может отставать до минуты
Было
meta.hasMore в ответах GET /v1/{entity} и POST /v1/{entity}/search выводился из meta.total: «есть ещё» означало «offset плюс отданные строки меньше общего количества». Пока количество считалось на каждый вызов, это совпадало с правдой.
Стало
Платформа перестаёт запрашивать у Битрикс24 подсчёт количества на каждый повторный вызов с той же парой «ключ и запрос» — подсчёт непропорционально дорог для портала. Отсюда два наблюдаемых следствия.
meta.hasMore на таких ответах считается по полноте страницы: пришла полная страница — «возможно, есть ещё»; пришла неполная — список закончился. Цикл «читай, пока hasMore» по-прежнему доходит до конца всегда. Если коллекция ровно кратна limit, последний шаг вернёт пустой список — это штатный признак конца.
meta.total остаётся числом и остаётся на месте, но становится информативным: он может отставать до минуты, поэтому число отданных строк может оказаться больше него.
Влияние на интеграторов
Менять ничего не нужно, если листание идёт по meta.hasMore — это рекомендованный способ. Если код опирается на meta.total как на точную границу цикла или проверяет «отдано не больше, чем total», переключитесь на meta.hasMore. Точное количество на момент запроса даёт POST /v1/{entity}/aggregate с функцией count.
2026-07-28
NEW-0728-1: приложение стёртого автора больше не выдаёт новые пользовательские токены
Когда автор приложения удалил свои персональные данные, приложение перестаёт выдавать токены новым пользователям. Раньше такой запрос доходил до Битрикс24 и создавал рабочий токен вместе с именем и почтой нового пользователя, хотя автора в системе уже нет.
Возврат из GET /v1/oauth/callback в этом случае приходит на ваш redirect_uri с ?error=app_unavailable — той же формой, что уже используют token_exchange_failed, invalid_domain и profile_fetch_failed. POST /v1/oauth/placement-session отвечает 403 с кодом APP_UNAVAILABLE — тем же кодом отвечает и обработчик размещения, куда Битрикс24 открывает виджет приложения (раньше там приходил 401 с общим USER_AUTH_REQUIRED, который читался как проблема авторизации пользователя).
Уже выданные токены и сессии этого приложения не продлеваются. Ранее работавшие вызовы не затронуты: пока автор активен, поведение обоих эндпоинтов прежнее.
NEW-0728-2: снятие зависшего лока стало кросс-репличным; ответ DELETE /lock несёт broadcast и localLock
DELETE /v1/infra/servers/:id/lock теперь рассылает снятие лока на все реплики платформы, поэтому снимает зависший лок и тогда, когда он держится на другой реплице (частый случай под горизонтальным масштабированием). Ответ дополнен полями broadcast (снятие разослано по флоту, best-effort) и localLock (держался ли лок на этой реплице). Поле released теперь описывает только текущую реплику и не является подтверждением снятия по всему флоту — при зависшем локе на другой реплице released может быть false, хотя лок реально снят; не опрашивайте эндпоинт в цикле до released: true, повторите операцию. Прежние вызовы работают без изменений (поля добавлены аддитивно). Дополнительно зависший exec-лок теперь гарантированно снимается серверным авто-сбросом вскоре после истечения TTL.
Ответ POST /v1/infra/servers/:id/exec при 502 EXEC_BUSY для galaxy-приложения теперь несёт error.hint с честным путём восстановления (общий exec-канал хоста; эскалация к платформенной команде — DELETE /lock тут не помогает, так как блокирует мьютекс агента). Ответ POST /v1/infra/servers/:id/deploy при 409 GALAXY_APP_BUSY дополнен error.hint, retryable: true, retryAfter и заголовком Retry-After.
NEW-0728-3: указатели на открытые линии, чек-листы задач и ТЗ приложений в ответах самоописания
Ответ GET /v1/guide дополнен указателем data.appBlueprints — ссылка на документацию готовых ТЗ приложений и условие ответа 403 BLUEPRINTS_DISABLED.
Для ключа со скоупом imopenlines в ответ добавлен блок data.openLines: описание раздела, ссылки на все семь страниц документации и разграничение двух групп эндпоинтов. Настройка линий и действия оператора доступны на любом портале. Статистика дашборда до прихода обновления Битрикс24 отвечает 422 METHOD_NOT_YET_AVAILABLE, а без права на просмотр статистики — 403 B24_TARIFF_RESTRICTION.
В ответе GET /v1/me блок api._rules получил три новых указателя — на чек-листы задач, открытые линии и ТЗ приложений.
Поля аддитивные, существующие клиенты не затронуты. Сами эндпоинты не менялись.
FIX-0728-4: деплой в галактику восстанавливает оборванный туннель хоста
Было
Деплой галакси-приложения на хост, туннель которого молча оборвался под нагрузкой сборки (в том числе «фантомно-CONNECTED» хост — флаг завис, а туннель уже мёртв), зацикливался на GALAXY_HOST_UNREACHABLE / GALAXY_DEPLOY_INTERRUPTED: платформа не чинила туннель сама, и повторные попытки клиента били в тот же мёртвый туннель.
Стало
Такой обрыв на пути деплоя теперь запускает фоновый ремонт туннеля хоста, поэтому честный повтор попадает уже на восстановленный туннель и деплой доезжает. Коды ошибок и их «повторяемая» семантика не изменились — меняется только поведение (самовосстановление).
BC-0728-5: единый конверт 404 для несуществующих маршрутов /v1
Поддержка старого формата до: 28.07.2026
Прежняя форма тела не возвращается — переходного периода с двойным форматом нет, изменение действует с даты публикации.
Было
Запрос на несуществующий путь или неподдерживаемый HTTP-глагол под /v1/ отвечал телом веб-сервера вне единого конверта API:
{
"message": "Route GET:/v1/dealz not found",
"error": "Not Found",
"statusCode": 404
}
Стало
Тот же запрос отвечает в едином конверте V1 с новым кодом ROUTE_NOT_FOUND. HTTP-статус прежний — 404:
{
"success": false,
"error": {
"code": "ROUTE_NOT_FOUND",
"message": "Route GET:/v1/dealz not found. Check GET /v1/guide for available endpoints and verbs."
}
}
ROUTE_NOT_FOUND означает «такого маршрута или глагола не существует» — сверьте путь со списком в GET /v1/guide. Не путайте с ENTITY_NOT_FOUND и доменными кодами вида *_NOT_FOUND: там маршрут существует, не найден запрошенный объект. Вне /v1/ форма тела 404 не изменилась.
Что делать интеграторам
Ветвитесь по error.code, а не по форме тела. Клиент, который разбирал поля message, error и statusCode прежнего тела на путях /v1/, должен перейти на единый конверт success и error.code.
BC-0728-6: календарь: неизвестное имя в select отвечает ошибкой, а не пустым объектом
Поддержка старого формата до: 28.07.2026
Было
GET /v1/calendar-events (а также GET /v1/calendar-events/{id}, POST /v1/calendar-events/search и calendar-events-подвызовы POST /v1/batch) при незнакомом имени поля в select (например dateFrom — такого поля нет, реальное имя from) молча возвращали объекты, состоящие из одного id. Клиент считал, что сузил трафик, а на деле терял данные.
Стало
Незнакомое имя поля в select отвечает 400 UNKNOWN_SELECT_FIELD с перечнем допустимых имён (Available: …). Имена в формате Битрикс24 (DATE_FROM) и алиасы дат (updatedAt) по-прежнему принимаются и проецируют канонический ключ — ошибку вызывают только имена, которые не разрешаются ни в одно поле схемы. На остальных сущностях поведение прежнее: незнакомое имя даёт предупреждение в meta.warnings, без ошибки.
Что делать интеграторам
Брать имена полей из GET /v1/calendar-events/fields и убрать из select несуществующие имена (dateFrom/dateTo → from/to). Клиенты, не передающие select или передающие корректные имена, не затронуты.
NEW-0728-7: календарь: поля occurrenceIndex и version
У событий календаря появились два поля только для чтения. occurrenceIndex — порядковый номер вхождения в развёрнутой серии повторяющегося события, с нуля: строки серии делят один id, и пара id + occurrenceIndex однозначно идентифицирует строку набора. version — монотонный счётчик изменений события, растёт при каждом изменении и не зависит от региональных настроек аккаунта. Рецепт диффа: запросите GET /v1/calendar-events с select=id,version, сравните пары со своим снимком и дочитайте изменённые события по id.
NEW-0728-8: чаты: эхо ограничения limit в meta
Три эндпоинта чатов — GET /v1/chats/recent, GET /v1/chats/:dialogId/messages и GET /v1/chats/:dialogId/users — при срезании переданного limit до допустимого диапазона дополняют ответ полем meta с парой requestedLimit и appliedLimit: сколько запросили и сколько применено. Когда limit в диапазоне, meta не добавляется — конверт прежний. Для чтения сообщений задокументирован реальный потолок облачного Битрикс24 — не больше 50 записей за вызов независимо от limit, продолжение читается курсором lastId.
NEW-0728-9: взаимные алиасы полей дат updatedAt и createdAt
Поля дат в каталоге сущностей носят два семейства имён: одни сущности объявляют updatedAt и createdAt, другие — updatedTime и createdTime. Теперь члены пары принимаются на вход взаимозаменяемо: в filter и select — на всех сущностях, где объявлен парный ключ, в sort — на сущностях с camelCase-схемой полей. Например, updatedTime на сущности с полем updatedAt работает как updatedAt, и наоборот. Канонические имена полей в ответах не меняются — алиас действует только на входе.
NEW-0728-10: POST-алиас поиска по Базе знаний
Поиск документов Базы знаний 2.0 принимает и POST /v1/note/documents/search с JSON-телом { "query": "...", "limit": 20 } — для агентов, которые ожидают поиск POST-запросом по аналогии с остальными сущностями. Канонической остаётся форма GET /v1/note/documents/search с query-параметрами. Обе формы принимают только query и limit, при передаче параметра и в теле, и в query приоритет у тела.
NEW-0728-11: лента: limit до 200 записей за запрос
GET /v1/posts принимает limit от 1 до 200. Страница ленты Битрикс24 фиксирована в 50 записей — при limit больше 50 платформа склеивает до четырёх страниц в один ответ. Значение больше 200 отвечает прежним 400 INVALID_LIMIT. В meta добавлено поле returned — фактическое число записей в ответе, а meta.nextOffset при многостраничном чтении выводится из окна ответа, чтобы цепочка страниц продолжалась как раньше.
FIX-0728-12: календарь: честные offset и hasMore, детерминированный порядок
Было
GET /v1/calendar-events при любом offset возвращал начало одного и того же набора: запрошенный диапазон приходит от Битрикс24 одним массивом без пагинации, поэтому каждая «страница» повторяла первую, meta.hasMore оставался true, а хвост набора за пределами первой страницы был недоставаем.
Стало
Полный набор сортируется детерминированно — по началу события from, при равенстве по id, затем по occurrenceIndex — и из него отдаётся честное окно от offset до offset + limit. meta.total — число вхождений в наборе: повторяющиеся события развёрнуты по-вхожденно, строки серии делят один id. meta.hasMore отвечает true только пока за окном остаются записи. Порядок элементов в ответе стал детерминированным и может отличаться от прежнего.
Влияние на интеграторов
Обход набора через offset теперь отдаёт весь диапазон. Клиентам, которые обходили дубли страниц самостоятельно, ничего менять не нужно — дублей больше нет.
FIX-0728-13: select принимает объявленные имена Битрикс24 и предупреждает о незнакомых полях
Было
Параметр select понимал только канонические имена полей из GET /v1/{entity}/fields. Имя в другом написании — исходное имя Битрикс24 (DATE_FROM, UF_DEPARTMENT) или другой регистр — молча выпадало из проекции: поля не было в ответе без какого-либо признака ошибки, а запрос из одних таких имён вырождался в объекты с единственным полем id. Пакетные вызовы select не применяли вовсе: и глобальный POST /v1/batch, и пакетный вызов одной сущности POST /v1/{entity}/batch возвращали полные объекты.
Стало
select принимает канонические имена без учёта регистра, объявленные исходные имена Битрикс24 (DATE_FROM проецирует from, UF_DEPARTMENT — departmentId) и взаимные алиасы полей дат — поле приходит в ответе под каноническим ключом. Незнакомое имя больше не теряется молча: список, поиск и получение записи по id дополняют ответ массивом meta.warnings с записями { "code": "UNKNOWN_SELECT_FIELD", "field": "<имя>" } — до 10 предупреждений на ответ. Оба пакетных вызова применяют select к операциям списка и поиска так же, как одиночные эндпоинты; получение записи по id внутри глобального пакета select не применяет. Глобальный вызов дополнительно отдаёт предупреждения о незнакомых именах в meta по каждому своему вызову. Пакетный вызов одной сущности предупреждений не несёт и жёсткого отклонения незнакомых имён не выполняет — незнакомое имя там по-прежнему просто отсутствует в ответе.
Влияние на интеграторов
Ответы одиночных эндпоинтов только дополняются. В пакетных вызовах клиент, который передавал select и при этом читал поля за его пределами, теперь получит только запрошенные поля — уберите select из вызова или перечислите в нём все нужные поля.
FIX-0728-14: быстрый отказ оконного поиска при таймауте портала
Было
Поиск POST /v1/{entity}/search с широким диапазоном дат разбивается на временные окна. Таймаут Битрикс24 на первом окне пропускался, и остальные окна выполнялись каждое со своим таймаутом: до ответа проходило 60–75 секунд, после чего приходил 503 BITRIX_TIMEOUT. Если поздние окна успевали, возможен был частичный 200 с неполными данными после той же минуты ожидания.
Стало
Таймаут первого окна (BITRIX_TIMEOUT) завершает запрос сразу: ответ 503 с кодом BITRIX_TIMEOUT и заголовком Retry-After приходит примерно за 15 секунд, остальные окна не выполняются. Частичный 200 при таймауте первого окна больше невозможен — это осознанный размен: окна одинаковы по форме, таймаут первого предсказывает таймауты остальных, а частичный ответ после минуты ожидания провоцировал волны повторов. Таймаут любого последующего окна обрабатывается как раньше — окно пропускается, ответ может быть частичным.
Влияние на интеграторов
Повторяйте запрос по заголовку Retry-After. Клиенты, которые полагались на частичный ответ при перегрузке портала, теперь получают быстрый 503 — сузьте диапазон дат или повторите позже.
FIX-0728-15: Galaxy-деплой из .zip: честная причина ошибки извлечения вместо EMPTY_BUILD_CONTEXT
Деплой Galaxy-приложения из .zip теперь возвращает реальную (санитизированную) причину ошибки извлечения архива в buildLog 502-ответа, а не вводящее в заблуждение «EMPTY_BUILD_CONTEXT»/общее сообщение.
FIX-0728-16: отбой лимитера времени операций Битрикс24 возвращается как `OPERATION_TIME_LIMIT` с `Retry-After`
Было
Когда портал отбивал вызов метода, израсходовавшего бюджет рабочего времени, платформа
отдавала 429 RATE_LIMITED с Retry-After: 2, а сам вызов повторяла до трёх раз. Против
адресного отказа на несколько минут эти повторы не помогали, а Retry-After: 2 вводил в
заблуждение: клиент возвращался через две секунды и получал тот же отказ.
Стало
Такой отбой возвращается кодом OPERATION_TIME_LIMIT — тем же, что отдаёт портал, — с
Retry-After, посчитанным от известного срока снятия ограничения, и понятным текстом в
userMessage. Повторы отключены: ограничение адресное — на тройку «портал + ключ +
метод», ровно так же, как его накладывает сам Битрикс24, — и до истечения срока платформа
отбивает вызовы этого метода сама, не обращаясь к порталу. Остальные методы портала, как и
тот же метод под другим ключом, не затронуты. Прочие отказы 429 (в том числе
RATE_LIMITED и QUEUE_OVERFLOW) не изменились.
Соблюдайте Retry-After: раньше срока тот же вызов пройти не может. Тяжёлые чтения стоит
разредить по времени или сузить — меньше полей, меньше страница, POST /v1/batch.
FIX-0728-17: Значение * в select возвращает все поля
Было
Привычная для Битрикс24 запись select: ["*"] (и ["*", "UF_*"]) на одиночных эндпоинтах приводила к обратному результату: * не совпадает ни с одним объявленным полем, поэтому в ответе оставался только id. Признака ошибки не было — запись просто приходила пустой.
Стало
* и UF_* (в любом регистре) распознаются как запрос «вернуть все поля»: отбор полей не применяется, приходит полная запись. Незнакомое имя, переданное рядом со звёздочкой, не отклоняется — ответ дополняется предупреждением UNKNOWN_SELECT_FIELD. Это работает одинаково в списке, поиске, получении записи по id и в обоих пакетных вызовах — POST /v1/batch и POST /v1/{entity}/batch. У событий календаря, где незнакомое имя поля отвечает ошибкой 400, значение * ошибкой не считается.
Влияние на интеграторов
Ничего менять не нужно. Клиент, переносивший запросы из портального кода Битрикс24 вместе с select: ["*"], начнёт получать полные записи вместо объектов с одним id.
FIX-0728-18: оконный поиск останавливается, набрав запрошенное, и сообщает о неполном окне
Было
POST /v1/{entity}/search с широким диапазоном дат разбивает диапазон на временные окна и объединяет их результаты. Обход шёл до конца диапазона даже тогда, когда запрошенных limit записей уже набрано: поиск с limit: 50 по диапазону в несколько месяцев вычитывал весь диапазон целиком — отвечал долго и создавал на аккаунте Битрикс24 нагрузку, несопоставимую с размером ответа. Если внутри одного окна подходящих записей оказывалось больше, чем отдаёт одно чтение окна, окно возвращало только начало своего набора, и делало это молча: ни признака в ответе, ни способа дочитать остаток (оконный поиск отвергает offset > 0 кодом UNSTABLE_OFFSET_PAGINATION).
Молчала и вторая причина неполноты, на аккаунтах, где окна читаются пакетами: набрав предельные 5000 записей, поиск перестаёт отправлять оставшиеся окна и отдаёт срезанный префикс — в ответе об этом тоже ничего не было.
Стало
Обход окон прерывается, как только набрано больше уникальных записей, чем запрошено в limit. В ответе по-прежнему не больше limit записей, а признак «есть ещё» несёт hasMore. При досрочной остановке meta.total равен количеству набранного, то есть это нижняя оценка числа подходящих записей, а не полный счёт по диапазону — ровно так уже вёл себя этот поиск на аккаунтах с пакетным чтением окон, теперь поведение единое.
Неполная выдача больше не молчит — ни по одной из двух причин, независимо от того, читаются окна по одному или пакетами, на сущностях, чей метод списка Битрикс24 отдаёт постранично. Ответ дополняется предупреждением { "code": "WINDOW_TRUNCATED", "field": "…", "message": "…" } в массиве meta.warnings, где field — поле диапазона, по которому шло разбиение. Код предупреждения один и тот же в обоих случаях — ветвиться по нему можно, не разбирая текст; message называет сработавшую причину: одно окно держало больше записей, чем отдаёт одно чтение окна, либо набраны предельные 5000 записей и оставшиеся окна не отправлялись.
Исключения названы прямо. Три сущности предупреждения не получат никогда, потому что их метод списка Битрикс24 не отдаёт постранично: файлы и папки (/v1/files, /v1/folders) и рабочие группы (/v1/workgroups). Там запрос идёт одиночным вызовом, limit в Битрикс24 не передаётся вовсе, и аккаунт отдаёт свою страницу примерно на 50 записей: окно, в котором лежит 200 дисковых объектов, вернёт 50 и промолчит, как и до правки. У страниц (/v1/pages), сайтов (/v1/sites) и событий календаря (/v1/calendar-events) метод списка тоже не постраничный, но он отдаёт весь запрошенный набор за один вызов — там предупреждение просто недостижимо, а неполноты не возникает.
Дополнительно снижена нагрузка, которую этот поиск создаёт на аккаунте Битрикс24: вырожденная нижняя граница по id больше не уходит в Битрикс24. Снимаются две формы, и опираются они на разное. >= со значением 0 или меньше и > с отрицательным значением исключают только отрицательные id — они тавтологичны при одном допущении неотрицательности. > ровно с нулём (filter[>id]=0 — именно её передают в начале обхода курсором) исключает запись с id = 0, то есть дополнительно опирается на автоинкрементную нумерацию записей Битрикс24 от единицы; это целевой случай правки, и он снимается сознательно. Граница >=id=1 сохраняется — гард намеренно узкий и смотрит только на значения 0 и меньше.
Состав выдачи не меняется ни на одной сущности, где Битрикс24 действительно применяет фильтр по id. Известное исключение — воронки (/v1/categories): среди них есть запись с id: 0 («Общая»), но метод списка воронок игнорирует filter целиком, поэтому и до, и после правки в ответ приходит полный набор воронок. Форма ответа прежняя.
Влияние на интеграторов
Не считайте meta.total точным числом подходящих записей на широком диапазоне дат — при досрочной остановке это нижняя оценка; ориентируйтесь на hasMore. Дочитать оконный поиск постранично нельзя (offset > 0 отвергается), поэтому «есть ещё» закрывается либо большим limit (до 5000), либо более узким диапазоном дат.
Проверяйте meta.warnings на WINDOW_TRUNCATED: это признак неполной выдачи, и пагинацией он тоже не лечится — сузьте диапазон дат или добавьте фильтров, чтобы поиск перестал упираться в потолок. Одного hasMore для этого недостаточно: оконное разбиение работает только при offset === 0, поэтому запрос следующей страницы уйдёт уже не оконным и даст другой результат. Поиск по узкому диапазону, который не разбивается на окна, не затронут.
Учтите границу оконного поиска: сортировка применяется внутри окна, а не поверх объединения окон. Окна строятся от старых к новым и склеиваются в этом же порядке, после чего результат режется до limit; глобальной сортировки по объединению нет. Поэтому запрос с сортировкой по убыванию и небольшим limit по широкому диапазону дат возвращает самые СТАРЫЕ подходящие записи, а не самые новые. Само поведение не новое, но досрочная остановка закрывает сортировку после слияния как способ починки (записи поздних окон больше не вычитываются), поэтому называем это прямо. Нужен настоящий «последние N по дате» — либо сузьте диапазон так, чтобы разбиение на окна не включалось, либо возьмите диапазон одним запросом с большим limit и отсортируйте на своей стороне.
2026-07-27
NEW-0727-1: фавикон приложения одной строкой — /_gw/icon
Фавикон во вкладке браузера теперь ставится одной статической строкой без id сервера:
<link rel="icon" href="/_gw/icon">
/_gw/icon — платформенный путь на домене приложения; он всегда отдаёт текущую загруженную иконку. Одна загрузка POST /v1/infra/servers/:id/icon управляет и карточкой в каталоге Bitrix24, и фавиконом: перезалили иконку — фавикон обновится сам (~5 минут), пересобирать приложение не нужно. Свой статический файл иконки в приложение класть больше не нужно. Строка совместима с созданием приложения одним запросом (id не требуется).
Дополнительно ответ POST /v1/infra/servers/:id/deploy и создания приложения одним запросом POST /v1/infra/servers теперь возвращает запись в warnings[], если иконка ещё не загружена — с точным эндпоинтом для загрузки (иконка грузится отдельным запросом, id известен только после создания). Тот же warnings[] по-прежнему подсказывает, если не заданы displayName/description. Успешный деплой без иконки или названия больше не выглядит завершённым молча.
NEW-0727-2: source-at-create: ошибка SOURCE_AT_CREATE_GALAXY_ONLY теперь несёт подсказку с путём к галактике
Отказ POST /v1/infra/servers с source, который не удалось разместить в галактике (на портале в режиме «обе стратегии» без открытого galaxy-хоста, либо на портале только со standalone), теперь дополнительно несёт error.hint — с понятным путём: как получить galaxy-хост и/или как задеплоить в два шага на выделенный сервер. Код и текст ошибки не изменились.
FIX-0727-3: galaxy-приложения: PATCH /sleep и PATCH /port теперь отвечают 400 — управляйте ими со страницы «Галактики»
Было
Для приложения, размещённого в галактике (GALAXY_APP), PATCH /v1/infra/servers/:id/sleep возвращал 200 и записывал sleepAfterMinutes, а PATCH /v1/infra/servers/:id/port отвечал 404/409 — расходясь с задокументированным контрактом /v1/me (deployment.galaxyApp), где ни одно V1-действие жизненного цикла к galaxy-приложению не применяется.
Стало
Оба вызова для galaxy-приложения отвечают 400 с error.code = "GALAXY_APP_USE_GALAXY_ROUTE" и ничего не меняют. Настраивайте авто-сон приложения через маршрут галактики; порт у galaxy-приложения закреплён за хостом и не задаётся. Для обычных (standalone) серверов поведение /sleep и /port не изменилось.
FIX-0727-4: galaxy-приложение восстанавливается после обрыва туннеля хоста
Было
Если у galaxy-хоста обрывался защищённый туннель (сервер в статусе RUNNING, но связь потеряна), развёртывание и выполнение команд galaxy-приложения возвращали 502 GALAXY_HOST_UNREACHABLE, а запрос логов — пустой ответ с подсказкой. Хост оставался недостижим до ручного ремонта: повтор того же запроса упирался в ту же ошибку сколь угодно долго.
Стало
Платформа теперь сама восстанавливает туннель хоста в фоне, не задерживая ответ. Повтор того же запроса проходит, как только хост переподключается (обычно в течение минуты). Параллельные развёртывания/команды на один общий хост не запускают дублирующее восстановление.
Влияние на интеграторов
Код ошибки и форма ответа не изменились — 502 GALAXY_HOST_UNREACHABLE (для развёртывания и выполнения команд) по-прежнему помечен как повторяемый, а логи по-прежнему отдают пустой список с подсказкой. Изменилось только то, что теперь повтор приводит к успеху, а не к вечной ошибке. Продолжайте повторять запрос по своей обычной политике на этот код и на пустой ответ логов.
Затронутые эндпоинты: POST /v1/infra/servers/:id/deploy, POST /v1/infra/servers/:id/exec, GET /v1/infra/servers/:id/logs
NEW-0727-5: предупреждение, когда changelog деплоя некуда опубликовать
Ответ POST /v1/infra/servers/:id/deploy теперь возвращает запись в warnings[], если в запросе передан changelog, а деплой не создал новую версию исходников. Заметка о релизе привязана к версии, поэтому без неё текст никуда не сохраняется и в ленту канала приложения в мессенджере Bitrix24 не уходит.
Причина видна в поле source того же ответа: хранилище исходников выключено (feature-disabled-platform или feature-disabled-portal), сохранение не удалось (save-failed) либо загруженные байты совпали с предыдущей версией. Раньше такой деплой отвечал обычным успехом, и узнать, что заметка потерялась, было нечем. Предупреждение приходит и в JSON-режиме, и в событии done при ?stream=true.
FIX-0727-6: Список bizproc-templates без явного select возвращает все поля, включая id
Было
GET /v1/bizproc-templates и POST /v1/bizproc-templates/search без явного select возвращали только поле documentType. Без id клиент не мог выполнить последующие update/delete — список был бесполезен без второго запроса с явным select.
Стало
Оба вызова без select возвращают полный набор объявленных полей записи (id, moduleId, entity, documentType, autoExecute, name, description, modified, isModified, userId). Явный select работает как прежде. Форма запроса не изменилась.
NEW-0727-7: загрузка файла в документ Базы знаний возвращает assetMarkdown сразу
Загрузка файла через POST /v1/note/documents/{documentId}/files раньше отдавала только { id }, поэтому за готовым блоком для вставки в документ приходилось идти вторым запросом в GET /v1/note/documents/{documentId}/files/{id} либо собирать разметку [[image fileId=N]] руками.
Теперь ответ несёт весь объект файла — id, documentId, name, size, mimeType, assetType, assetMarkdown — то есть ту же форму, что отдаёт GET. Загрузили картинку, взяли assetMarkdown из ответа, вставили в текст документа и вызвали PATCH — второй запрос больше не нужен.
Изменение аддитивное: поле id осталось на месте и с тем же значением, поэтому клиент, который читает только его, продолжает работать без правок. Если конкретный портал вернёт объект без assetMarkdown, лишних полей платформа не придумывает — в ответе будет то, что пришло от Битрикс24.
FIX-0727-8: границы `ttlSeconds` у токенов доступа в машинной схеме совпали с поведением
Было
Схема OpenAPI для POST /v1/infra/servers/{id}/access-tokens обещала ttlSeconds в диапазоне от 60 секунд до 30 суток. Платформа же с самого начала принимала от 300 секунд до 315 360 000 (десять лет) и отклоняла всё за этими пределами кодом 400 INVALID_TTL. Поэтому клиент или генератор клиентских библиотек, взявший минимум прямо из схемы, получал жёсткий отказ на значении, которое схема сама и предлагала, а вариант «Бессрочно» из интерфейса выглядел недоступным через API. Текстовая документация всё это время была верна — расходилась только машинная схема.
Стало
Схема берёт границы и значение по умолчанию из тех же констант, которыми проверяется запрос, поэтому разойтись им больше нечем: минимум 300, максимум 315 360 000, по умолчанию 86 400.
Влияние на интеграторов
Поведение эндпоинта не менялось — менялось только то, что о нём написано в машинной схеме. Если вы генерировали клиента по OpenAPI и он валидировал ttlSeconds на своей стороне, перегенерируйте его: прежний клиент отклонял бы корректные значения больше 30 суток и разрешал бы заведомо отказные меньше 300 секунд.
NEW-0727-9: Приложение в плейсменте может авто-ресайзить свой iframe
Приложение, встроенное в плейсмент, теперь может сообщать платформе высоту своего контента, и платформа растит iframe под неё — раньше высоту фиксировал Битрикс24 и высокий контент обрезался. Приложение постит сообщение родительскому окну: window.parent.postMessage({ type: 'vibe:resize', height: <пиксели> }, '*'). Принимается тип vibe:resize или vibe:setHeight с числовым полем height; targetOrigin должен быть '*' — браузер сверяет его с непосредственным родителем окна. Пересчитывайте высоту при изменении контента, например через ResizeObserver. Полное описание и рекомендации — раздел «Авто-высота iframe» руководства по рантайму приложения.
Функция активируется на стороне платформы по аккаунтам Битрикс24; если ресайз пока не срабатывает, для вашего аккаунта она ещё не активна.
FIX-0727-10: offset в списках считается по записям, а не по страницам
Было
Битрикс24 отдаёт списки страницами по 50 и трактует смещение как номер страницы, а не как число записей. Мы передавали offset как есть, поэтому он молча округлялся вниз до кратного 50: offset=0, offset=7 и offset=49 возвращали одну и ту же первую страницу — без ошибки и без предупреждения. Обход выборки шагом меньше 50 записей зацикливался на первой странице, а шаг ровно в 50 работал и создавал впечатление, что параметр исправен.
Стало
offset считается по записям на generic-списках: GET /v1/{entity}, POST /v1/{entity}/search, POST /v1/batch (действие list) и GET /v1/{entity}/{id}/activities. offset=7 начинает выборку с 8-й записи. Смещение и limit независимы: ?limit=2&offset=51 вернёт ровно две записи, начиная с 52-й. Битрикс24 по-прежнему отдаёт страницами по 50 — Вайбкод запрашивает страницу, покрывающую нужную позицию, и отбрасывает лишнее начало; цена — не более одной дополнительной страницы у Битрикс24 на запрос.
Заодно исправлены два следствия. meta.hasMore учитывает смещение: раньше у сущностей, чей список Битрикс24 отдаёт под именованным ключом — сделки, контакты, компании, лиды, задачи, заказы, товары, счета и другие, всего два десятка, — он сравнивал только длину страницы с общим количеством и оставался true на последней странице при ненулевом offset. GET /v1/{entity}/{id}/activities теперь уважает limit: метод Битрикс24 не принимает ограничение выборки, поэтому раньше приходила вся страница целиком независимо от запрошенного значения.
Если выборка на запрошенной позиции оказалась пустой из-за фильтрации на стороне Битрикс24, ответ несёт meta.warnings с кодом OFFSET_BEYOND_FETCHED_PAGE — раньше это выглядело как пустой список без объяснения.
Для глубокой навигации по большим выборкам курсор по ключу (filter[>id] с order[id]=asc) по-прежнему надёжнее смещения: он не зависит от глубины и устойчив к параллельным изменениям.
Влияние на интеграторов
Менять ничего не нужно: смещение, кратное 50, работает как раньше, и код, который его так и использовал, продолжит работать без правок.
Два момента стоит проверить. Первый — GET /v1/{entity}/{id}/activities с явным limit: раньше приходила вся страница (до 50 записей) независимо от значения, теперь придёт ровно запрошенное количество. Если код полагался на то, что за один вызов вернётся больше запрошенного, увеличьте limit или пройдите выборку постранично. Второй — обход выборки шагом меньше 50: раньше он зацикливался на первой странице, теперь идёт вперёд, поэтому цикл, который «выручал» лишний выход по счётчику, начнёт возвращать новые записи.
Остаточные исключения, где смещение НЕ построчное. Во-первых, четыре сущности generic-слоя, у которых метод Битрикс24 округляет смещение до границы страницы, а расширить выборку сверх запрошенного размера нельзя, не рискуя потерять записи: calendar-events, calendar-sections, telephony-lines, workgroups. Смещение там осталось прежним. Во-вторых, отдельные эндпоинты с собственными обработчиками, которые в это изменение не входили: /v1/warehouses, /v1/bookings, /v1/posts, /v1/requisite-links, /v1/lists, /v1/timeline-logs и история стадий в /v1/crm-extras. Их поведение не изменилось.
У четырёх сущностей generic-слоя выше смещение осталось прежним, но meta.hasMore у них стало точнее: раньше на последней странице при ненулевом смещении он мог сказать «больше нет», хотя записи оставались.
FIX-0727-11: статус закрытого тикета обратной связи больше не показывается как «в работе»
Было
Тикет, попавший под детекцию probe-кампании, показывался автору со статусом NEW независимо от того, что с ним реально происходило. Фильтр списка работает по настоящему статусу, поэтому закрытый тикет одновременно попадал во вкладку «Решено» и рисовался бейджем «в работе» — один и тот же тикет противоречил сам себе. Затронуты GET /v1/feedback и GET /v1/feedback/{id}.
Стало
Маскируется только то состояние, для которого маска и заводилась: авто-архив. Закрытый тикет отдаёт RESOLVED, тикет в работе — свой реальный статус, а авто-архив по-прежнему приходит как NEW. Ключи с доступом к обратной связи (management, скоуп vibe:feedback) как и раньше видят настоящий статус.
Влияние на интеграторов
Клиент, который читал status и ожидал NEW у такого тикета, теперь получит его фактический статус — это и есть исправление. Дополнительно: у тикета, отмеченного детекцией и уже закрытого, отзыв (PATCH {"status":"WITHDRAWN"}) и ответ автора теперь отклоняются с 409 FEEDBACK_CLOSED, как у любого закрытого тикета; раньше они проходили, потому что гейт сверялся с замаскированным статусом.
FIX-0727-12: Connect-ключи: сохранённые права авторитетны — vibe:ai / vibe:search больше не добавляются автоматически
Было
Ключ, выданный через VibeCode Connect, при каждом запросе автоматически получал платформенные права vibe:ai и vibe:search, даже если они не запрашивались и не были согласованы. Такой ключ мог обращаться к AI-эндпоинтам (/v1/chat/completions, /v1/ai/*) и поиску (/v1/search), и расход шёл со счёта аккаунта, к которому привязан портал.
Стало
Сохранённые на ключе права теперь авторитетны — платформа не расширяет их автоматически. Ключ, выданный через Connect, обращается к AI- и поиск-эндпоинтам только если соответствующее право реально присутствует в ключе; иначе ответ 403. Ротация ключа сохраняет этот признак. Обычные ключи, созданные в кабинете, поведения не меняют.
FIX-0727-13: Приложения: производный ключ и синхронизация прав не выдают больше, чем есть у вызывающего ключа
Было
Вызов POST /v1/apps авторитетным ключом (выданным через VibeCode Connect или производным от него) минтил парный ключ приложения с полным набором платформенных прав по умолчанию (vibe:infra, vibe:ai, vibe:search, vibe:storage) — даже если у самого вызывающего ключа этих прав не было. Так же PATCH /v1/apps/:id мог записать в объявление приложения vibe:*-право, которого у ключа нет. В обоих случаях производный ключ получал возможность обращаться к AI и поиску за счёт аккаунта, к которому привязан портал.
Стало
Производный ключ получает ровно те платформенные права, что реально есть у вызывающего ключа. Если у авторитетного ключа права нет, запрос с ним в теле возвращает 403 SCOPE_GRANT_REQUIRES_CONSENT, а список несогласованных прав приходит в error.details.unconsented. Права, которые у ключа есть (например согласованный vibe:storage), проходят как раньше. Срок жизни производного ключа наследуется от вызывающего. Синхронизация прав приложения на парные ключи больше никогда не добавляет vibe:* авторитетному ключу — сужение прав при этом по-прежнему применяется. Обычные ключи, созданные в кабинете, поведения не меняют: платформенные права им по-прежнему выдаются по умолчанию.
FIX-0727-14: таймаут вызова Битрикс24 стал управляемым, внутренний повтор после таймаута отменён
Было
Платформа всегда обрывала HTTP-вызов к Битрикс24 на 15-й секунде, а для методов чтения после обрыва делала одну внутреннюю повторную попытку — итого до ~30 секунд до ответа 503 BITRIX_TIMEOUT. Повтор при этом запускал второе параллельное выполнение того же вызова на портале: обрыв соединения не останавливает работу Битрикс24 над запросом.
Стало
- Лимит времени одного вызова к Битрикс24 настраивается платформой (по умолчанию прежние 15 секунд). На порталах, где Битрикс24 отвечает медленно, платформа может поднять лимит — запросы, которые раньше стабильно завершались
503 BITRIX_TIMEOUT, доживают до реального ответа и возвращают данные. - Внутренняя повторная попытка после таймаута отменена для всех методов.
503 BITRIX_TIMEOUTна чтениях приходит примерно вдвое быстрее (~15 секунд вместо ~30), и запрос больше не выполняется на портале дважды. Повторы по429(rate limit) не тронуты. - Текст ошибки
Bitrix24 did not respond within 15sподставляет фактический лимит (например,within 60s) — не опирайтесь на константу в тексте.
2026-07-25
NEW-0725-1: deploy: необязательное поле changelog
POST /v1/infra/servers/:id/deploy принимает новое необязательное поле changelog — обычный текст до 2000 символов с описанием, что изменилось в этой версии. При выходе новой версии приложения текст публикуется подписчикам в ленту канала приложения в мессенджере Bitrix24; если поле не передано, в ленту уходит только номер версии. Поле доступно и в JSON-, и в multipart-режиме деплоя. Прежние вызовы деплоя работают без изменений.
NEW-0725-2: вызовы моделей по короткоживущему токену партнёрской системы
Было
Ручки AI-роутера /v1/chat/completions, /v1/embeddings, /v1/audio/transcriptions и /v1/models принимали только обычный ключ платформы.
Стало
Те же ручки (и их /v1/ai/...-алиасы) дополнительно принимают короткоживущий токен нового типа. Его получает партнёрская система Битрикс24 по своему подписанному каналу; токен привязан к порталу и конкретному сотруднику, живёт один час и допущен ровно к этим восьми маршрутам — на любом другом пути ответ SCOPE_FORBIDDEN. Расход по такому токену считает сама платформа и списывает синхронно в AI-квоту портала, поэтому при исчерпании квоты вызов отбивается ещё до обращения к модели. Доступны только модели, включённые в программу квоты — перечень отдаёт /v1/models под этим же токеном.
Коды отказа, которые теперь может вернуть этот маршрут: TOKEN_INVALID, SCOPE_FORBIDDEN, MODEL_NOT_IN_QUOTA_PROGRAM, CREDENTIAL_NOT_PLATFORM, ACCOUNT_FROZEN, PORTAL_DELETED, PORTAL_BLOCKED, PORTAL_SUSPENDED. Конверт прежний: success и error с полями code и message.
Поведение обычных ключей платформы не изменилось: без токена нового типа ответы прежние, байт в байт.
2026-07-24
FIX-0724-1: дела: нераспознанное имя поля фильтра теперь отклоняется с 400 UNKNOWN_FILTER_FIELD
Было
GET /v1/activities и POST /v1/activities/search молча отбрасывали неизвестный ключ фильтра: запрос возвращал 200 success с фильтром, урезанным до пустого — то есть отдавал весь (owner-scoped или вообще весь) набор дел. Например {"filter":{"ownerTypeId":2,"ownerId":3,"bogusField":123}} игнорировал bogusField и возвращал все дела родительской сделки. Это расходилось с документацией и с поведением других сущностей CRM (компании, счета), где такой фильтр уже отклонялся.
Стало
Имя поля фильтра, которого нет в схеме дела — и которое не является пользовательским полем UF_*, ключом id или спец-токеном — теперь отклоняется до вызова Bitrix24 с 400 UNKNOWN_FILTER_FIELD и списком доступных полей, как уже делают companies/quotes/contacts. Полный список фильтруемых полей возвращает GET /v1/activities/fields.
Влияние на интеграторов
Фильтрация по реальным полям дела (в camelCase или в родном ВЕРХНЕМ регистре Bitrix24), по полям UF_*, операторы (>=, <, ! и т.п.), диапазоны и AND/NOT работают как прежде. Если вы полагались на молчаливый сброс нераспознанного ключа — уберите его из фильтра.
NEW-0724-2: /fields бизнес-процессных действий и роботов отдаёт названия и описания полей
Было
GET /v1/bizproc-activities/fields и GET /v1/bizproc-robots/fields описывали каждое поле только типом и флагом readonly, без человекочитаемых подписей.
Стало
По каждому из 12 полей теперь приходят label и description по-русски, что упрощает построение форм и подсказок. Типы полей не изменились.
FIX-0724-3: Скачивание своих файлов из хранилища больше не возвращает 403
Было
GET /v1/storage/objects/:key мог вернуть 403 при обращении к объекту, которым владелец ключа законно владеет, но который физически лежит под префиксом другого «семейства» хранилища (например, файлы сервера, видимые в списке разработчика). Объект показывался в списке, но скачать его, получить presigned-ссылку или сделать HEAD не удавалось.
Стало
Область доступа временных ключей учитывает фактическое семейство объекта (портал при этом остаётся привязан к контексту вызывающего), поэтому скачивание (?download), потоковая отдача (?inline) и HEAD для собственных объектов работают независимо от семейства. Проверка владения не изменилась — по чужому объекту по-прежнему приходит 404.
FIX-0724-4: GET /v1/workflows учитывает параметр limit
Было
GET /v1/workflows принимал limit, но молча его игнорировал — всегда возвращалась целая страница запущенных бизнес-процессов (до 50), сколько бы ни запросили.
Стало
limit уважается: в ответе не больше запрошенного числа записей. Значения больше 50 набираются постранично (потолок — 500); meta.total по-прежнему показывает общее число запущенных процессов.
FIX-0724-5: деплой: приложение в оборачивающей папке архива больше не падает с ENOENT package.json
Было
Деплой (POST /v1/infra/servers/:id/deploy) на standalone-сервер архива, в котором проект завёрнут в единственную папку верхнего уровня (например, myapp/package.json вместо package.json в корне), падал на шаге установки:
npm error enoent Could not read package.json ... open '/opt/app/package.json'
Загруженный архив оставался соседом распакованного содержимого, поэтому авто-выравнивание единственной оборачивающей папки не срабатывало (в корне оказывалось две записи — архив и папка), и package.json оставался вложенным.
Стало
Загруженный архив удаляется до шага выравнивания, поэтому единственная оборачивающая папка «схлопывается», package.json оказывается в корне деплоя, и установка проходит штатно.
Влияние на интеграторов
Действий не требуется. Плоские архивы (файлы в корне архива) работают как прежде; для гарантии можно паковать плоско: tar -czf build.tar.gz -C <папка_проекта> ..
FIX-0724-6: эндпоинты /v1/users* перестали возвращать 403 и 500 на ключах с доступом user
Было
На ключе только для чтения (READONLY) с доступом user вызов GET /v1/users/me возвращал 403 WRITE_BLOCKED_READONLY_KEY, хотя это ридовый эндпоинт. Отдельно GET /v1/users, GET /v1/users/:id, POST /v1/users/search и GET /v1/users/fields возвращали 500 INTERNAL_ERROR на порталах, где у одного из пользовательских полей (UF_*) пустое определение.
Стало
GET /v1/users/me работает на ключе только для чтения. Остальные /v1/users* возвращают данные и пропускают поле с пустым определением вместо падения.
Влияние на интеграторов
Действий не требуется — прежние вызовы продолжают работать, а ранее падавшие сценарии теперь отвечают корректно.
NEW-0724-7: доставка callback bizproc-активити и роботов на Black Hole-приложение
Регистрация bizproc-активити или робота с handler, указывающим на ваш деплой-сервер Black Hole, теперь приводит к надёжной доставке callback выполнения (с токеном события, блоком авторизации, кодом и свойствами) в приложение. Раньше такой callback мог не дойти: онлайн-события Битрикс24 не повторяются, а спящий или просыпающийся сервер терял вызов. Платформа перехватывает handler при регистрации, ставит вызов в устойчивую очередь и повторяет доставку с будильником сервера и откатами.
Затрагивает POST /v1/bizproc-activities и POST /v1/bizproc-robots (а также их изменение). Новый код ошибки SERVER_APP_MISMATCH (400): сервер Black Hole за указанным handler должен принадлежать тому же приложению, что регистрирует активити. Регистрация такого handler через /v1/batch не поддерживается — используйте одиночный запрос (BIZPROC_CALLBACK_BATCH_UNSUPPORTED). Кроме того, POST /v1/bizproc-robots теперь заранее требует code, name и handler — при их отсутствии возвращается 400 MISSING_REQUIRED_FIELDS вместо сырой ошибки Битрикс24 (как уже было у активити).
Возможность раскатывается постепенно и включается по аккаунтам: до включения на вашем аккаунте регистрация проходит как прежде, без управляемой доставки. После включения меняется поведение batch-регистрации: попытка зарегистрировать BH-handler через /v1/batch начинает отклоняться (BIZPROC_CALLBACK_BATCH_UNSUPPORTED) — переведите такие регистрации на одиночный POST /v1/bizproc-activities или /v1/bizproc-robots.
2026-07-23
NEW-0723-1: библиотека чертежей приложений — ТЗ по API-ключу
Готовые технические задания популярных приложений теперь доступны по ключу: GET /v1/app/blueprints/:slug?locale=ru|en возвращает сырой markdown ТЗ (Content-Type: text/markdown). Эндпоинт требует Authorization: Bearer <ключ>; неизвестный или скрытый чертёж — 404 BLUEPRINT_NOT_FOUND. Ссылку на ТЗ несёт копируемый «Промт для AI» при создании ключа — AI-агент скачивает ТЗ тем же ключом. Прежний анонимный путь /api/public/blueprints/:slug.md удалён.
NEW-0723-2: исходники удалённого сервера: доступ, уборка и честный ответ на вычищенные байты
Исходники переживают сервер — это давняя гарантия платформы, но добраться до них через API было нельзя: весь набор /v1/infra/servers/:id/sources* отвечал 404 на удалённый сервер, поэтому владелец не мог ни посмотреть свои версии, ни снять с них тег, ни удалить. Для версии с тегом published или manual это был тупик: снятие тега — единственный разрешённый способ обойти 409 PROTECTED_BY_TAG, а именно оно и было недоступно.
Теперь на удалённом сервере работают чтение и уборка: список версий, метаданные версии, скачивание, tag, PATCH, DELETE и cleanup. Сохранение новых версий (POST /sources) по-прежнему отвечает 404 — мёртвый сервер новых депозитов не принимает.
Чтобы удалённый сервер вообще можно было найти, GET /v1/infra/servers принимает ?includeDeleted=true. По умолчанию выдача не меняется. В каждой строке появилось поле deletedAt (null у живых серверов).
Отдельно: версия, чьи байты уже вычищены из хранилища, теперь отвечает 410 с кодом SOURCE_VERSION_BYTES_PURGED вместо 404. Разница существенная — 404 утверждал, что версии нет, тогда как запись о ней жива, а восстановление другое: перезалить архив, а не искать его в другом месте. Код приходит на скачивании и на деплое по {"source": {"versionId": "vN"}}.
Затронутые эндпоинты: GET /v1/infra/servers, POST /v1/infra/servers/:id/deploy, GET /v1/infra/servers/:id/sources, GET /v1/infra/servers/:id/sources/:versionId/download — контракт исходников описан на странице Хранилище исходников
FIX-0723-3: деплой galaxy-приложения: обрыв туннеля при сборке больше не маскируется под «host unreachable»
Было
Если во время сборки на galaxy-хосте моргал туннель и здорового контейнера этого деплоя в итоге не оказывалось (сборку прервало, контейнер не поднялся, канал exec был занят или приложение крашилось), деплой (POST /v1/infra/servers/:id/deploy) возвращал 502 GALAXY_HOST_UNREACHABLE с советом «повторите, когда хост переподключится». Хост при этом часто был доступен — совет вводил в заблуждение, а вызывающая сторона не видела реальную причину (например, что её приложение падает при старте).
Стало
Если после моргания туннеля хост доступен, но деплой не довёл приложение до рабочего состояния, деплой возвращает новый код 502 GALAXY_DEPLOY_INTERRUPTED — «хост доступен, но деплой прервался до старта приложения: отправьте тот же деплой повторно; если приложение раз за разом не стартует, сначала почините его (команду запуска, порт, зависимости, переменные окружения или лимит памяти)». Это по-прежнему повторяемая ошибка — слот цел, удалять и пересоздавать его не нужно. Код GALAXY_HOST_UNREACHABLE теперь остаётся только для действительно недоступного хоста (ни одна попытка проверки до него не достучалась). Если приложение действительно крашится в цикле, это надёжно выявляется уже на повторном деплое обычной проверкой живости (код GALAXY_APP_START_FAILED).
NEW-0723-4: подсказка в ошибке таймаута шага деплоя
Ошибка DEPLOY_TIMEOUT у POST /v1/infra/servers/:id/deploy теперь несёт структурированное поле error.hint (reason, recovery, recoveryAction), привязанное к зависшему шагу (error.step). Для команд пользователя (install, preStart) подсказка объясняет, что команда не завершилась в отведённое время, и советует сделать её неинтерактивной и завершающейся, а долгоживущие сервисы запускать из команды start или в фоне (docker compose up -d). Для шага установки рантайма (runtime — платформенный шаг, а не команда пользователя) и прочих служебных шагов подсказка указывает на возможный обрыв туннеля и на POST /v1/infra/servers/:id/repair. Поле аддитивное: прежние error.code, error.message и error.step не изменились, менять интеграцию не нужно; подсказка приходит и в JSON-режиме, и в SSE-событии error.
FIX-0723-5: ошибка шага деплоя показывает реальную причину, а не безобидное предупреждение
Было
При падении шага деплоя поле data.steps[].stderr (и, как следствие, error.message) могло нести только безобидное предупреждение из одного потока, теряя реальную причину сбоя.
Стало
Оба потока возвращаются вместе, с метками stderr: и stdout:; реальная причина больше не скрывается. Форма ответа и имя поля не изменились.
FIX-0723-6: сортировка списка по неуникальному полю больше не теряет записи на второй странице
Было
Запрос списка или поиск с сортировкой по неуникальному полю (например по датовому begindate) при выборке больше 50 записей мог молча вернуть меньше записей, чем есть: на границе страницы часть записей с одинаковым значением поля сортировки терялась. Ответ приходил с кодом 200, без признака неполноты. Затронуты сущности на основе CRM smart-process — сделки, лиды, контакты, компании, предложения, счета и элементы смарт-процессов /v1/items/{entityTypeId} — на путях GET /v1/{entity}, POST /v1/{entity}/search, в подвызовах POST /v1/batch и per-entity POST /v1/{entity}/batch. Тот же класс нестабильности затрагивал и числовую агрегацию (POST /v1/{entity}/aggregate с sum/avg/min/max/groupBy): выборка записей для агрегата шла без порядка, поэтому на объёмах больше 50 записей часть строк могла теряться и искажать результат.
Стало
К сортировке автоматически добавляется вторичный ключ по id — порядок становится строго определённым, и постраничная выборка не теряет и не дублирует записи независимо от поля сортировки. Пользовательская сортировка остаётся основным ключом; записи с одинаковым значением поля упорядочиваются по id по возрастанию. Менять запросы не нужно.
FIX-0723-7: переоткрытие тикета комментарием больше не оставляет штамп решения
Было
Комментарий команды через POST /v1/feedback/:id/comments, возвращающий тикет из RESOLVED или WITHDRAWN обратно в активный статус (NEW, REVIEWING, AWAITING_USER, NEEDS_REVIEW), не сбрасывал resolvedAt и resolvedBy. Они «зависали» от прошлого закрытия, и на чтении (GET /v1/feedback/:id, GET /v1/feedback) переоткрытый тикет выглядел одновременно активным и решённым.
Стало
Такой комментарий очищает resolvedAt и resolvedBy — на чтении активный тикет больше не несёт даты решения. resolution при этом не очищается: он отражает текст самого комментария. Комментарий, ставящий RESOLVED, по-прежнему проставляет штамп; переход в ARCHIVED и обычный переход между активными статусами штамп не трогают. Поведение согласовано с уже действовавшим сбросом на PATCH /v1/feedback/:id.
FIX-0723-8: фильтр списков statuses / departments / storages / currencies / products больше не игнорируется молча
Было
GET /v1/statuses, /v1/departments, /v1/storages, /v1/currencies, /v1/products работают через legacy-методы Битрикс24 (crm.status.list, department.get, disk.storage.getlist, crm.currency.list, crm.product.list), которые молча игнорируют нефильтруемые ключи и операторы. Неизвестное или неподдерживаемое поле фильтра — например filter[system] у статусов, filter[module] у хранилищ или filter[price] у товаров — а также операторы $gt / $contains / $ne возвращали 200 со всей таблицей. Клиент получал полный набор вместо ожидаемого подмножества — тихий отказ с неверными данными.
Стало
Для этих сущностей фильтр проверяется до вызова Битрикс24: разрешены только те поля, которые метод действительно фильтрует (проверено вживую). Остальные поля, операторы и пустые множества возвращают 400 UNSUPPORTED_FILTER с перечнем фильтруемых полей. Разрешённые поля по сущностям: statuses — id, entityId, statusId, name, sort, semantics, categoryId; departments — id, name, parentId, headId; storages — id, name, code, entityType, entityId; products — id, name, code, xmlId, active, sectionId, sort. crm.currency.list не фильтрует ничего — любой filter у /v1/currencies возвращает 400 с подсказкой отфильтровать на стороне клиента.
BC-0723-9: POST /v1/apps больше не возвращает поля prefix и suffix в ответе на создание
Поддержка старого формата до: 21.07.2026
Было
Ответ POST /v1/apps на создание приложения содержал два значения на vibe_app_ — короткий prefix и полный rawKey. Короткий prefix ошибочно принимали за ключ, и запрос с ним возвращал 401.
Стало
Ответ на создание содержит одно значение vibe_app_ — рабочий rawKey. Поля prefix и suffix остаются в GET /v1/apps и GET /v1/apps/:id для отображения маскированного ключа.
Что делать интеграторам
Используйте поле rawKey из ответа на создание как X-Api-Key. Маскированный префикс, если он нужен, берите из GET /v1/apps или GET /v1/apps/:id вместо ответа на создание.
FIX-0723-10: POST /v1/batch сохраняет телефон и почту при создании и обновлении лидов и контактов
Было
Через общий POST /v1/batch значения телефона и почты (мультиполя) при создании или обновлении лида либо контакта терялись. Вызов возвращал успех, но поле не сохранялось. Те же данные через одиночный POST /v1/leads или PATCH /v1/contacts/:id и через POST /v1/{entity}/batch сохранялись корректно.
Стало
Общий POST /v1/batch сериализует мультиполя так же, как одиночные вызовы. Телефон и почта сохраняются при создании и обновлении.
FIX-0723-11: Поиск и research больше не отвечают 402 INSUFFICIENT_BALANCE при положительном балансе
Было
POST /v1/search и POST /v1/research с платформенным движком (bitrix-search) на персональном ключе могли вернуть 402 INSUFFICIENT_BALANCE даже при достаточном балансе Vibe на счёте портала. Предварительная проверка баланса искала счёт по владельцу ключа, а счёт с недавних пор один на весь портал — и не находился.
Стало
Проверка и списание баланса всегда идут по счёту портала. При положительном балансе запрос выполняется и списывается корректно; 402 возвращается только при реальной нехватке средств. Форма запроса и ответа не изменилась.
FIX-0723-12: POST /v1/apps честнее сообщает о необходимости подписки Маркетплейса на cloud-shared пути
Было
При создании приложения через cloud-shared путь выпуска ключа (единая cloud↔box-модель, раскатка по кольцу порталов) на портале без активной подписки «BitrixGPT + Маркетплейс» POST /v1/apps возвращал непрозрачный 502 CONNECTOR_APP_INSTALL_FAILED без указания причины.
Стало
Отказ по подписке теперь классифицируется заранее: до обращения к коннектору POST /v1/apps проверяет авторитетное состояние подписки портала и, если оно отсутствует, сразу возвращает 403 с кодом B24_MARKET_SUBSCRIPTION_REQUIRED (или B24_MARKET_TRIAL_USED, если демо уже использован), понятным сообщением и ссылкой на оформление в error.details.upgradeUrl. Если состояние не авторитетно, тот же результат срабатывает при явном отказе коннектора по подписке. Прочие отказы cloud-shared выпуска (модуль не установлен, доступ запрещён портал-админом, прочие ошибки) классифицируются как прежде.
Влияние на интеграторов
Менять ничего не нужно, успешные вызовы не затронуты. Тем, кто обрабатывал 502 CONNECTOR_APP_INSTALL_FAILED при создании приложения, стоит дополнительно ловить 403 B24_MARKET_SUBSCRIPTION_REQUIRED / B24_MARKET_TRIAL_USED и подсказывать пользователю оформить подписку на портале.
FIX-0723-13: запуск сервера не отвечает ошибкой, если машина уже работает
Было
POST /v1/infra/servers/:id/start для сервера в состоянии error
вызывал запуск машины у облака и любой отказ отдавал как 502 PROVIDER_ERROR. Если машина к этому
моменту уже работала — например, её подняло автоматическое восстановление после вытеснения, — облако
отвечало отказом «машина уже в состоянии RUNNING», и вызов возвращал ошибку на операции, которая
фактически удалась. Клиент видел 502 и не мог отличить это от настоящего сбоя.
Стало
Такой отказ распознаётся как идемпотентный успех: если машина уже работает или находится в
переходном состоянии, вызов возвращает 200 и сервер переходит в provisioning, как при обычном
запуске. Настоящие отказы — недостаточно прав, исчерпана квота, машина не найдена — по-прежнему
возвращают 502 PROVIDER_ERROR.
Это тот же критерий идемпотентности, который уже применяли запуск агента и внутренний путь пробуждения сервера.
BC-0723-14: форма deployment.standalone.requiredFields.create в /v1/me стала объектом + документирует slug поля name
Поддержка старого формата до: 22.01.2027
Было
В ответе GET /v1/me per-kind под-блок deployment.standalone.requiredFields.create был массивом ["provider", "name", "plan", "region"] — формат name не указывался; у соседнего deployment.galaxyApp.requiredFields.create поле name говорило лишь «required». Имя с кириллицей или заглавными буквами при POST /v1/infra/servers отклонялось с 400 INVALID_REQUEST, но self-discovery об этом ограничении молчал.
Стало
deployment.standalone.requiredFields.create теперь объект (как соседний deployment.galaxyApp.requiredFields.create), и в обоих под-блоках name несёт формат: slug из строчных латинских букв по маске ^[a-z][a-z0-9-]*$, длина 2–63 символа. Человекочитаемую подпись кладите в необязательное поле displayName.
Что делать интеграторам
Плоский deployment.requiredFields["POST /v1/infra/servers"] (массив ["provider","name","plan","region"]) НЕ изменился — если вы читаете его, делать ничего не нужно, набор обязательных полей тот же. Если же ваш код парсил per-kind под-блок deployment.standalone.requiredFields.create как массив (.forEach / .includes("name") / .length / [0]), перейдите на чтение объекта: ключи — имена полей (provider/name/plan/region), значения — их описания.
NEW-0723-15: GET /v1/contacts/fields получил label и description для всех полей
В ответе GET /v1/contacts/fields теперь у всех 28 статических полей контакта есть человекочитаемые label и description. Раньше базовые поля (name, lastName, typeId и другие) приходили только с type и readonly, без описания смысла. Метки приходят по-русски. Семантику поля можно получить программно из ответа, без сверки со статической документацией. Кроме того, GET /v1/openapi.json публикует эти метки и описания (по-английски) как title и description в схемах Contact и ContactInput.
NEW-0723-16: smart-processes: поля relations и linkedUserFields во входной схеме
Поля relations (связи с сущностями CRM — parent/child, например привязка смарт-процесса к сделкам) и linkedUserFields теперь объявлены во входной схеме и видны в GET /v1/smart-processes/fields. Их можно передавать в POST /v1/smart-processes и PATCH /v1/smart-processes/:entityTypeId, чтобы связать смарт-процесс с другими сущностями CRM и вывести его в пользовательских полях. Фильтрация и сортировка по этим полям не поддерживаются — это вложенные структуры записи, а не поля выборки.
FIX-0723-17: bizproc-activities и bizproc-robots: тип documentType в /fields исправлен на array
Было
GET /v1/bizproc-activities/fields и GET /v1/bizproc-robots/fields показывали для documentType тип object, тогда как поле — массив из трёх элементов ([moduleId, entity, documentType]), как уже было объявлено у bizproc-templates.
Стало
Тип documentType в /fields теперь array у всех трёх сущностей — согласованно с реальным контрактом.
FIX-0723-18: PATCH /v1/bizproc-templates возвращает id числом
Было
PATCH /v1/bizproc-templates/:id возвращал data.id строкой ("1215"), тогда как POST возвращает число (1215). Клиент, сравнивавший id из ответа создания с ответом обновления, получал ложное несовпадение.
Стало
Ответ PATCH возвращает data.id числом (1215) — так же, как POST.
FIX-0723-19: userfields: тип label в схеме создания исправлен на string
Было
OpenAPI-схема POST /v1/userfields/{entity} объявляла label как object. B24 crm.<entity>.userfield.add принимает LABEL только строкой, поэтому SDK, сгенерированный по спеке (где label — объект), отправлял неверный тип и получал ошибку. OpenAPI-спека — публичный контракт: клиенты генерируют по ней SDK, и у тех, у кого тип был «объект», клиент был сломан.
Стало
label в схеме создания объявлен как string (подпись на языке портала по умолчанию). Мультиязычные подписи задаются через editFormLabel / listColumnLabel / listFilterLabel (PATCH после создания). Рантайм не менялся — правка только генерируемой спеки.
FIX-0723-20: sleep-now для galaxy-приложения теперь отвечает 400 — управляйте им со страницы «Галактики»
Было
POST /v1/infra/servers/:id/sleep-now для приложения, размещённого в галактике (GALAXY_APP), усыплял контейнер и отвечал 200. Это расходилось с сессионным маршрутом, который такое приложение уже отклонял, и могло рассинхронизировать состояние контейнера с хостом.
Стало
Тот же вызов для galaxy-приложения отвечает 400 с error.code = "GALAXY_APP_USE_GALAXY_ROUTE" и не меняет состояние: контейнер остаётся RUNNING. Управляйте жизненным циклом приложения через маршруты галактики. Для обычных (standalone) серверов поведение sleep-now не изменилось.
FIX-0723-21: комментарий задачи больше не отдаётся под чужой задачей
Было
На старых порталах GET /v1/tasks/:taskId/comments/:id возвращал 200 и сам комментарий, даже когда комментарий не принадлежал задаче :taskId: один и тот же комментарий отдавался под любой задачей, а поле taskId в ответе было простым эхом пути.
Стало
Перед выдачей комментарий сверяется с задачей из пути. Если комментарий не принадлежит :taskId, эндпоинт отвечает 404 с кодом NOT_FOUND и сообщением «Comment not found». Поле taskId в ответе теперь совпадает с реальной родительской задачей. Запросы комментария по его настоящей задаче работают как прежде.
FIX-0723-22: тип компании — единое поле typeId, не companyType
Было
Имя поля «тип компании» различалось на слоях. POST /v1/companies с полем companyType молча игнорировал тип — компания создавалась с типом по умолчанию; сохранить тип можно было только полем typeId. Чтение (GET, поиск) всегда возвращало тип в поле typeId. Фильтр же принимал companyType, но не typeId.
Стало
Тип компании — единое поле typeId во всех операциях: создание и изменение, чтение и поиск, фильтр (filter[typeId]) и группировка (groupBy: typeId). Значения прежние — CUSTOMER, SUPPLIER, COMPETITOR (список: GET /v1/statuses?filter[entityId]=COMPANY_TYPE). Поле теперь описано в GET /v1/companies/fields.
Влияние на интеграторов
Указывайте тип полем typeId. Чтение не меняется — тип всегда приходил в typeId. На создании и изменении companyType больше не описан (он и раньше не сохранял значение). В фильтре и группировке теперь работает typeId, а companyType возвращает 400 (UNKNOWN_FILTER_FIELD в фильтре, INVALID_AGGREGATION_FIELD в группировке) — замените имя на typeId.
2026-07-22
BC-0722-1: иконка сервера отдаётся как PNG
Поддержка старого формата до: 21.07.2026
Иконка сервера теперь отдаётся как PNG 256×256 (Content-Type: image/png) — платформа рендерит её из вашего загруженного SVG. Загрузка (POST /v1/infra/servers/:id/icon) по-прежнему принимает только SVG и теперь может вернуть 400 ICON_RASTERIZE_FAILED, если файл не удаётся растеризовать в PNG.
NEW-0722-2: пробуждение по расписанию (wake-schedules) доступно на всех порталах
CRUD для окон пробуждения — GET|POST /v1/infra/servers/:id/wake-schedules, PATCH|DELETE /v1/infra/servers/:id/wake-schedules/:scheduleId — теперь доступен на всех порталах для отдельных серверов (kind: "STANDALONE"): запрос больше не отвечает 403 WAKE_SCHEDULE_DISABLED. Платформа поднимает спящий сервер к заданному моменту по cron-выражению, дальше запуск задачи делает собственный cron внутри уже поднятой машины. Galaxy-приложения (kind: "GALAXY_APP") пока не участвуют в раскрытии и по-прежнему отвечают 403 WAKE_SCHEDULE_GALAXY_DISABLED. Ответ GET /v1/infra/servers/:id и список серверов теперь содержат аддитивные поля nextScheduledWakeAt (время ближайшего пробуждения, ISO 8601 или null) и wakeScheduleCapable — прежние поля не меняются.
NEW-0722-3: 409 SERVER_NOT_READY при деплое несёт признак повторяемости
POST /v1/infra/servers/:id/deploy в ответе 409 SERVER_NOT_READY, когда на сервере уже идёт восстановление подключения (запущенное параллельным деплоем или ремонтом), теперь дополнительно возвращает поля error.retryable: true и error.retryAfter (секунды) и заголовок Retry-After. Это машинный сигнал: повтор запроса имеет смысл — подождите указанный интервал и повторите деплой. Прежние клиенты не затронуты: код и текст ошибки прежние, поля добавлены аддитивно.
NEW-0722-4: Новый эндпоинт активации триала Маркета для портала ключа
POST /v1/portals/:id/activate-market-trial активирует одноразовый триал Битрикс24 Маркета для портала, которому принадлежит вызывающий ключ. Раньше активация была доступна только из кабинета — у ключей API программного пути не было.
:id обязан совпадать с порталом ключа, иначе 403 PORTAL_MISMATCH. Тело не требуется. Лимит — 3 запроса в час на портал.
Ответ при успехе: { "success": true, "data": { "status": "activated", "trialEndsAt": "..." } } (или "status": "already_active", если триал/доступ уже есть). Ошибки: 403 WRITE_BLOCKED_READONLY_KEY (ключ только для чтения), 403 PURPOSE_KEY_FORBIDDEN (служебный ключ специального назначения не может активировать триал), 404 NOT_FOUND (портал не найден), 409 ALREADY_ACTIVATED (триал уже активирован), 409 TRIAL_ACTIVATION_UNAVAILABLE (триал недоступен для этого портала), 503 TRIAL_ACTIVATION_RETRY (временная ошибка, повторите позже).
FIX-0722-5: портал с активной подпиской Маркетплейса больше не получает ложный отказ `MARKETPLACE_REQUIRED`
Было
На портале с несколькими держателями ключа разработчика состояние подписки Маркетплейса могло не прочитаться: если первый опрошенный ключ отвечал данными о портале, но без блока подписки (урезанный набор прав), опрос на этом останавливался и до ключа, способного прочитать подписку, дело не доходило. Подписка оставалась неизвестной, и портал с реально оплаченной подпиской получал 402 MARKETPLACE_REQUIRED на POST /v1/infra/servers, а GET /v1/me отдавал capabilities.servers.create.available: false. Повторный вызов GET /v1/me?refresh=tariff отказ не снимал.
Стало
Опрос продолжается до ключа, который вернёт блок подписки. Порталу с оплаченной подпиской создание сервера разрешается, capabilities.servers.create.available становится true. Формат ответов не изменился, действий со стороны клиента не требуется. Состояние обновляется при очередном обновлении тарифа портала (не позже часа) либо сразу по GET /v1/me?refresh=tariff.
Влияние на интеграторов
Действий не требуется. Клиент, который ветвился на 402 при создании сервера, продолжает работать: на затронутых порталах этот ответ просто перестаёт приходить.
FIX-0722-6: поле `region` в ответах об инфраструктуре всегда возвращает идентификатор региона
Было
У сервера, созданного и запущенного на международном сегменте, GET /v1/infra/servers и GET /v1/infra/servers/:id могли вернуть в поле region внутренний идентификатор зоны размещения, а не идентификатор региона из каталога. Значение не совпадало ни с одним id из GET /v1/infra/providers/:providerId/regions, поэтому сопоставить сервер с регионом каталога по этому полю было нельзя, и оно раскрывало детали внутреннего размещения.
Стало
region всегда содержит нейтральный идентификатор региона из того же пространства имён, что и каталог, — например bc-eu-central. Значение совпадает с id соответствующей записи GET /v1/infra/providers/:providerId/regions, поэтому сервер сопоставляется с регионом каталога напрямую. Зона размещения — внутренняя деталь: при создании сервера указывается регион, конкретную зону внутри него платформа выбирает сама.
Что делать интеграторам
Ничего, если вы передаёте в region значение, взятое из каталога регионов, — этот сценарий не менялся. Если ваш код сравнивал region из ответа о сервере со строкой, полученной ранее из ответа о сервере, а не из каталога, — сравнение теперь надёжно: оба конца в одном пространстве имён. На вход по-прежнему принимаются и идентификаторы из прежнего каталога.
2026-07-21
FIX-0721-1: имя и описание встроенного поискового движка, имя облачного провайдера
Публичное имя платформенного поискового движка приведено к продуктовому: GET /v1/search/providers
и /v1/me возвращают Bitrix24 AI Search в поле name (латинские локали). Идентификатор
провайдера bitrix-search не менялся — клиентам ничего делать не нужно.
Из описания того же провайдера снято упоминание цитирования источников: возможность зависит от
движка, привязанного к инстансу, и объявляется машинно в capabilities.output.citations того же
ответа.
GET /v1/infra/providers на международном сегменте возвращает в поле name бренд Bitrix24 Cloud
вместо Bitrix Cloud. Идентификатор провайдера bitrix-cloud не менялся.
Было
"name": "Bitrix AI Search" · "description": "Platform AI search with agentic mode and source citations" · "name": "Bitrix Cloud"
Стало
"name": "Bitrix24 AI Search" · "description": "Platform AI search with agentic mode" · "name": "Bitrix24 Cloud"
FIX-0721-2: создание шаблона бизнес-процесса теперь принимает файл шаблона
Было
POST /v1/bizproc-templates отвечал 422 Incorrect field TEMPLATE_DATA! при любом теле — создать шаблон было невозможно: поле с содержимым файла .bpt не входило в схему сущности и до Битрикс24 не доходило.
Стало
Поле templateData (файл .bpt в виде массива [имя файла, содержимое в base64]) принимается и передаётся в Битрикс24, шаблон создаётся. Поле обязательно на создание: без него запрос отклоняется с 400 MISSING_REQUIRED_FIELDS до обращения к Битрикс24 (раньше приходила сырая ошибка Incorrect field TEMPLATE_DATA!). Оно доступно на запись и в описании полей GET /v1/bizproc-templates/fields, но не возвращается при чтении.
NEW-0721-3: Сводный реестр исходников — GET /v1/me/sources
Новый эндпоинт GET /v1/me/sources — программный аналог кабинетной страницы «Исходники приложений». Возвращает снапшоты исходников по всем серверам и приложениям, которыми владеет ключ (а ключ администратора аккаунта — по всему аккаунту), с пагинацией (page/limit/search) и стандартным конвертом { success, data, total, page, limit }. Каждая строка несёт указатель для перехода вглубь — listEndpoint и latestDownloadEndpoint — плюс reachableViaApi и, для строк-серверов, blackholeStatus. В отличие от GET /v1/infra/servers, который ограничен серверами вызывающего ключа, этот реестр охватывает и сервер на другом ключе того же владельца.
Ответы server-scoped эндпоинтов исходников (POST /v1/infra/servers/:id/sources и соседние list/download/tag/cleanup) теперь описаны в документации; поле versions[].serverContext ({ serverId, serverName, serverDisplayName, linkedApp }) закреплено в контракте.
NEW-0721-4: необязательное поле error.b24Code в ответах 422 BITRIX_ERROR
В ответах 422 BITRIX_ERROR появилось необязательное поле error.b24Code — сырой код ошибки Битрикс24 для программной обработки (например, PERIOD_REQUIRED, INVALID_FILTER). Изменение аддитивно: прежние клиенты, разбирающие только error.code и error.message, не затронуты.
NEW-0721-5: Статистика Открытых линий — 6 методов дашборда
Новый раздел API для дашбордов контакт-центра: POST /v1/openlines/stats (агрегаты за период), GET /v1/openlines/operators (real-time нагрузка операторов), POST /v1/openlines/sessions/search, POST /v1/openlines/sessions/stats, POST /v1/openlines/sessions/transfers, POST /v1/openlines/ratings/search. Требуется скоуп imopenlines и право тарифа report_open_lines (иначе 403 B24_TARIFF_RESTRICTION).
В процессе раскатки — методы выходят в обновлении Битрикс24 imopenlines 26.700.0 и доступны не на всех порталах. Пока обновление не приехало на портал, методы возвращают 422 METHOD_NOT_YET_AVAILABLE с целевой версией в ответе — это признак раскатки, а не ошибка интеграции.
FIX-0721-6: тарифный отказ Битрикс24 отдаётся как 403 B24_TARIFF_RESTRICTION на всех эндпоинтах
Было
Отказ Битрикс24 по тарифному праву приходил как 422 BITRIX_ERROR с непрозрачным сообщением — отличить его от прочих ошибок Битрикс24 программно было нельзя.
Стало
Такой отказ отдаётся как 403 с кодом B24_TARIFF_RESTRICTION. Правило общее для всего V1 API, а не только для Открытых линий: любой эндпоинт, вызвавший метод Битрикс24, недоступный на тарифе портала, теперь отвечает этим кодом.
Влияние на интеграторов
Клиенты с обычной обработкой ошибок продолжают работать без изменений — отказ остаётся ошибкой, просто становится точнее. Если вы отдельно ветвились на 422 для тарифных отказов, перенесите ветку на 403 и error.code === 'B24_TARIFF_RESTRICTION'. Этот код не означает сбой интеграции: возможность не входит в тариф портала Битрикс24, и повторять запрос бессмысленно до смены тарифа.
FIX-0721-7: Версионные рантаймы деплоя ставят заявленную версию на Ubuntu 24.04
Было
Рантайм node20 устанавливал Node.js 18 (в репозиториях Ubuntu 24.04 нет Node 20), а python311 и RAG-рантаймы (node20-rag, python311-rag) падали на шаге установки — нужных пакетов в дистрибутиве нет. В ответе GET /v1/infra/runtimes поле packages показывало postgresql-14, хотя ставилась PostgreSQL 16.
Стало
node20 ставит Node.js 20 (с проверкой мажорной версии), python311 — Python 3.11, RAG-рантаймы — PostgreSQL 16 с расширением pgvector в базе приложения. Поле packages отражает фактическую версию (postgresql-16). Публичные идентификаторы рантаймов и формат запроса /deploy не изменились.
FIX-0721-8: статус ремонта сервера корректен при опросе
Было
Опрос GET /v1/infra/servers/:id/repair-status при многоузловом бэкенде мог кратковременно вернуть idle, даже когда ремонт ещё шёл, — если запрос попадал на другой обслуживающий узел, чем тот, что выполняет ремонт. Клиент, опрашивающий статус в цикле, мог из-за этого ошибочно решить, что ремонт завершился, ещё до его старта.
Стало
Эндпоинт надёжно возвращает реальный прогресс ремонта (running / done / failed) независимо от того, на какой узел попал опрос.
Влияние на интеграторов
Форма ответа не изменилась, действий со стороны клиента не требуется.
FIX-0721-9: приложение на standalone-сервере больше не работает с правами администратора
Было
Приложение, задеплоенное на standalone Black Hole сервер, запускалось с правами администратора и без изоляции. Любая уязвимость в самом приложении (например, выполнение произвольного кода) сразу давала полный контроль над всей виртуальной машиной: доступ к ключам подключения сервера, к служебным настройкам и к системным файлам.
Стало
Приложение работает под выделенной непривилегированной учётной записью и видит только свой каталог (extractTo, по умолчанию /opt/app), которым владеет. Системные каталоги защищены от записи, повышение привилегий запрещено. Порт ниже 1024 по-прежнему работает — платформа выдаёт для него отдельное разрешение.
Команды деплоя (install, preStart) и /exec по-прежнему выполняются с правами администратора — здесь ничего не изменилось, sudo не нужен.
Ничего менять не требуется: если приложению для запуска действительно нужны права администратора, деплой автоматически возвращает прежний режим, завершается успешно и добавляет предупреждение с причиной. Чтобы сразу пропустить эту попытку (актуально для nginx в качестве команды запуска, MySQL через системный сокет и запуска через Docker), передайте "hardening": "off" в теле деплоя.
В data.steps[] появились два новых значения step: service_user — передача каталога деплоя непривилегированной учётной записи, и hardening — предупреждение о возврате приложения к прежнему режиму. Клиентам, которые разбирают шаги по имени, стоит их учесть.
BC-0721-10: битый кандидат в image_url больше не роняет весь запрос
Поддержка старого формата до: 21.01.2027
Было
Массив content мог нести несколько частей image_url. Если хотя бы одна из них
содержала не изображение — например HTML-страницу с ошибкой, закодированную в Base64
и объявленную как image/png, — платформа передавала её модели как есть. Модель не
могла её декодировать, и весь запрос завершался ошибкой 502 с кодом
ai_provider_unavailable, даже когда остальные изображения были корректны.
Отдельно: части с неподдерживаемым MIME-типом, повреждённым Base64 или превышением
лимита 20 МиБ отклонялись ответом 400 invalid_image_payload — тоже на весь запрос
целиком.
Стало
Перед отправкой модели платформа проверяет фактическое содержимое каждой части
image_url по сигнатуре байтов, а не по заявленному MIME-типу. Часть, содержимое
которой является веб-ответом (HTML, XML, JSON, ответ HTTP) либо не декодируется,
заменяется на своей позиции текстовой заглушкой [image unavailable: <причина>].
Остальные изображения обрабатываются как обычно, запрос завершается успешно.
Позиции частей сохраняются: длина массива content не меняется, поэтому нумерация
кандидатов на стороне клиента остаётся верной.
Отклонённые части видны в ответе — в поле warnings появляется запись с кодом
IMAGE_CONTENT_REJECTED, а в заголовках X-Image-Parts-Rejected с их количеством.
Для потоковых ответов заголовок приходит вместе с началом потока.
Ответом 400 теперь завершаются только структурные ошибки: отсутствующее поле
url, строка, не являющаяся ни URL, ни data-URI, неподдерживаемая схема и http://
в production.
Что делать интеграторам
Если ваш код полагался на 400 invalid_image_payload как на признак того, что
изображение не принято, — читайте вместо этого warnings или заголовок
X-Image-Parts-Rejected. Запрос теперь завершается успешно, и молчаливой потери
изображения не происходит: факт замены всегда отражён в ответе.
Дополнительно изменилось поведение при отказе модели. Раньше любой не-2xx ответ
провайдера приходил как 502, теперь статус отражает причину:
- ответ модели
400или422→400с кодомai_provider_rejected. Повторять такой запрос без изменений бесполезно; - превышение лимита на стороне модели (
429) →429с заголовкомRetry-After. Повторить нужно, выдержав указанную задержку. В потоковом ответе заголовок невозможен, поэтому задержка приходит полемretryAfterв кадре ошибки; - таймаут на стороне модели (
408) →503с кодомai_provider_timeout.
Ответы 401, 403 и 5xx по-прежнему приходят как 502. Изменение затрагивает
POST /v1/chat/completions и
POST /v1/embeddings.
Ошибки со стороны модели теперь дополнительно несут поле providerStatusCode —
исходный HTTP-статус ответа модели. По нему 429 от модели отличается от 429
собственного лимита платформы (у последнего поля нет): код rate_limit_exceeded
у обоих одинаковый, чтобы SDK ретраили единообразно, а различитель — это новое
необязательное поле.
Также в data-URI теперь допускаются параметры между типом и ;base64 —
data:image/jpeg;name=photo.jpg;base64,... больше не отклоняется.
2026-07-20
NEW-0720-1: Ответы приложений теперь возвращают статус публикации
Ответы раздела приложений — список, данные приложения, создание, публикация и снятие с публикации — теперь несут два новых поля: catalogStatus (PRIVATE / PUBLISHED / UNPUBLISHED) и publishedAt (дата публикации, ISO 8601, или null). Раньше прочитать статус публикации через V1 было нельзя — приходилось угадывать по массиву placements, что ненадёжно: снятое с публикации приложение может сохранить ранее привязанные коды, а PRIVATE и UNPUBLISHED по placements неразличимы. Поля добавлены аддитивно — прежние вызовы работают без изменений.
FIX-0720-2: переименование чата больше не отвечает ложным успехом тому, кто не участник
Было
PATCH /v1/chats/:chatId, вызванный от имени администратора портала, который не состоит в чате, возвращал { "success": true, "data": true }, хотя название чата не менялось: Битрикс24 отвечал на такой вызов ложным успехом. Отличить его от настоящего переименования по ответу было нельзя, и интегратор считал операцию выполненной. Вызывающий при этом не мог даже прочитать этот чат.
Стало
Перед переименованием проверяется, что вызывающий состоит в чате. Если нет — ответ 404 CHAT_NOT_FOUND_OR_NO_ACCESS, тот же, что и для любого другого не-участника, и попытки переименования не происходит. Ложный успех больше не выдаётся. Переименование участником, у которого есть права, работает без изменений.
NEW-0720-3: стриминг chat-completions обрывает зависший ответ апстрима явной ошибкой
Если при стриминге (POST /v1/chat/completions с stream: true) апстрим-модель отдала заголовки, но затем замолчала в середине ответа и не присылает новых данных дольше окна ожидания, прокси теперь прерывает вызов и присылает в поток терминальный кадр ошибки перед data: [DONE]:
data: {"error":{"code":"stream_idle_timeout","type":"server_error","retryable":true,"retryAfter":<секунды>}}
Раньше такой вызов висел бесконечно (агент оставался в состоянии «receiving stream response»). Ошибка повторяемая — прочитайте поток до конца ([DONE]) и повторите запрос с учётом retryAfter. Обычные (не зависшие) стримы и «думающие» модели, которые непрерывно присылают токены рассуждений, не затронуты.
NEW-0720-4: tasks: новое поле timeSpentInLogs
Поле timeSpentInLogs (фактически затраченное время в секундах, сумма записей учёта времени) теперь задекларировано в схеме задач — доступно в select, фильтре и сортировке GET /v1/tasks и POST /v1/tasks/search, и присутствует в GET /v1/tasks/fields.
Раньше поле возвращалось, только если в select были указаны ОБА написания сразу (timeSpentInLogs и TIME_SPENT_IN_LOGS); теперь достаточно любого одного. Поле только для чтения — фиксируется через эндпоинт учёта времени, не через обновление задачи.
FIX-0720-5: GET /v1/files/:id?include=folder теперь возвращает папку
Было
GET /v1/files/:id с ?include=folder отвечал 200, но без блока _included, хотя GET /v1/files/fields объявлял folder как includable — включение молча не срабатывало.
Стало
?include=folder прикрепляет папку в _included.folder, как и обещает /fields.
Влияние на интеграторов
Действий не требуется. Клиенты, читавшие _included.folder, теперь получают объект вместо его отсутствия.
FIX-0720-6: /v1/me: блок infra стал точным по лимиту серверов, идентификатору провайдера и разбивке
Было
GET /v1/me в блоке infra возвращал limits.max: 3 независимо от реально применяемого лимита серверов на ключ; providers мог отдавать внутренний идентификатор провайдера (расходясь с GET /v1/infra/providers) и с возможными дублями; limits.breakdown относил виртуальные машины управляемых ботов к direct вместо bots.
Стало
limits.max отражает реально применяемый лимит серверов на ключ; providers отдаёт публичный идентификатор провайдера, согласованный с GET /v1/infra/providers, без дублей; limits.breakdown учитывает машины ботов в bots. Интеграцию менять не нужно — значения просто стали корректными.
FIX-0720-7: deal-categories: неизвестные поля фильтра отклоняются, сортировка по id учитывает направление
Было
GET /v1/deal-categories с фильтром по неизвестному полю молча возвращал ВСЮ таблицу воронок с 200 — Bitrix24 игнорирует неизвестные ключи фильтра legacy-метода и отдаёт весь список. А сортировка ?sort=id&order=desc игнорировала направление и всегда возвращала один и тот же порядок.
Стало
Фильтр по неизвестному или неподдерживаемому полю (а также операторные префиксы >/>=/!/… и операторные объекты) отклоняется до вызова Bitrix24 с 400 UNSUPPORTED_FILTER; в сообщении перечислены фильтруемые поля (id, name, sort). Фильтр точным совпадением и $in по этим полям работают как прежде. Сортировка ?sort=id теперь корректно учитывает asc/desc.
FIX-0720-8: openline-configs: нераспознанные поля в теле записи больше не пропадают молча
Было
POST /v1/openline-configs (и PATCH) молча игнорировал нераспознанные поля тела — Bitrix24 отбрасывает неизвестные ключи метода imopenlines.config.*. Тело из одних только неизвестных полей при этом создавало конфигурацию со значениями по умолчанию и отвечало 200.
Стало
Если в теле НЕТ ни одного известного поля — запрос отклоняется с 400 VALIDATION_ERROR до вызова Bitrix24, и в сообщении перечислены нераспознанные поля. Если известное поле есть, но часть полей нераспознана — запись выполняется как прежде, а в ответе возвращается meta.warnings с перечнем проигнорированных полей (раньше они исчезали без следа). Поля только для чтения (id, queue, dateCreate и другие) в теле записи теперь отклоняются с 400 READONLY_FIELD — раньше они проходили как «известные» и могли привести к созданию конфигурации со значениями по умолчанию.
FIX-0720-9: GET /v1/{entity}/fields сигналит о неполных метаданных при сбое Bitrix24
Было
Если запрос динамических полей к Bitrix24 (*.fields) падал (rate-limit, QUERY_LIMIT_EXCEEDED, таймаут очереди), эндпоинт молча отдавал 200 только со статическими полями схемы — у многих из них нет человекочитаемой метки (label). Ответ выглядел полным, клиент не мог отличить его от корректного и строил недетерминированные маппинги полей.
Стало
При сбое запроса полей ответ по-прежнему 200 со статическими полями, но несёт meta.warnings: [{ "code": "fields_partial", "message": "..." }] — клиент видит, что набор полей неполный, и может повторить запрос. Формат предупреждения — объект { code, message }, тот же канал и та же форма, что у meta.warnings в list/search, поэтому один разбор по warning.code работает на всех эндпоинтах. Сбой теперь также логируется на стороне Vibe.
2026-07-19
BC-0719-1: Cowork/Code: тарифная линейка переименована — Free / Pro / Max / Ultra, цена Ultra снижена
Поддержка старого формата до: 18.07.2026
Было
Поле tier в ответах GET /v1/cowork/state и GET /v1/cowork/me (а также recommendation.upgrade.nextTier и каталог tiers[]) принимало значения FREE, START, PRO, MAX. Тариф MAX (×20) стоил 40 000 Ꝟ/мес.
Стало
Линейка переименована со сдвигом: START → PRO, PRO → MAX, MAX → ULTRA; набор значений теперь FREE, PRO, MAX, ULTRA. Множители тарифов не изменились: PRO ×1 (база), MAX ×5, ULTRA ×20; у FREE — 5% от PRO. Цена ULTRA (бывший MAX, ×20 объёма) снижена с 40 000 до 20 000 Ꝟ/мес. Объёмы окон откалиброваны по фактическому использованию: 5-часовое окно выросло вдвое на всех тарифах, месячные объёмы уменьшены; в течение текущего оплаченного периода лимиты подписки не меняются — новые значения применяются со следующего продления. Переименование применяется атомарно в момент релиза: значение START больше не возвращается, добавилось значение ULTRA. Клиенты, ветвящиеся по строковым значениям tier / nextTier, должны обновить маппинг с учётом сдвига смысла (PRO теперь база, MAX — средний тариф); клиенты, отображающие серверные multiplier / feeVibes как есть, продолжают работать без изменений.
FIX-0719-2: цены тарифов Cowork/Code на международной версии приведены к долларовой шкале
Было
На международной версии платформы каталог тарифов в GET /v1/cowork/state отдавал цены в масштабе российской версии: feeVibes 2000 / 10000 / 20000 за Pro / Max / Ultra. При курсе 1 Vibe credit = 1 доллар это читалось как 2000–20000 долларов в месяц.
Стало
Каталог тарифов на международной версии отдаёт долларовую сетку: Pro — feeVibes: 20, Max — 100, Ultra — 200 в месяц; квоты трёх окон масштабированы согласованно, поэтому ёмкость тарифа в запросах не изменилась. Российская версия не затронута.
Влияние на интеграторов
Если ваш клиент читает tiers[].feeVibes из GET /v1/cowork/state на международной версии — отображаемые значения уменьшились в 100 раз и теперь совпадают с реально списываемой ценой активации. Ничего менять в коде не нужно.
2026-07-18
FIX-0718-1: деплой переиспользует сервер приложения вместо создания дубликата
Было
POST /v1/infra/servers c source всегда создавал новый сервер, даже если у приложения, которому принадлежит ключ, уже был сервер — на счёт заводился второй, простаивающий. А POST /v1/infra/servers/:id/deploy отвечал WRONG_KEY, если ключ вызова отличался от того, которым сервер был создан (например, у приложения есть личный ключ и ключ авторизации).
Стало
Если ключ вызова принадлежит приложению, у которого уже есть живой сервер, POST /v1/infra/servers возвращает этот сервер с полем reused: true вместо создания нового. Если в том же запросе передан source, а переиспользуемый сервер — это приложение галактики (kind: "GALAXY_APP"), у которого ещё нет работающего контейнера (ни разу не деплоилось или прошлый деплой упал), исходный код сразу разворачивается в его собственный сервер (ответ содержит reused: true и deploying: true, а статус сервера на время сборки — provisioning) — так же, как при обычном create с source: опрашивайте GET /v1/infra/servers/:id до статуса running, второй вызов деплоя не нужен. Если же приложение галактики уже работает, ответ содержит reused: true и next: "deploy" — разверните исходный код отдельным вызовом POST /v1/infra/servers/:id/deploy (так живой контейнер не затрагивается на время сборки). Для обычного сервера или запроса без source ответ содержит reused: true и next: "deploy" — разверните исходный код отдельным вызовом POST /v1/infra/servers/:id/deploy. POST /v1/infra/servers/:id/deploy теперь принимает любой ключ этого же приложения и деплоит в его сервер.
Влияние на интеграторов
Ничего менять не нужно. Дубликаты серверов больше не создаются. Раньше one-shot create с source на уже существующий сервер приложения возвращал next: "deploy" и терял переданный source — теперь исходный код разворачивается сразу. И деплой, и чтение статуса (GET /v1/infra/servers/:id) сервера приложения работают под любым ключом этого приложения — независимо от того, каким из них вы вызываете API.
FIX-0718-2: DELETE /lock снимает зависший лок и на удалённом сервере
Было
DELETE /v1/infra/servers/:id/lock возвращал 404 NOT_FOUND, если сервер был удалён — даже когда лок операции остался в памяти платформы и продолжал держать сервер. Из-за этого сценарий «предыдущий сервер удалён, лок завис, следующий деплой падает с EXEC_BUSY» не имел выхода: снять такой лок через API было нельзя.
Стало
DELETE /lock снимает зависший лок и на удалённом сервере — при условии, что он всё ещё принадлежит вашему API-ключу (владение остаётся единственной проверкой; лок не хранит данных и не держит облачных ресурсов). Успешный вызов возвращает 200 с data.released: true. 404 NOT_FOUND теперь означает только «сервер не существует или принадлежит другому ключу».
NEW-0718-3: коды ошибок коннектора при установке приложения теперь возможны и на облачных порталах (поэтапная раскатка)
POST /v1/apps на облачном портале теперь тоже может устанавливать приложение через модуль vibecodeconnector и, соответственно, возвращать те же коды ошибок коннектора, что раньше были возможны только на коробочных порталах: 403 CONNECTOR_APP_INSTALL_FORBIDDEN (администратор портала Битрикс24 запретил пользователю установку приложений), 409 CONNECTOR_MODULE_NOT_INSTALLED (модуль vibecodeconnector не установлен на портале) и 502 CONNECTOR_APP_INSTALL_FAILED (прочие сбои установки). Изменение аддитивное: ответ при успешной установке не изменился, а раскатка идёт поэтапно — на большинстве облачных порталов путь установки пока прежний. Клиентам, которые уже обрабатывают эти коды на коробочных порталах, менять ничего не нужно; клиентам, которые их не обрабатывали, стоит добавить обработку.
2026-07-17
FIX-0717-1: деплой на спящий galaxy-хост отвечает раньше клиентских таймаутов
Было
POST /v1/infra/servers/:id/deploy на спящем общем хосте удерживал соединение открытым до ~6,5 минут, пока хост просыпался. HTTP-клиенты с типовым таймаутом ожидания заголовков (~300 секунд — дефолт Node fetch) обрывали соединение раньше ответа платформы: деплой выглядел как сетевой сбой fetch failed без кода ошибки и рекомендаций. Неудачное пробуждение к тому же возвращало хост в сон, и каждый повтор начинал загрузку хоста заново.
Стало
Платформа будит хост в фоне и ждёт подключения не дольше ~4 минут. Хост успел подключиться — деплой выполняется одним вызовом, как раньше. Не успел — сразу возвращается повторяемый 502 GALAXY_HOST_UNREACHABLE с hint (повторить тот же запрос через 1–2 минуты, слот не удалять), а хост продолжает просыпаться в фоне — повторный деплой подхватывает уже идущую загрузку вместо новой. В deployment.galaxyApp._rules и deployment.standalone._rules (GET /v1/me) добавлена рекомендация держать таймаут HTTP-клиента не ниже 690 секунд — строго выше платформенного окна в 660 секунд.
Влияние на интеграторов
Изменений в запросах не требуется. Если деплой на спящий galaxy-хост раньше завершался у вас сетевой ошибкой без ответа платформы — теперь придёт либо успех, либо 502 с инструкцией повторить.
NEW-0717-2: поиск сотрудников на сервере подсказывает причину пустого списка
Эндпоинт GET /v1/infra/servers/:id/b24-users теперь возвращает дополнительное поле hint, когда список пуст из-за отсутствия доступа к Битрикс24 — приложение ещё не авторизовано на портале или ключ отозван. Прежние вызовы работают без изменений: поле аддитивное и отсутствует при успешной выдаче.
NEW-0717-3: справочники полей каталога сообщают о nullable-полях
Справочники GET /v1/catalog-prices/fields, GET /v1/catalog-sections/fields и GET /v1/catalog-products/fields теперь добавляют ключ "nullable": true полям, которые могут вернуть null: у цен это quantityFrom, quantityTo и extraId, у разделов — iblockSectionId, xmlId, code и description, у товаров — iblockSectionId, code, weight, purchasingPrice, purchasingCurrency, quantity и quantityReserved. Тот же признак приходит в data.entities[].fieldsDetailed ответа GET /v1/guide, который читается ключом OAuth-приложения без сессии, а в машинной спеке GET /v1/openapi.json такие поля описаны union-типом вида ["number", "null"]. Клиент, который строит типизированную модель по справочнику, теперь получает верную nullability и не падает на первом же null. Набор полей, их типы и значения в ответах не изменились.
NEW-0717-4: EXEC_BUSY подсказывает, через сколько повторить
Ответ 409 EXEC_BUSY (другая операция держит блокировку сервера) на POST /v1/infra/servers/:id/exec и POST /v1/infra/servers/:id/deploy теперь несёт retry-подсказку: HTTP-заголовок ответа Retry-After (в секундах) и два новых поля в теле ошибки — retryable: true и retryAfter (в секундах). Значение retryAfter — короткий интервал опроса (повторяйте с ним, пока не пройдёт), а не полное время до автоматического снятия блокировки; полный верхний предел по-прежнему в error.hint.autoExpiresInSeconds. Изменение аддитивное: код, поле message и hint не меняются, клиенты, читающие error.code, продолжают работать без правок.
FIX-0717-5: деплой galaxy-приложения со слишком большим архивом отдаёт 413, а не «хост недоступен»
Было
POST /v1/infra/servers/:id/deploy с source.content, превышающим лимит загрузки, возвращал 502 GALAXY_HOST_UNREACHABLE — с текстом про недоступность хоста и советом «повторить, когда хост переподключится». Хост при этом был полностью доступен, а повтор того же архива давал тот же результат: интегратор оказывался в бесконечном цикле бесполезных попыток.
Стало
Тот же случай возвращает 413 GALAXY_UPLOAD_TOO_LARGE со структурированным error.hint. Причина детерминирована (архив слишком большой), а не транзиентна, поэтому повтор без изменений не поможет. hint подсказывает уменьшить архив — исключить node_modules, .git и артефакты сборки (зависимости платформа ставит на хосте). У galaxy-приложения источник — только встроенный source.content (source.url отклоняется с 400 GALAXY_DEPLOY_CONTENT_ONLY), поэтому уменьшить архив — единственный способ восстановления. Отдельный смежный случай: тело запроса, превышающее жёсткий внешний лимит платформы (500 МБ на base64-тело ≈ ~375 МБ бинарного архива), теперь отклоняется на краю кодированным 413 PAYLOAD_TOO_LARGE (на Vibe-REST /v1/-маршрутах — deploy/upload/create; OpenAI-совместимые AI-роуты отдают ошибку в своём конверте) вместо сырого HTML — раньше клиент получал недекодируемый ответ.
Влияние на интеграторов
Ничего менять не нужно: успешные деплои не затронуты. Клиенты, которые ветвились на коде ошибки для этого сбоя, теперь видят честный 413 GALAXY_UPLOAD_TOO_LARGE вместо вводящего в заблуждение 502 GALAXY_HOST_UNREACHABLE — последний остаётся для настоящего обрыва туннеля во время сборки.
NEW-0717-6: displayName и description в деплое задают карточку в каталоге Битрикс24
POST /v1/infra/servers/:id/deploy принял два необязательных поля тела — displayName и description. POST /v1/infra/servers (создание сервера) принял необязательный description. Значения становятся именем и описанием карточки приложения в каталоге Битрикс24. Если деплой прошёл без displayName и description, ответ содержит warnings: string[] с подсказкой задать их; одношаговое создание galaxy-приложения с source (тело POST /v1/infra/servers с полем source) тоже возвращает warnings в ответе 201, когда поля не заданы. Обратная совместимость сохранена — запросы без новых полей продолжают работать как раньше.
FIX-0717-7: деплой галактик отдаёт 503 при временной перегрузке базы
Было
При кратковременном исчерпании пула соединений с базой во время деплоя приложения в галактику POST /v1/infra/servers/:id/deploy возвращал общий 502 GALAXY_APP_DEPLOY_FAILED — тот же код, что и настоящий провал сборки. Клиент не мог отличить временную перегрузку от терминальной ошибки и часто читал ответ как окончательный.
Стало
Временная перегрузка базы теперь отдаётся как 503 POOL_EXHAUSTED с заголовком Retry-After (число секунд для повторной попытки). Настоящий провал сборки по-прежнему 502 GALAXY_APP_DEPLOY_FAILED.
Влияние на интеграторов
Ничего менять не нужно. Если ваш клиент повторяет запросы, теперь на 503 он получает явный сигнал бэкоффа через Retry-After вместо непрозрачного 502.
FIX-0717-8: фильтр и сортировка списка комментариев задачи работают на обеих карточках
Было
Запрос GET /v1/tasks/:taskId/comments с параметром filter или с сортировкой не по ID на портале с новой карточкой задачи возвращал 200 и пустой список, даже когда комментарии в задаче были. Ошибки не приходило, поэтому отличить «под фильтр ничего не подошло» от «фильтр не сработал» было нельзя.
Стало
Такой запрос возвращает подошедшие комментарии. Фильтр и сортировка работают по полям ID, AUTHOR_ID и POST_DATE, перед именем поля в фильтре допустим префикс !, >, >=, < или <=. Фильтр по AUTHOR_NAME и сортировка по AUTHOR_NAME или AUTHOR_EMAIL на новой карточке отвечают 400 с кодом UNSUPPORTED_FILTER_FIELD или UNSUPPORTED_SORT_FIELD и указывают на AUTHOR_ID, на старой карточке эти поля по-прежнему принимаются. Добавлен параметр offset — он учитывается на новой карточке при запросе с filter или сортировкой не по ID, на остальных путях чтения игнорируется. Код INVALID_FILTER теперь приходит ещё и тогда, когда filter — скаляр или пустой массив вместо объекта (0, false, "", []), значение поля ID или AUTHOR_ID не число, значение POST_DATE не разбирается как дата, либо значение поля — объект или массив вместо скаляра. В meta добавлено поле truncated со значением true — просмотрено предельное окно истории, и часть комментариев осталась за его границей.
Влияние на интеграторов
Менять ничего не нужно: запрос, который раньше отдавал пустой список, начинает отдавать данные. Учтите три границы. Первая — фильтр по AUTHOR_NAME и сортировка по AUTHOR_NAME или AUTHOR_EMAIL на портале с новой карточкой вместо пустого 200 теперь отвечают 400, переведите такой запрос на AUTHOR_ID, идентификатор сотрудника по имени даёт GET /v1/users. Вторая — meta.total на запросе с фильтром к новой карточке считает подошедшие комментарии в пределах просмотренного окна, а не во всей истории задачи, и при meta.truncated: true это неполное число. Третья — значение POST_DATE на новой карточке сравнивается с createdAt в UTC, поэтому результат на границе суток может отличаться от выборки на старой карточке. На порталах со старой карточкой поведение не изменилось.
2026-07-16
BC-0716-1: json_object на реасонинг-модели восстанавливает JSON, тело 422 уточнено
Поддержка старого формата до: 15.01.2027
Было
Запрос POST /v1/chat/completions с response_format типа json_object на модели с рассуждением (например bitrix/bitrixgpt-5.5-agent) стабильно возвращал 422 structured_output_truncated, даже когда модель завершилась сама (finish_reason: "stop") и положила готовый валидный JSON в служебный канал reasoning_content — ответ терялся. В теле любой такой ошибки присутствовали поля error.suggestedMaxTokens и error.param, а текст утверждал «finish_reason=length» независимо от реальной причины остановки.
Стало
Если модель на json_object завершилась сама и валидный JSON лежит в reasoning_content, платформа восстанавливает его и возвращает 200 с этим JSON в content (в потоковом режиме — чанком content перед терминальным чанком с finish_reason). Тело 422 стало правдивым: error.suggestedMaxTokens и error.param присутствуют только при реальной обрезке (finish_reason: "length"); при завершении по любой другой причине (stop и т.д.) эти поля опущены, а текст называет фактический finish_reason. Поведение json_schema не изменилось — там строгая схема проверяется на стороне модели.
Что делать интеграторам
Ничего, если вы просто обрабатываете 422 по error.code. Если ваш код безусловно читает error.suggestedMaxTokens или error.param на ошибке structured_output_truncated — сделайте чтение опциональным: при finish_reason !== "length" этих полей теперь нет. Потоковым клиентам с response_format — собирать content по всем дельтам до data: [DONE].
FIX-0716-2: env, отправленный файлом в multipart-деплое, больше не игнорируется молча
Было
При multipart/form-data в POST /v1/infra/servers/:id/deploy поле env, отправленное как файл или Blob, молча игнорировалось — деплой завершался успехом, но приложение стартовало без переменных окружения.
Стало
Такой запрос возвращает 400 с кодом VALIDATION_ERROR и подсказкой отправлять env текстовым полем с JSON-строкой.
Влияние на интеграторов
Корректный способ (текстовое поле env со значением вида {"KEY":"value"}) не затронут. Кто отправлял env файлом или Blob — теперь получает явную ошибку вместо ложного успеха.
FIX-0716-3: деплой крупных архивов через source.content больше не падает с Gateway HTTP 413
Было
На части порталов POST /v1/infra/servers/:id/deploy с source.content (base64-архив) падал на шаге download с { "code": "DEPLOY_FAILED", "message": "Gateway HTTP 413", "step": "download" }, если base64-тело превышало ~1 МБ (примерно 768 КБ исходного tar.gz) — вопреки заявленному лимиту 500 МБ. Обходом была загрузка через source.url.
Стало
Лимит 500 МБ на inline-загрузку (source.content и multipart) действует на всех порталах. source.url продолжает работать как раньше.
BC-0716-4: POST /v1/infra/servers отклоняет неизвестные поля в теле
Поддержка старого формата до: 15.01.2027
Было
Неизвестное поле в теле запроса молча игнорировалось. Запрос с deployMode: "STANDALONE" (несуществующее поле) возвращал 201 и создавал galaxy-приложение вместо ожидаемого выделенного сервера — правильное поле называется placement: "dedicated".
Стало
POST /v1/infra/servers отклоняет тело с неизвестным полем ошибкой 400 UNKNOWN_PARAM. В details.unknownFields перечислены лишние поля, в details.suggestions — подсказка правильного имени (deployMode → placement), в details.validParams — полный список допустимых полей.
Что делать интеграторам
Убрать из тела поля, которых нет в списке параметров создания, либо исправить опечатку по подсказке details.suggestions. Модель размещения задаётся полем placement (auto по умолчанию, dedicated — отдельная виртуальная машина).
NEW-0716-5: /v1/sites/fields описывает допустимые значения поля type
GET /v1/sites/fields теперь возвращает у поля type перечень допустимых значений в type.enum с подписями: PAGE (лендинг), STORE (интернет-магазин), KNOWLEDGE (база знаний 2.0), а также VIBE (сайт из конструктора) и SMN (связка с модулем «Управление сайтом»). Значения VIBE и SMN встречаются только в ответах и доступны только для чтения — создать сайт такого типа через API нельзя.
NEW-0716-6: Избранное и закрепление задачи без прав на редактирование
Добавлены четыре ручки для «личных» действий над задачей, которые в Битрикс24 разрешены при доступе только на чтение (не на редактирование): POST /v1/tasks/:taskId/favorite добавляет задачу в избранное, DELETE /v1/tasks/:taskId/favorite убирает из избранного, POST /v1/tasks/:taskId/pin закрепляет задачу в списке задач текущего пользователя, DELETE /v1/tasks/:taskId/pin открепляет. Раньше единственным способом изменить задачу был PATCH /v1/tasks/:id, который требует прав на редактирование и возвращал «Нет доступа к редактированию задачи», из-за чего разрешённые пользователю действия были недоступны.
NEW-0716-7: предупреждение о вытесняемом тарифе в ответах пробуждения по расписанию
Ответы POST /v1/infra/servers/:id/wake-schedules и PATCH /v1/infra/servers/:id/wake-schedules/:scheduleId теперь дополнительно несут два поля верхнего уровня: preemptibleAdvisoryCode и preemptibleAdvisory. Если сервер работает на вытесняемом тарифе, preemptibleAdvisoryCode равен "PREEMPTIBLE_BEST_EFFORT", а preemptibleAdvisory — краткое английское пояснение того же факта: подъём такого сервера к моменту окна не гарантирован, окно может быть пропущено при нехватке свободной ёмкости. Для сервера на невытесняемом тарифе оба поля — null. Предупреждение не блокирует создание или обновление окна — это тот же неблокирующий паттерн, что уже используют поля tzWarning/tzWarningCode. Старые интеграции, не читающие новые поля, продолжают работать без изменений.
2026-07-14
NEW-0714-1: OpenAPI-спека: валидность 3.1, семантика полей и срез по скоупу
Было
Машинная спека GET /v1/openapi.json содержала 3.0-ключ nullable (невалиден в 3.1), не объявляла path-параметр {entityTypeId} на batch/aggregate/fields/products, не несла описаний/допустимых значений/примеров полей, описывала только ключи vibe_app_ и отдавалась одним монолитом.
Стало
Спека валидна по OpenAPI 3.1: nullable-поля используют union-тип ["<тип>","null"], все path-параметры объявлены. У свойств появились title/description, допустимые значения (x-enumValues + расшифровка в описании) и примеры. Секьюрити-схемы называют все три вида ключей (vibe_api_/vibe_app_/vibe_live_), у каждой операции есть x-required-scope, а в корне — каталог x-scopes. Добавлены корневые tags/externalDocs, блок webhooks для бот-событий и anyOf для мультиполей. GET /v1/openapi.json?scope=<scope> (например ?scope=crm) отдаёт срез спеки по одному скоупу, чтобы он помещался в контекст агента. Указатели на спеку добавлены в /v1/me и /v1/guide. Массовый перенос curl-примеров по каждой операции — отдельная последующая работа.
FIX-0714-2: Общая 5xx-ошибка на AI-эндпоинтах теперь в OpenAI-совместимом конверте
AI-эндпоинты (/v1/ai/*, /v1/models, /v1/chat/*, /v1/audio/*) документированы с OpenAI-совместимым форматом ошибок. При исчерпании пула соединений это уже соблюдалось, но общая (непредвиденная) 5xx-ошибка на этих эндпоинтах отдавала обычный V1-конверт.
Было
Общая 5xx-ошибка на AI-эндпоинте: {"success": false, "error": {"code": "...", "message": "..."}} — не тот формат, что SDK-клиенты OpenAI ожидают для этих путей.
Стало
Тот же случай теперь отдаёт {"error": {"message": "...", "type": "...", "code": "..."}} — единый конверт для всех ошибок AI-эндпоинтов, включая общие 5xx.
Влияние на интеграторов
Клиенты на OpenAI SDK, уже читающие error.type/error.code (штатный путь для этих эндпоинтов), не заметят изменения. Код, который на AI-эндпоинтах ожидал success/error.code в верхнем уровне именно на общих 5xx, должен переключиться на error.type/error.code.
FIX-0714-3: `versions` в списке версий исходников приложения ограничен 500 записями
GET /v1/apps/:id/sources возвращал весь список версий без ограничения — для приложений с очень длинной историей это была неограниченная выборка.
Было
data.versions — весь список версий без ограничения по размеру; data.totalVersions всегда равнялся data.versions.length.
Стало
data.versions содержит не более 500 последних версий (по savedAt, убывание). data.totalVersions и data.totalSizeBytes по-прежнему считаются по полной выборке — точность агрегатов не зависит от кэпа.
Влияние на интеграторов
Для приложений с историей до 500 версий поведение не меняется. Для приложений с историей больше 500 версий data.versions.length теперь может быть меньше data.totalVersions — код, полагавшийся на их равенство, должен ориентироваться на data.totalVersions/data.totalSizeBytes для агрегатов и не считать data.versions полным списком.
FIX-0714-4: Телефония: `userId`/`duration` теперь по-настоящему валидируются, а не только на truthy
POST /v1/calls/register, /v1/calls/:callId/show, /v1/calls/:callId/hide, /v1/calls/:callId/finish принимали userId (и duration у finish) без проверки типа/формы — любое truthy-значение (например, строка "abc" или объект) проходило в Битрикс24 и падало там уже непрозрачной ошибкой апстрима.
Было
{"userId": "abc"} (или любое другое truthy, не являющееся положительным целым) проходил валидацию и уходил в Битрикс24; ошибка Required: userId (number) появлялась только на полностью пустом/falsy значении.
Стало
userId принимается как положительное целое число либо как числовая строка ("42"), иначе — чистый 400 MISSING_PARAMS с уточнённым текстом Required: userId (positive integer) (для register — плюс phoneNumber). duration у finish аналогично — неотрицательное число либо числовая строка, иначе 400.
Влияние на интеграторов
Корректные вызовы (userId — число или числовая строка) не меняются. Вызовы с ранее «проходившим» некорректным userId/duration (не число, не числовая строка) теперь получают явный 400 вместо непрозрачной ошибки со стороны Битрикс24.
FIX-0714-5: Создание комментария к несуществующей задаче — понятная ошибка вместо ложного успеха
POST /v1/tasks/:taskId/comments и его пакетный вариант POST /v1/tasks/:taskId/comments/batch (action: create) обращаются к Битрикс24 для создания комментария на старой карточке задачи. Если задача не существует или недоступна ключу, Битрикс24 не создаёт комментарий и не возвращает идентификатор.
Было
Оба эндпоинта отвечали успехом с пустым идентификатором — одиночный вызов 201 {"success": true, "data": {"id": null}}, пакетный — элементом {"success": true, "id": null}. Комментарий не создавался, но интегратор не мог отличить это от штатного случая.
Стало
Одиночный вызов возвращает 404 TASK_NOT_FOUND. Пакетный вариант помечает соответствующий элемент как {"success": false, "error": "TASK_NOT_FOUND"}, не отменяя обработку остальных элементов пакета. Отдельный, не связанный с этой ошибкой случай сохраняется: на новой карточке задачи комментарий уходит в чат, и если система не смогла восстановить его id отдельным поиском, ответ по-прежнему success: true с id: null — комментарий в этом случае реально создан.
Влияние на интеграторов
Код, проверяющий data.id / data[i].id на null как признак ошибки, продолжит работать без изменений и получит более точный код ошибки. Код, полагавшийся на молчаливый успех с id: null при недоступной задаче, должен обрабатывать 404 / TASK_NOT_FOUND явно.
FIX-0714-6: Лимит запросов `/v1/search`, `/v1/research`, `/v1/batch` теперь считается на портал
Было
Лимиты (/v1/search 60/мин, /v1/research 20/мин, /v1/batch 30/мин) фактически считались по IP-адресу, а не по порталу. Портал с несколькими API-ключами (или несколько порталов за одним общим egress-IP) мог получить больше документированного лимита, а само ограничение обходилось ротацией IP.
Стало
Лимит считается на портал: все API-ключи одного портала делят единый бакет (60 / 20 / 30 запросов в минуту соответственно). Документированный лимит на тенант теперь применяется корректно и не обходится ни числом ключей, ни сменой IP.
Влияние на интеграторов
Если ваш портал распределял нагрузку на /v1/search / /v1/research / /v1/batch между несколькими API-ключами, суммарный предел теперь — единый лимит портала, а не сумма по ключам. При достижении предела возвращается 429 с заголовком Retry-After (как и раньше).
FIX-0714-7: Календарь: рабочий batch-delete секций, чистые ошибки update и без утечки sync-полей
Было
POST /v1/calendar-sections/batchсaction: "delete"не имел канала дляtype/ownerId, которые требуетcalendar.section.delete— каждый элемент падал на обеих платформах.PATCH /v1/calendar-sections/{id}безtype/ownerId/nameуходил в Битрикс24 и возвращал сырой422с именем внутреннего метода.GET /v1/calendar-sectionsотдавал недокументированные сырые поля Битрикс24GAPI_CALENDAR_ID,CAL_DAV_CON,SYNC_TOKEN,PAGE_TOKEN,EXTERNAL_TYPE(три из них — токены синхронизации).GET /v1/calendar-eventsиGET /v1/calendar-events/{id}отдавали внутреннее полеattendeesEntityList— схема пыталась его вырезать, но не срабатывала из-за несовпадения регистра ключа.
Стало
- Batch-delete секций читает
type/ownerIdиз тела рядом сidsи прокидывает их в каждую команду удаления. Отсутствие любого — чистый400 MISSING_REQUIRED_PARAMSдо вызова Битрикс24. - Partial-update секций требует якорные
type/ownerId/name(у секций нет get-by-id для подстановки) — чистый400, без сырого422и без утечки имени метода. - Оба календарных read-пути убирают перечисленные внутренние/sync-поля из ответа.
Затронутые эндпоинты:
POST /v1/calendar-sections/batchPATCH /v1/calendar-sections/{id}GET /v1/calendar-sectionsGET /v1/calendar-events
FIX-0714-8: PATCH catalog-product-properties снова работает (был неустранимый catch-22)
Было
Обновить свойство товара было невозможно ни при каком теле: PATCH без iblockId → 422 («Required fields: iblockId» — B24 требует его на каждом update), а PATCH с iblockId → 400 READONLY_FIELD (поле только для создания). Итог — весь глагол UPDATE мёртв: ни одно поле нельзя было изменить после создания.
Стало
iblockId теперь подставляется автоматически из существующей записи (pre-fetch, как у catalog-sections), так что PATCH {name:"…"} доходит до B24 с нужным iblockId и возвращает 200. iblockId в теле по-прежнему не нужен (и по-прежнему отклоняется как read-only, если его прислать) — его сохраняет сам сервис.
Влияние на интеграторов
Если раньше ваш PATCH свойства товара всегда падал 422/400 — теперь шлите только изменяемые поля (PATCH {name:"…"}), iblockId передавать не надо.
FIX-0714-9: Валидация entityTypeId: мусорные формы → 400 вместо тихого усечения
Было
Пять поверхностей парсили entityTypeId (селектор типа smart-process / динамической сущности) снисходительно — неякорным parseInt или коэрсирующим Number(), — и мусорная форма молча превращалась в ДРУГОЙ (в рамках портала) тип сущности:
- Путевой
entityTypeId(/v1/items/:entityTypeId/...,/v1/categories/:entityTypeId/...— CRUD и/aggregate):GET /v1/items/1058abcусекался до1058,1e3→1,1.5→1. - Глобальный
POST /v1/batch:params.entityTypeIdчерезNumber()принимал дробные (1.5), hex ('0x10'→ 16), переполнение доInfinity, а также массив[1058]→ 1058. /v1/items/:entityTypeId/userfields/*: собственный парсер —2abcрезолвил пользовательские поля типа2.POST /v1/smart-processes/batch:ids: ['1030abc']усекался до1030— delete/update молча выполнялся против ЧУЖОГО реального типа; дробное число (1030.5) тоже проходило.POST /v1/triggers/fire(entityType="item"):entityTypeId: '1038abc'→ триггер автоматизации выстреливал по типу1038.
Стало
Все пять поверхностей требуют каноничную форму положительного целого (/^[1-9]\d*$/ для строк, Number.isInteger для чисел): любая иная форма → 400 с прежним кодом ошибки поверхности (INVALID_DYNAMIC_PARAM / INVALID_ENTITY_TYPE_ID / BATCH_ITEM_VALIDATION / MISSING_PARAMS) ДО вызова Битрикс24. Aggregate использует общий с CRUD-маршрутами валидатор вместо инлайн-копии.
Влияние на интеграторов
Формы, которые раньше коэрсились в корректное значение и обслуживались — 007 → 7, %20-пробелы, +2 → 2, массив [1058] в batch — теперь отклоняются с 400: значение должно быть каноничным целым без префиксов, хвостов и ведущих нулей. Boolean в batch отклонялся и раньше (коэрсился в reserved-тип); меняется только код ошибки — теперь INVALID_DYNAMIC_PARAM. Корректные вызовы не меняются.
FIX-0714-10: Переоткрытие фидбека очищает поля решения
Было
PATCH /v1/feedback/:id (и админ-эндпоинт PATCH /api/platform/feedback/:id, через который переоткрывает админ-UI) при возврате тикета в активный статус (NEW, REVIEWING, AWAITING_USER, NEEDS_REVIEW) из RESOLVED/WITHDRAWN не сбрасывал resolvedAt, resolvedBy и resolution — они «зависали» от предыдущего закрытия, и переоткрытый тикет выглядел одновременно активным и решённым.
Стало
Возврат тикета из RESOLVED/WITHDRAWN в активный статус очищает resolvedAt, resolvedBy и resolution. RESOLVED/WITHDRAWN по-прежнему проставляют штамп решения; ARCHIVED не трогает поля (архивирование сохраняет историю решения). Обычный переход между активными статусами (например AWAITING_USER → REVIEWING) поля НЕ трогает — на активных тикетах resolution отражает последний комментарий команды. Явно переданный в том же запросе resolution имеет приоритет над очисткой.
Влияние на интеграторов
Если вы читали resolvedAt/resolution переоткрытого тикета и получали значения от прошлого закрытия — теперь они null для активного тикета.
FIX-0714-11: `INVALID_JSON_BODY` больше не цитирует внутренний текст парсера
Было
Шесть маршрутных групп (/api/billing/*, /v1/apps*, /v1/bots*, /v1/keys*, /v1/note*, /v1/infra/servers/* deploy/exec/upload) на битый JSON отвечали 400 с сообщением вида Invalid JSON: Unexpected token } in JSON at position 41 — сырой текст движка V8 (отпечаток рантайма, деталь реализации). Группа deploy/exec/upload при этом не ставила и код ошибки.
Стало
Все шесть отвечают единым статическим сообщением Request body is not valid JSON. — как /v1/<entities> (тот же класс закрывается там отдельным исправлением). На V1-поверхностях код — INVALID_JSON_BODY (deploy/exec/upload теперь тоже его ставит); статус 400 не изменился.
Влияние на интеграторов
Если ваш код парсил текст сообщения (например, вытаскивал позицию ошибки) — опирайтесь на код INVALID_JSON_BODY; позиция ошибки больше не сообщается.
FIX-0714-12: Смета: amount, currency и даты в ответе GET снова заполнены (были null)
Было
Чтение сметы (GET/list/search /v1/quotes) отдавало null для amount, currency, beginDate, closeDate — значения «протекали» только под сырыми ключами Битрикс24 (opportunity, currencyId, begindate, closedate). Запись работала правильно, но READ-проекция теряла все поля-алиасы: сумма сметы и валюта были 100% невидимы через задокументированный API.
Стало
READ-ветка теперь реверс-мапит объявленные алиасы (зеркало write-маппинга): opportunity → amount, currencyId → currency, begindate → beginDate, closedate → closeDate — с приведением типов. Сырые ключи Битрикс24 в ответе больше не появляются.
Влияние на интеграторов
Если вы читали amount/currency смет и получали null — теперь они заполнены. Код, читавший обходным путём сырой opportunity/currencyId из ответа, их там больше не найдёт — переключитесь на документированные amount/currency.
FIX-0714-13: Валидация записи: фантомный чек-лист → 404, мусорные типы и `:id` → 400
Было
POST /v1/tasks/:taskId/checklistна несуществующую задачу возвращал201с правдоподобнымid, хотя пункт не создавался (его нельзя было получить GET-ом).POST /v1/warehousesпринимал нестроковыеtitle/address(число, объект) и пересылал их в Битрикс24 с непредсказуемым результатом;POST /v1/doc-templatesтак же пропускал нестроковыеname/regionи нечисловойnumeratorId.- Нечисловой
:idв путях сущностей с типизированным числовым id (GET/PATCH/DELETE /v1/quotes/abc,/v1/deals/1.5,/v1/leads/1e3) уходил в Битрикс24 как есть — в ответ прилетала непрозрачная ошибка B24 вместо внятного кода. У smart-processes12abcусекалсяparseInt-ом до12и попадал в ЧУЖОЙ тип.
Стало
- Чек-лист: родительская задача проверяется до создания пункта; несуществующая (или недоступная ключу) задача →
404 TASK_NOT_FOUND. - Склады и шаблоны документов: значение не того типа →
400 INVALID_PARAMSбез вызова Битрикс24 (числовая строка вnumeratorIdпо-прежнему принимается). - Сущности с явно типизированным числовым id: не-канонично-целый
:id→400 INVALID_PARAMSдо вызова Битрикс24 (id0— основная воронка сделокcategories— остаётся валидным). Smart-processes сохраняютINVALID_ENTITY_TYPE_IDи теперь отклоняют12abcна GET/PATCH/DELETE, а не усекают до12. Сущности, у которых тип id не задекларирован в схеме, сохраняют прежнее сквозное поведение.
Затронутые эндпоинты: POST /v1/tasks/:taskId/checklist, POST /v1/warehouses, POST /v1/doc-templates + GET/PATCH/DELETE по сущностям с типизированным числовым id.
Влияние на интеграторов
Если ваш код опирался на фантомный 201 от чек-листа или отправлял мусорные значения «на авось» — теперь придёт явный 4xx с кодом. Корректные вызовы не меняются.
FIX-0714-14: Пять тихих false-success/hint-дефектов: честный ответ вместо мнимого успеха
Было
PATCH /v1/userfields/{entity}/{id}clabel— тихий no-op на обновлении: ответ200, но подпись поля не менялась (Битрикс24crm.*.userfield.updateигнорируетLABEL).POST /v1/humanresources/nodes/{id}cparentId— ложный «перенос удался»:nameприменялся,parentIdмолча игнорировался, а в ответе эхом возвращался старый родитель.POST /v1/chats/messages/bulk— псевдонимdialogId: "me"не разрешался в bulk-цикле (одиночные роуты его разрешают) → сообщение уходило не туда.POST /v1/bots/{botId}/chats/{dialogId}/users— предохранительUSERS_NOT_ADDEDбыл мёртвым кодом (не совпадал с формой ответа метода v2) → неудачное добавление проходило как успех.- Ошибки
crm.item.listполучали нерелевантную подсказку «Maximum 50 records…» даже когда причина была иной (например «entity type does not exist»).
Стало
labelна обновлении разворачивается в реальные параметрыEDIT_FORM_LABEL/LIST_COLUMN_LABEL/LIST_FILTER_LABEL— подпись действительно меняется.parentIdиtypeтеперь только для создания: наPATCHони отклоняются с400(перенос — черезPOST /v1/humanresources/nodes/{id}/move), а не имитируют успех.- Bulk-чтение сообщений разрешает
dialogId: "me"для каждого элемента, как и одиночные роуты. - Предохранитель добавления в бот-чат снова срабатывает: непринятые пользователи возвращаются в
warning.USERS_NOT_ADDED. - Подсказки об известных ограничениях привязаны к релевантности сообщения ошибки, а не только к имени метода.
Затронутые эндпоинты:
PATCH /v1/userfields/{entity}/{id}PATCH /v1/humanresources/nodes/{id}POST /v1/chats/messages/bulkPOST /v1/bots/{botId}/chats/{dialogId}/users
FIX-0714-15: GET /v1/task-time теперь честно возвращает больше 50 записей при limit>50
Было
GET /v1/task-time с limit больше 50 возвращал только 50 записей, хотя meta.limit повторял запрошенное значение, а meta.hasMore мог вводить в заблуждение. Клиент, обходивший данные постранично с шагом больше 50, молча терял часть записей.
Стало
Запрос отдаёт до limit записей (максимум 500), собирая их постранично на стороне бэкенда; meta.total и meta.hasMore соответствуют фактически возвращённому окну. При limit больше 50 значение offset теперь указывает на правильную позицию, а не сдвигается на первые страницы.
NEW-0714-16: GET /v1/companies/fields и системные поля catalog-prices получили label и description
В ответе GET /v1/companies/fields теперь у всех полей компании есть человекочитаемые label и description. В GET /v1/catalog-prices/fields те же метки добавлены системным полям extraId, priceScale и timestampX. Метки приходят по-русски. Семантику поля можно получить программно из ответа, без сверки со статической документацией.
FIX-0714-17: /stop и /reboot различают отсутствующий сервер и неподходящий статус
Было
POST /v1/infra/servers/:id/stop и POST /v1/infra/servers/:id/reboot на сервере не в статусе running (например, спящем) отвечали плоским 404 NOT_FOUND с текстом «Running server not found» — по нему нельзя было понять, что сервер существует, и агент решал, что он удалён.
Стало
Оба маршрута ведут себя как /start и /wake: 404 SERVER_NOT_FOUND — только когда сервера с таким id действительно нет; 422 SERVER_WRONG_STATE — когда сервер есть, но не в статусе running. В error.currentState возвращается текущее состояние, в error.availableActions — доступные действия (wake/start/repair/delete).
FIX-0714-18: понятная ошибка на /fields у комментариев и учёта времени задач
Было
Запрос GET /v1/tasks/:taskId/comments/fields отвечал сбивающим с толку 400 INVALID_PARAMS с текстом «taskId and id must be positive integers» (спрашивали про поля — ответ про идентификатор), а GET /v1/tasks/:taskId/time/fields утекал сырой ошибкой Bitrix24 с внутренним PHP-методом и HTML-тегом.
Стало
Обе сущности распознают сегмент fields и возвращают понятный 400 WRONG_PATH: у них нет метода /fields (состав полей описан в справочнике /v1/guide), в тексте перечислены доступные маршруты. Нечисловой или дробный идентификатор в маршрутах по id теперь отклоняется как 400 INVALID_PARAMS до обращения к Bitrix24 — без утечки внутренней ошибки.
FIX-0714-19: Лимиты `/v1/ai/credentials*` считаются на портал; `CREDENTIAL_NOT_FOUND` подсказывает решение
Было
- Лимиты BYOK-маршрутов (
POST /v1/ai/credentials,/:id/test,/:id/fetch-models— 10/мин;/:id/modelsдобавление/удаление — 30/мин) фактически считались по IP-адресу: перебор чужих ключей обходился ротацией IP, а арендаторы за одним общим egress-IP делили один бакет. МаршрутPATCH /:id(при передачеcredentialsон проверяет ключ у апстрима — тот же оракул, что/:id/test) не имел лимита вовсе. 404 CREDENTIAL_NOT_FOUNDот/v1/searchи/v1/researchсодержал только слаг провайдера — без указания, как настроить ключ.
Стало
- Лимит считается на портал: все API-ключи одного портала делят единый бакет; ротация IP и число ключей на предел не влияют. При превышении —
429сRetry-After. ДополнительноPATCH /:id(проверяет ключ у апстрима, как/:id/test) раньше вовсе не имел лимита — теперь тоже 10/мин на портал. - В ответ
CREDENTIAL_NOT_FOUNDдобавлено полеhintс точным рецептом:POST /v1/search/credentials {provider, apiKey}, список провайдеров —GET /v1/search/providers.
FIX-0714-20: PATCH bizproc-шаблонов, роботов и активностей через /v1 применяет изменения
Было
PATCH /v1/bizproc-templates/:id, /v1/bizproc-robots/:code и /v1/bizproc-activities/:code с полями метаданных (name, description, autoExecute) возвращали 422 BITRIX_ERROR "No fields to update." — обновить сущность было нельзя (то же и в пакетных запросах). Поле autoExecute, переданное числом, дополнительно отвергалось как Incorrect field AUTO_EXECUTE!.
Стало
Поля корректно применяются (в том числе autoExecute, переданное числом); эндпоинт подтверждает успех и возвращает id обновлённой сущности. Работают и одиночный PATCH, и пакетные запросы. Создание (POST) не изменилось.
FIX-0714-21: Транскрипция: проверка кошелька до вызова распознавания
Было
POST /v1/audio/transcriptions (и /v1/ai/audio/transcriptions) не проверял состояние кошелька перед вызовом: PREPAY-счёт, ушедший за овердрафт (но ещё не замороженный фоновой проверкой), всё равно запускал распознавание и уводил баланс глубже в минус. Чат и эмбеддинги отклоняли такой вызов заранее; полностью замороженный счёт и раньше блокировался глобально (ACCOUNT_FROZEN).
Стало
Как у чата и эмбеддингов: денежная проверка выполняется до обращения к Whisper. Превышенный овердрафт → 402 insufficient_balance, распознавание не запускается. BYOK-ключи (USER-scope) бесплатны — для них проверки нет и поведение не меняется.
FIX-0714-22: удаление приложения больше не блокируется галактическим хостом на его ключе
Было
DELETE /v1/apps/:id возвращал 409 APP_HAS_ACTIVE_SERVERS, если на ключе приложения оказывался галактический хост (общая инфраструктура портала). Снять хост через удаление приложения было нельзя, а объяснения в ответе не было.
Стало
Галактический хост исключён из проверки блокирующих серверов: он управляется на уровне портала, а не ключа, поэтому не должен мешать удалению приложения. Отдельные серверы приложения (в том числе контейнеры приложений) по-прежнему блокируют удаление с 409 APP_HAS_ACTIVE_SERVERS — их сначала нужно перепривязать к другому ключу.
FIX-0714-23: OpenAPI: тело per-entity батч-эндпоинтов теперь описано верно (action + items/ids/calls)
Было
Спека (GET /v1/openapi.json) описывала тело каждого пер-сущностного батча как {create:[], update:[], delete:[]}. Рантайм же (общий обработчик батчей) требует {action, items|ids|calls} и отвечает 400 INVALID_BATCH_ACTION на документированную форму. Клиент, сгенерированный из спеки (codegen / AI-агент), получал 100% отказ batch-write на всех ~45 пер-сущностных батч-эндпоинтах. Сама фича работает — врала только спека (глобальный POST /v1/batch, /v1/tasks/{taskId}/comments/batch и /v1/guide уже описывали правильную форму).
Стало
Генератор спеки выдаёт правильную форму: один action (create/update/delete/list/get/fields); create/update шлют items, delete — ids, чтения — calls. Совпадает с рантаймом и с глобальным /v1/batch.
Влияние на интеграторов
Если вы генерировали клиент из openapi.json и batch-write падал INVALID_BATCH_ACTION — перегенерируйте: тело теперь {action:"create", items:[…]} вместо {create:[…]}. Handwritten-клиенты, уже славшие {action,…}, не затронуты.
FIX-0714-24: Валюты: fullName, формат и число знаков теперь сохраняются при плоской записи
Было
POST/PATCH /v1/currencies с плоскими fullName, formatString, decimals, decPoint, thousandsSep возвращал успех, но значения молча терялись — Битрикс24 хранит их в структуре локализации по языкам (LANG), а API слал их плоско. Задокументированный обходной путь «передавайте сырой LANG» тоже не работал: LANG — поле только для чтения, запрос отклонялся.
Стало
API упаковывает плоские локализуемые поля в локализацию вашего языка (языка API-ключа) перед вызовом Битрикс24, поэтому плоская запись сохраняется и читается обратно (POST {fullName:"…",decimals:3} → GET вернёт их). Работает на всех путях записи: одиночном, POST /v1/currencies/batch и глобальном POST /v1/batch. Сырой LANG в теле по-прежнему отклоняется как read-only. Разные значения сразу для нескольких языков через API пока не задаются. Язык записи определяется локалью API-ключа (ru или en) и может не совпадать с языком отображения валюты на портале — на порталах с иной локалью (de/pl/ua…) правка ляжет под en.
Влияние на интеграторов
Если вы обходили баг сырым LANG (и упирались в 400 READONLY_FIELD) — уберите его и шлите плоские поля. Уже работавшие плоские запросы теперь ещё и сохраняют значения.
FIX-0714-25: Aggregate уважает обязательные фильтры; infra-валидация не течёт сырым Zod
Было
POST /v1/<entity>/aggregateс сущностью, требующей фильтр (напр.catalog-productsтребуетiblockId), пропускал пустой запрос в Битрикс24 и возвращал сырой422, тогда как GET-list/search на том же условии отдают чистый400.POST /v1/infra/servers/:id/{deploy,exec,upload,logs}при ошибке валидации тела клал вerror.messageмногострочный JSON-сериализованный массив Zod-issues (сырой отпечаток валидатора).
Стало
- Aggregate проверяет обязательные фильтры/параметры до вызова Битрикс24: нет обязательного фильтра —
400 MISSING_REQUIRED_FILTER(напр.catalog-products→iblockId); нет обязательного list-параметра —400 MISSING_REQUIRED_PARAMS(напр.calendar-events→type,ownerId;humanresources-nodes→type). Как у list/search. - Infra-валидация форматирует ошибку компактно (
поле: сообщение; …), как соседнийinfra.ts. Код (VALIDATION_ERROR) и статус400не изменились.
Влияние на интеграторов
Если вы ловили сырой 422 от aggregate без фильтра — теперь придёт 400 MISSING_REQUIRED_FILTER. Если парсили infra-error.message как JSON — теперь это плоская строка поле: сообщение.
FIX-0714-26: Batch: работает per-entity batch для items и создание папок через batch
Было
POST /v1/items/{entityTypeId}/batchвозвращал404— per-entity batch-роут для сущностей с динамическим параметром (items,categories) монтировался по адресу без сегмента параметра (/v1/items/batch), поэтому документированный путь не находился, аentityTypeIdне доходил до команды Битрикс24. Обходной путь — глобальныйPOST /v1/batch— работал.POST /v1/folders/batchсaction: "create"падал на каждом элементе сERROR_ARGUMENT: batch-создание слалоfields[...], тогда какdisk.folder.addsubfolderожидает родительскую папку верхним параметромid, а остальные поля — подdata[...].
Стало
- Per-entity batch-роут для
items/categoriesмонтируется с сегментом:{entityTypeId}и прокидывает валидированныйentityTypeId(положительное целое; id выделенных API вродеdeals=2отклоняются с подсказкой — как в одиночных роутах) в каждую команду — для всех действий:create/update/deleteи read-действийlist/get/fields. - Batch-создание папок повторяет форму одиночного роута:
id=<родитель>&data[...]. ПропущенныйparentId— чистый per-item400до вызова Битрикс24.
Затронутые эндпоинты:
NEW-0714-27: восстановление зависшего exec-канала сервера
Новый эндпоинт POST /v1/infra/servers/:id/unstick принудительно освобождает залипший канал команд Black Hole-сервера, когда деплой или exec продолжает отвечать EXEC_BUSY («Another command is running») даже после DELETE /v1/infra/servers/:id/lock. Эндпоинт снимает блокировку на стороне платформы и разрывает туннель агента — при переподключении агент завершает зависшую команду и освобождает свой мьютекс. Перезагрузка сервера не выполняется.
Если в момент вызова на сервере ещё выполняется легитимная операция (деплой, exec, харден), эндпоинт по умолчанию отвечает 409 OPERATION_IN_PROGRESS и НЕ трогает её — расклинивать нужно только зависший канал. Повторите с ?force=true, если уверены, что канал команд действительно завис.
Ответ: { success: true, data: { backendLockReleased, agentBounced, reconnected } }. Коды ошибок: 404 SERVER_NOT_FOUND, 409 CONFLICT (восстановление уже идёт), 409 OPERATION_IN_PROGRESS (на сервере выполняется операция — повторите с ?force=true), 409 GALAXY_UNSTICK_UNSUPPORTED (для galaxy-хостов и galaxy-приложений не поддерживается), 502 GATEWAY_ERROR. Ошибка /exec (EXEC_BUSY) и провал /deploy (код DEPLOY_FAILED, сообщение «Another command is running») теперь несут подсказку hint с этим эндпоинтом.
NEW-0714-28: описание сервера в `PATCH /v1/infra/servers/:id`
Было
PATCH /v1/infra/servers/:id принимал только displayName. Поле description в контракте отсутствовало, а GET-ответы его не отдавали.
Стало
PATCH /v1/infra/servers/:id принимает опциональное поле description (строка, до 500 символов; пустая строка или null очищает описание; отсутствие поля оставляет текущее значение без изменений). Значение синхронизируется с карточкой приложения в каталоге. Поле description теперь возвращается в GET /v1/infra/servers, GET /v1/infra/servers/:id и в ответе PATCH. Старые запросы без description продолжают работать без изменений.
FIX-0714-29: деплой galaxy-приложения при обрыве связи во время сборки возвращает честную ошибку, а не ложный успех
Было
Если соединение с хостом обрывалось прямо во время сборки galaxy-приложения (частое явление под нагрузкой тяжёлой сборки), POST /v1/infra/servers/:id/deploy мог вернуть 200 со статусом running и пометкой [recovered] в buildLog, хотя новая версия так и не собралась и не поднялась — на хосте продолжал работать прежний контейнер. Повторный деплой упирался в тот же обрыв и снова рапортовал ложный успех.
Стало
Деплой считается восстановленным, только если он полностью завершился: под именем приложения запущен контейнер именно этой попытки, и деплой прошёл до конца. Если связь оборвалась во время сборки — или в любой момент до завершения деплоя — и новая версия не поднялась полностью, эндпоинт возвращает retryable-ошибку 502 с кодом GALAXY_HOST_UNREACHABLE и подсказкой «повторите тот же деплой, не удаляйте сервер» — вместо ложного 200. Только если деплой завершился полностью и потерялся лишь финальный ответ, восстановление в 200 работает как прежде.
BC-0714-30: переименование приложения в каталоге доходит до Битрикс24, publish потерял menuTitle
Поддержка старого формата до: 14.01.2027
Было
PATCH /v1/apps/:id с полем title у приложения, добавленного в каталог, возвращал 200, но не менял ничего из того, что видит пользователь: карточка каталога и привязки мест встраивания на портале (пункт левого меню, вкладки CRM) оставались со старым именем. Отказа не было никогда — вызов всегда успешен.
У POST /v1/apps/:id/publish тело не проверялось, а заголовок места встраивания задавался отдельным полем menuTitle.
Стало
Для приложения в каталоге title — это одна операция над отображаемым именем: имя синхронизируется в карточку каталога (вместе с title пишется catalogTitle) и перепривязывается в места встраивания на портале. Поэтому вызов, который раньше всегда отдавал 200, теперь может честно отказать:
400 NO_USER_TOKEN— приложение не авторизовано на портале, перепривязать места встраивания нечем;400 TITLE_TOO_LONG_FOR_CATALOG— имя приложения в каталоге ограничено 100 символами, тогда какtitleдопускает 255;502 BITRIX_PARTIAL_REBIND— Битрикс24 отклонил привязку. Имя в этом случае не записывается, повтор того же запроса чинит состояние.
Тело POST /v1/apps/:id/publish теперь валидируется, а параметр menuTitle удалён: заголовок места встраивания всегда равен отображаемому имени приложения. Пустое тело по-прежнему работает — публикация берёт значения из записи приложения.
Что делать интеграторам
- Убрать
menuTitleиз тела публикации: поле игнорируется, имя пункта меню задаётся черезtitleиcatalogTitle. - Держать имя приложения в каталоге в пределах 100 символов.
- Обработать отказы при переименовании: на
NO_USER_TOKENавторизовать приложение на портале, наBITRIX_PARTIAL_REBINDповторить запрос. - Учесть побочный эффект: переименование через
titleтеперь пишет иcatalogTitle— у приложения в каталоге эти поля держатся синхронными.
NEW-0714-31: GET /v1/tasks/:taskId/comments/fields — схема полей комментариев задачи
У комментариев к задаче появился метод /fields, как у остальных сущностей: GET /v1/tasks/:taskId/comments/fields возвращает статическую схему из 5 полей (id, taskId, authorId, message, createdAt) с типом, признаком «только для чтения», названием и описанием по-русски. Метод не обращается к Битрикс24. Раньше этот путь возвращал 400 WRONG_PATH — состав полей был доступен только из статических доков.
FIX-0714-32: пер-сущностный batch с action list теперь применяет filter
Было
POST /v1/{entity}/batch с action: "list" игнорировал filter: имена полей не приводились к именам Битрикс24 (например statusId у лидов не превращался в stageId), а операторы $gt / $contains / $in и другие не работали. Вызов возвращал 200 со всей таблицей — тихий отказ с неверными данными. Глобальный POST /v1/batch и POST /v1/{entity}/search при этом фильтровали корректно.
Стало
Пер-сущностный batch пропускает filter через тот же транслятор, что search и глобальный /v1/batch. Псевдонимы полей и операторы ($gt, $gte, $lt, $lte, $ne, $contains, $in, $nin, префиксные >=, <=, ! и т.д.) применяются. Некорректный filter (неизвестное поле у сущности с полной схемой, неподдерживаемый оператор, префиксы @ / !@, логические токены $or / $and) теперь возвращает 400 с указанием индекса вызова, а не молча всю выборку — так же, как на одиночных эндпоинтах.
FIX-0714-33: авто-пагинация больше не дублирует записи на границах страниц
Было
Для методов Битрикс24 без поддержки сортировки (например список объектов хранилища) запись на границе страниц могла сместиться между запросами соседних страниц и попасть в обе — при limit > 50 в выдаче появлялся дубль, занимавший слот, и клиент обрабатывал одну и ту же запись дважды.
Стало
После склейки всех страниц выдача дедуплицируется по id (сохраняется первое вхождение). Для методов со стабильной сортировкой ничего не меняется (дублей нет — операция вхолостую).
FIX-0714-34: список полей шаблона реквизитов больше не пустеет; создание не возвращает чужую запись
Было
GET /v1/requisite-presets/:presetId/fields мог вернуть 200 с пустым data: [], хотя в шаблоне есть поля: метод Битрикс24 отдаёт result то массивом [{…}], то объектом-картой {"0":{…},"1":{…}}, а обработчик принимал только массив. При создании поля (POST …/fields) ответ мог содержать ЧУЖУЮ существующую запись — эхо созданной строки читалось по id из ответа add, а на некоторых порталах чтение по этому id возвращало другое поле.
Стало
Список нормализует обе формы ответа Битрикс24 (массив и объект-карту) — поля больше не теряются. Эхо созданной записи возвращается только если её fieldName совпадает с тем, что создавали; при любом расхождении ответ содержит { id } (сам объект не подставляется), чтобы клиент не получил постороннюю строку.
Влияние на интеграторов
Идентификатор поля шаблона (id) — это позиционный идентификатор Битрикс24: он может меняться после операций записи в шаблоне и на части порталов не является устойчивым ключом. Не кэшируйте id между изменениями шаблона — перечитывайте список полей перед get/update/delete по конкретному полю.
FIX-0714-35: привязка плейсмента для ранее созданных приложений больше не отклоняется по обработчику
Было
POST /v1/placements/bind мог вернуть 400 с кодом PLATFORM_HANDLER_UNRESOLVABLE для приложения, созданного до перехода на единый платформенный обработчик (у такого приложения в качестве обработчика сохранился его собственный технический адрес). Привязка отклонялась даже тогда, когда платформенный обработчик /v1/bitrix-handler был доступен, — приложение нельзя было опубликовать заново через API.
Стало
Привязка проходит: обработчик плейсмента регистрируется на платформенный /v1/bitrix-handler, а в ответе появляются handlerRewritten: true и requestedHandler с исходным значением. Код PLATFORM_HANDLER_UNRESOLVABLE теперь возвращается только когда платформенный обработчик действительно недоступен. POST /v1/placements/unbind снимает такой плейсмент по тому же адресу.
2026-07-13
FIX-0713-1: Платформенные scope ключа OAuth-приложения синхронизируются при создании и правке
Было
Ключ, выписанный вместе с OAuth-приложением через POST /v1/apps, не получал платформенные scope (vibe:infra, vibe:ai, vibe:search, vibe:storage) — в отличие от ключа, созданного в кабинете. Из-за этого POST /v1/infra/servers под таким ключом отвечал 403 INFRA_SCOPE_REQUIRED. Добавление vibe:infra в scope приложения через PATCH /v1/apps/:id меняло только приложение, но не парный ключ — эффекта на доступ не было.
Стало
Парный ключ при создании через POST /v1/apps получает те же платформенные scope по умолчанию, что и ключ из кабинета. Изменение vibe:*-scope через PATCH /v1/apps/:id (добавление И удаление) теперь синхронизируется в активные ключи приложения. Ключ в режиме «только чтение» (READONLY) при этом отклоняется с 403 WRITE_BLOCKED_READONLY_KEY на трёх write-операциях: создание сервера (POST /v1/infra/servers), изменение scope приложения (PATCH /v1/apps/:id) и выписка READWRITE-ключа (POST /v1/apps с mode: "READWRITE").
Влияние на интеграторов
Приложения, созданные через API, теперь могут управлять инфраструктурой без пересоздания. Ключ, у которого приложение уже объявляет vibe:infra, но сам ключ его лишён (старое расхождение), чинится одной правкой: снять vibe:infra из scope приложения и вернуть обратно через PATCH /v1/apps/:id — вторая правка синхронизирует ключ; либо пересоздать приложение. READONLY-ключ по-прежнему может создавать READONLY-приложение (mode: "READONLY").
FIX-0713-2: product-sections: неподдерживаемые фильтры возвращают 400, а не всю таблицу
Было
Операторы (>, <, !, %, $ne, $contains, $nin) и поля вне точного равенства (sort, неизвестные) в фильтре GET /v1/product-sections и POST /v1/product-sections/search молча игнорировались — возвращался код 200 и весь список без фильтра.
Стало
Такие фильтры отклоняются с 400 UNSUPPORTED_FILTER. Фильтруйте точным равенством или $in по id, name, xmlId, code, catalogId, sectionId. Сортировка (order/sort) не изменилась.
Влияние на интеграторов
Вызовы с операторами или filter[sort], ранее возвращавшие 200 с неотфильтрованными данными, теперь возвращают 400 — переключитесь на точное равенство или $in.
FIX-0713-3: причина неудачного первого провижининга сервера теперь видна
Было
Если сервер не поднимался при первом создании (вытесняемый тариф вытеснен, нет свободной ёмкости), он молча оказывался в статусе sleeping без объяснения причины. Клиент, опрашивающий GET /v1/infra/servers/:id, видел sleeping и не понимал, почему деплой заблокирован.
Стало
Такой сервер теперь переходит в status: "error" с заполненным provisionError (человекочитаемая причина) и новым полем provisionErrorCode — машиночитаемой категорией сбоя (PREEMPTIBLE_EVICTION / PROVISION_TIMEOUT / NO_CAPACITY / GENERIC). Поле provisionErrorCode добавлено в ответы GET /v1/infra/servers/:id и GET /v1/infra/servers рядом с provisionError (аддитивно, null, если ошибок не было).
Влияние на интеграторов
Ничего менять не нужно: error — уже существующий статус. Восстановление такого сервера — через POST /v1/infra/servers/:id/start или /repair (не /wake: на статусе error он вернёт 422; поле availableActions в ответе подсказывает доступное действие).
NEW-0713-4: POST и PATCH /v1/tasks/:taskId/time принимают createdDate
Необязательное поле createdDate теперь передаётся в POST /v1/tasks/:taskId/time и PATCH /v1/tasks/:taskId/time/:itemId — запись учёта времени ложится на указанную дату, а не на текущий момент (сценарий понедельничного добивания трека за прошлую неделю). Принимаются ISO 8601 со смещением, ISO без смещения и YYYY-MM-DD — значение уходит в CREATED_DATE без изменений. Если поле не передано, поведение прежнее — дата равна моменту создания.
FIX-0713-5: meta.hasMore перестаёт зависать в true при filter + offset
Было
При пагинации списка с фильтром (например GET /v1/deals с filter и offset) meta.hasMore оставался true на любом offset — даже далеко за пределами meta.total. Цикл while (meta.hasMore) { offset += limit } уходил в бесконечность.
Стало
Когда Bitrix24 игнорирует смещение за пределами отфильтрованного набора и отдаёт его целиком, meta.hasMore считается по окну запроса (offset + limit < meta.total), а не выставляется в true безусловно. При offset=0 ответ прежний; после того как окно перекрыло total, hasMore становится false. Затрагивает список и POST /search для всех сущностей.
FIX-0713-6: GET /v1/lists и /v1/lists/:iblockId/elements учитывают offset
Было
GET /v1/lists/:iblockId/elements и GET /v1/lists молча игнорировали offset — при ?limit=50&offset=50 возвращалась та же первая страница, и клиент, листающий через offset, никогда не доходил до строк 51 и дальше.
Стало
offset мапится в нативный для этих методов параметр start, так что пагинация через offset работает. Явный start сохраняет приоритет, если переданы оба.
NEW-0713-7: Флаг preserveEnv в деплое сохраняет .env при cleanDeploy
Тело POST /v1/infra/servers/:id/deploy приняло необязательный булев preserveEnv (по умолчанию false). Когда cleanDeploy: true (очищает каталог приложения вместе с файлом .env), а preserveEnv: true — существующий .env считывается до очистки и восстанавливается после неё, если в этом же запросе не передан env (переданный env имеет приоритет). Без флага повторный деплой с cleanDeploy мог поднять приложение без переменных окружения — на порту по умолчанию.
NEW-0713-8: у неудачной сборки galaxy-приложения появилась подсказка `buildHint`
Было
Провал сборки galaxy-приложения возвращал только GALAXY_APP_BUILD_FAILED
(и GALAXY_APP_START_FAILED) c «хвостом» лога в buildLog — разобрать причину
можно было лишь вручную. GET /v1/infra/servers/:id по ERROR-приложению отдавал
короткий provisionError, но без готовой рекомендации.
Стало
Ответ теперь несёт разобранную причину. В теле ошибки POST /v1/infra/servers/:id/deploy
(502) при классифицируемом провале дополнительно приходят error.category
(машинная категория, например MODULE_NOT_FOUND, INSTALL_AUTH, RESOURCE) и
error.buildHint — локализованная строка-рекомендация с конкретным следующим шагом.
GET /v1/infra/servers/:id по ERROR-приложению добавляет то же поле data.buildHint.
Поля аддитивные: если причина не распознана, они отсутствуют (buildHint — null),
а provisionError/buildLog продолжают приходить как раньше — старые запросы
не меняются.
FIX-0713-9: одновременные одинаковые сохранения исходников больше не создают дубль-версию
Было
Два одновременных сохранения одинаковых байтов на один сервер (POST /v1/infra/servers/:id/sources, а также автосохранение при деплое) при редком стечении таймингов создавали две байт-идентичные версии вместо одной — дедупликация по содержимому была best-effort.
Стало
Дедупликация детерминированная: одинаковые байты, отправленные одновременно, всегда сходятся на одну версию. На POST /v1/infra/servers/:id/sources оба ответа возвращают один и тот же versionId, проигравший запрос получает deduplicated: true; автосохранение при деплое сходится на ту же единственную версию (формат ответа деплоя не менялся).
2026-07-12
NEW-0712-1: Ответ при исчерпании AI-квоты подсказывает путь к пополнению
Ошибка 402 ai_quota_exhausted с reason: wallet_empty теперь дополнительно возвращает поля hint и topupUrl. hint — короткая подсказка на английском: месячная AI-квота и баланс портала исчерпаны, и администратор портала может пополнить баланс, чтобы возобновить работу. topupUrl — ссылка, которую администратор открывает для пополнения. Поля аддитивны: прежние поля ответа (reason, resetAt) не изменились, а ветки reason: breaker и reason: wallet_off их не несут. Так агент-клиент может передать человеку путь к пополнению. Ошибку возвращают вызовы моделей, см. POST /v1/chat/completions.
FIX-0712-2: `sleep-now` на сервере с давно прошедшим расчётным пробуждением теперь честно усыпляет
Было
Для сервера с включённым расписанием пробуждения вызов POST /v1/infra/servers/:id/sleep-now мог навсегда возвращать { data: { slept: false, reason: "WAKE_IMMINENT" } }, если денормализованное «следующее пробуждение» осталось в далёком прошлом (сервер разбудили вне планировщика, и штамп не пересчитался). Сервер никогда не засыпал и держал тариф включённым круглосуточно.
Стало
Штамп старше динамического окна («поле для манёвра» = margin + типовой lead) считается протухшим, а не «неминуемым»: sleep-now честно усыпляет сервер и отвечает { success: true }, после чего планировщик тихо перекатывает якорь к будущему окну без пробуждения. WAKE_IMMINENT остаётся штатным ответом только для действительно близкого пробуждения.
2026-07-11
FIX-0711-1: галактики: ошибка GALAXY_HOST_UNREACHABLE стала действенной — подсказка hint и точная причина в provisionError
Было
Когда хост галактики был временно недоступен, POST /v1/infra/servers/:id/deploy отвечал голым 502 с кодом GALAXY_HOST_UNREACHABLE, а слот, созданный одним вызовом POST /v1/infra/servers с source, переходил в статус ошибки с текстом «Deploy failed unexpectedly — please retry; details are in the server logs». Ни причина, ни путь восстановления не сообщались — клиенты удаляли слот и создавали новый, что не помогает: новый слот попадает на тот же хост.
Стало
Ответ 502 GALAXY_HOST_UNREACHABLE — при деплое, exec-команде и удалении (DELETE /v1/infra/servers/:id) — несёт структурную подсказку error.hint: состояние временное, слот и его данные целы, нужно повторить тот же запрос через 1–2 минуты, удалять слот не нужно. На пути «создание с source» поле provisionError теперь содержит настоящую причину («Galaxy host … became unreachable during build …») и тот же совет повторить деплой в существующий слот. Дополнительно: когда несколько серверов работают под одним OAuth-приложением, сохранённая версия исходников больше не теряется из-за конфликта нумерации версий — ни при автосохранении на деплое, ни при явном сохранении через POST /v1/infra/servers/:id/sources.
Влияние на интеграторов
Изменение аддитивное: коды и статусы ответов не менялись, добавилось поле error.hint и уточнился текст provisionError. Обновлять клиентов не нужно. AI-агентам стоит читать hint.recovery — там прямо сказано, что делать.
NEW-0711-2: отдельный код ошибки, когда на портале не установлен модуль Vibecode Connector
Выписка ключа приложения через модуль-коннектор (POST /v1/apps) теперь при отсутствии на портале модуля vibecodeconnector возвращает 409 с кодом CONNECTOR_MODULE_NOT_INSTALLED и понятным сообщением «установите модуль», вместо прежнего непрозрачного 502 CONNECTOR_APP_INSTALL_FAILED. Это законное, постоянное состояние (особенно для коробки), а не временный сбой — повторять запрос бессмысленно, нужно установить модуль на портале. Остальные коды выписки не изменились.
FIX-0711-3: pacing в GET /v1/ai/quota теперь может заполняться платформой по умолчанию (поэтапная раскатка)
Платформа теперь умеет включать пейсинг (равномерное расходование AI-квоты) по умолчанию для портала — без действий администратора. Раскатка поэтапная (пилотные порталы → все порталы): пока портал не попал под платформенный default-on, поле data.pacing в ответе GET /v1/ai/quota остаётся null, как и раньше. Когда портал под default-on: mode: "ignore" — информационный режим, active: false — лимит окна не отклоняет запросы (отказ 429 ai_pacing_limited невозможен). Форма ответа не изменилась; интеграторам, уже обрабатывающим data.pacing как опциональное поле, ничего менять не нужно.
2026-07-10
NEW-0710-1: управление окнами пробуждения по расписанию (wake-schedules)
Новый CRUD-контракт на серверах Black Hole: GET/POST /v1/infra/servers/:id/wake-schedules, PATCH/DELETE /v1/infra/servers/:id/wake-schedules/:scheduleId. Позволяет объявить одно или несколько повторяющихся окон пробуждения (cronExpr + обязательная IANA-таймзона timezone, необязательные label/lead/enabled) — платформа будит спящий сервер к нужному моменту, дальше запуск задачи делает собственный cron внутри VM.
Раскатывается постепенно и пока не работает на всех порталах — до включения на конкретном портале запрос отвечает 403 с кодом WAKE_SCHEDULE_DISABLED. Доступно только для серверов в режиме BLACKHOLE (иначе 400 BLACKHOLE_ONLY) и пока не поддержано для галактик (400 GALAXY_NOT_SUPPORTED на хосте и вложенном приложении — появится позже). Минимальный интервал между срабатываниями и лимит окон на сервер (50) заданы платформой; нарушение отвечает 400 CADENCE_TOO_LOW и 403 WAKE_SCHEDULE_LIMIT соответственно. Ответ создания/обновления дополнительно несёт поле tzWarning — предупреждение о том, что таймзона в VM могла разойтись с таймзоной окна, если сервер не переразвёртывался. PATCH заменяет окно целиком (PUT-семантика): не переданные необязательные поля сбрасываются к значениям по умолчанию — enabled→true, label/lead→пусто. Передавайте полный объект окна при обновлении.
Затронутые эндпоинты: GET|POST /v1/infra/servers/:id/wake-schedules, PATCH|DELETE /v1/infra/servers/:id/wake-schedules/:scheduleId.
NEW-0710-2: машиночитаемый код tz-предупреждения в ответах wake-schedules (`tzWarningCode`)
Ответ создания/обновления окна пробуждения (POST/PATCH /v1/infra/servers/:id/wake-schedules[/:scheduleId]) теперь дополнительно несёт поле tzWarningCode рядом с существующим текстовым tzWarning — "SINGLE_ZONE" / "MULTI_ZONE" / null (когда после мутации у сервера не осталось включённых окон). Значение — машиночитаемый эквивалент того же предупреждения, чтобы клиент мог локализовать текст сам вместо отображения английской строки tzWarning как есть. Поле аддитивное, tzWarning не меняется и остаётся для обратной совместимости.
Затронутые эндпоинты: POST|PATCH /v1/infra/servers/:id/wake-schedules[/:scheduleId].
FIX-0710-3: wake-schedules: расписание запрещено на серверах «Всегда онлайн»
POST/PATCH /v1/infra/servers/:id/wake-schedules теперь отклоняют создание или обновление окна пробуждения на сервере в режиме «Всегда онлайн» (24/7) — ответ 400 с кодом ALWAYS_ON_CONFLICT. Такой сервер работает на невытесняемом тарифе и не уходит в авто-сон, поэтому расписание пробуждения тихо нарушило бы оплаченную гарантию постоянной доступности. Обычные засыпающие серверы Black Hole и вытесняемые агенты/боты не затронуты. Отключите «Всегда онлайн», чтобы объявлять окна пробуждения.
Затронутые эндпоинты: POST /v1/infra/servers/:id/wake-schedules, PATCH /v1/infra/servers/:id/wake-schedules/:scheduleId.
FIX-0710-4: wake-schedules теперь можно объявлять на вложенных приложениях galaxy
Было
POST/PATCH /v1/infra/servers/:id/wake-schedules отвечал 400 GALAXY_NOT_SUPPORTED для любого сервера семейства galaxy — как для самого хоста, так и для вложенных приложений (kind=GALAXY_APP).
Стало
Вложенные приложения galaxy (kind=GALAXY_APP) теперь принимаются — окно пробуждения можно объявить на конкретном приложении, и платформа разбудит его (и при необходимости — хост) к нужному моменту. Сам хост galaxy по-прежнему отвечает 400 GALAXY_NOT_SUPPORTED: расписание объявляется на приложениях, а не на хосте.
Затронутые эндпоинты: POST|GET /v1/infra/servers/:id/wake-schedules, PATCH|DELETE /v1/infra/servers/:id/wake-schedules/:scheduleId.
FIX-0710-5: sleep-now откладывает засыпание, когда пробуждение по расписанию близко
Было
POST /v1/infra/servers/:id/sleep-now всегда усыплял сервер немедленно, даже если ближайшее пробуждение по расписанию наступало через минуту — сервер сразу же просыпался обратно.
Стало
Если у сервера есть включённое расписание пробуждения и ближайшее пробуждение наступит в пределах защитного окна, вызов не усыпляет сервер и отвечает 200 с телом { "success": true, "data": { "slept": false, "reason": "WAKE_IMMINENT" } }. В остальных случаях сервер усыпляется, а планировщик разбудит его в следующее окно.
Влияние на интеграторов
Блок data с полем slept: false приходит только при отказе усыпить — проверяйте его наличие, если полагаетесь на то, что после вызова сервер обязательно засыпает. При успешном усыплении ответ состоит из одного success: true. Старые вызовы серверов без расписания работают без изменений.
FIX-0710-6: не-стриминговые chat/completions и embeddings больше не обрываются на 360 секундах
Было
Не-стриминговый (stream:false) запрос к POST /v1/chat/completions (и POST /v1/embeddings), генерация которого длилась дольше ~2 минут, стабильно обрывался на ~360 секундах с {"code":"ai_provider_unavailable","message":"This operation was aborted"} — независимо от таймаута клиента. При этом на обречённую генерацию тратилось тройное количество вычислений.
Стало
Такой запрос обрабатывается в рамках единого бюджета ~850 секунд (одна попытка на весь бюджет, без утроения нагрузки). Если генерация всё же не укладывается в бюджет, возвращается осмысленный 503 с кодом ai_provider_timeout, локализованным userMessage и подсказкой (уменьшить объём запроса либо использовать потоковый режим stream:true) — без заголовка Retry-After (таймаут не транзиентный). Обрыв соединения клиентом теперь немедленно отменяет генерацию на стороне провайдера. Стриминговый режим (stream:true) этим лимитом не затрагивался.
FIX-0710-7: requisite-presets/:presetId/fields и requisite-links: сортировка, фильтрация и валидация параметров списка
Было
GET /v1/requisite-presets/:presetId/fields игнорировал sort/order и любые filter[...] — всегда возвращал полный список в порядке Битрикс24. GET /v1/requisite-links и POST /v1/requisite-links/search принимали только простое равенство, а операторы ($gte, > и т.п.), sort/order и неизвестные поля молча пропускались — в результате приходила вся таблица.
Стало
Оба списка честно применяют sort/order (в том числе форму order[поле]=asc|desc) и filter. У requisite-links работают операторы сравнения ($gt/$gte/$lt/$lte, $in/$nin, префиксы >=/>). Неизвестное поле фильтра или сортировки теперь возвращает 400 (UNKNOWN_FILTER_FIELD / UNKNOWN_SORT_FIELD), логический оператор верхнего уровня ($or/$and) — 400 INVALID_FILTER_OPERATOR, а фильтр по entityId без entityTypeId — 400 MISSING_ENTITY_TYPE_ID вместо сырого «Access denied».
Влияние на интеграторов
Запросы по документированным полям продолжают работать и теперь действительно сортируются/фильтруются. Если раньше вы полагались на молчаливое игнорирование неизвестного параметра, теперь он вернёт 400 — уберите опечатку или используйте поле из ответа.
FIX-0710-8: Сортировка банковских реквизитов по id учитывает направление
Было
Запрос списка банковских реквизитов (GET /v1/bank-details) с сортировкой по id по убыванию (?sort=-id) возвращал записи по возрастанию — направление сортировки молча игнорировалось.
Стало
?sort=-id (и ?sort=id) сортирует по идентификатору в запрошенном направлении.
Влияние на интеграторов
Изменений в коде не требуется — запросы, полагавшиеся на сортировку по id, теперь возвращают ожидаемый порядок.
NEW-0710-9: GET /v1/quotes/fields — объявлены ~26 полей предложения с человеческими названиями
Схема сущности «предложения» (quotes) пополнена ~26 полями, которые Битрикс24 возвращал, но которые не были объявлены: quoteNumber, updatedBy, lastActivityBy, lastActivityTime, content, terms, leadId, storageTypeId, storageElementIds, personTypeId, webformId, lastCommunicationTime, contactIds, contacts, locationId, taxValue, actualDate, mycompanyId, utmSource/utmMedium/utmCampaign/utmContent/utmTerm, lastCommunicationCallTime/lastCommunicationEmailTime/lastCommunicationImolTime/lastCommunicationWebformTime. Теперь они видны в GET /v1/quotes/fields с читаемыми названиями (вместо служебных STORAGE_TYPE_ID/UTM_SOURCE), а их значения приводятся к типам (числа, даты в ISO) в ответах list/get. Названиями снабжены также ранее необъявленные stageId, opened, closed.
FIX-0710-10: PATCH без единого записываемого поля отклоняется явной ошибкой
Было
PATCH /v1/{entity}/:id с пустым телом — или с телом, в котором нет ни одного распознанного записываемого поля (например из-за опечатки в имени поля) — доходил до Битрикс24, который молча игнорировал такой запрос и отвечал успехом. Обёртка возвращала 200 с неизменённым объектом, и клиент считал, что правка применилась, хотя на деле ничего не менялось — риск тихой рассинхронизации, особенно у AI-агентов. Это касалось всех трёх поверхностей обновления: одиночного PATCH /v1/{entity}/:id, пакетного POST /v1/{entity}/batch и общего POST /v1/batch.
Стало
Обновление с пустым телом возвращает 400 EMPTY_UPDATE_BODY (в пакетных вызовах — ошибку элемента) на всех трёх поверхностях, до обращения к Битрикс24. Для /v1/catalog-products/:id добавлена строгая проверка: PATCH, в котором нет ни одного распознанного записываемого поля, возвращает 400 NO_RECOGNIZED_UPDATE_FIELDS. Осмысленное обновление всегда несёт хотя бы одно поле — передайте распознаваемое поле (пользовательские свойства propertyNNN и поля UF_* тоже принимаются). Сущности, у которых уже была проверка полей, ведут себя как прежде.
Дополнительно у товаров каталога в GET /v1/catalog-products/fields появились и стали доступны для явного select, фильтра и сортировки поля из контракта обновления товара — code, xmlId, sort, vatId, height, length, width, previewText, detailText и другие (раньше фильтр по ним отвечал 400 UNKNOWN_FILTER_FIELD). Надёжно фильтровать и сортировать стоит по индексируемым полям (code, xmlId, sort, vatId, размеры); по полнотекстовым (previewText, detailText) Битрикс24 фильтр может игнорировать. Ответы списков без явного select не изменились.
FIX-0710-11: leads, companies, quotes — поля дат в /fields теперь createdTime и updatedTime
Было
GET /v1/leads/fields, GET /v1/companies/fields и GET /v1/quotes/fields объявляли поля createdAt и updatedAt, хотя в ответах list/get/search Bitrix24 всегда возвращал createdTime и updatedTime — прочитать значение по имени createdAt/updatedAt было нельзя. Фильтр и сортировка при этом принимали имена createdAt/updatedAt.
Стало
/fields этих сущностей объявляют реальные ключи createdTime и updatedTime — как у contacts, invoices и items. Ключи в теле ответов те же (createdTime/updatedTime приходили всегда), но теперь значение нормализуется к ISO-8601 в UTC (2026-04-15T07:00:00.000Z) — раньше приходило в исходном формате Bitrix24 со смещением портала (2026-04-15T10:00:00+03:00). Момент времени тот же, меняется только представление.
Влияние на интеграторов
Читайте даты из createdTime и updatedTime (ключи не менялись). Если вы сравниваете строку даты побайтово или кэшируете по ней — учтите переход +03:00 → Z (тот же момент времени). В фильтре и сортировке используйте createdTime/updatedTime; прежние createdAt/updatedAt теперь возвращают 400 UNKNOWN_FILTER_FIELD (фильтр) и 400 UNKNOWN_SORT_FIELD (сортировка). На запись createdAt/updatedAt больше не отклоняются как readonly — они игнорируются как неизвестные поля (как у contacts/invoices/items); задать дату создания/изменения по-прежнему нельзя.
FIX-0710-12: сон-настройки: флип в «Всегда онлайн» теперь запрещён при активных окнах пробуждения
PATCH /v1/infra/servers/:id/sleep теперь отклоняет установку sleepAfterMinutes: null («Всегда онлайн», 24/7) на сервере, у которого есть включённые окна расписания пробуждения — ответ 400 с кодом ALWAYS_ON_CONFLICT. Это обратное направление уже существующего гейта: раньше запрещалось создавать окно пробуждения на сервере «Всегда онлайн», теперь симметрично запрещён и обратный переход — иначе сервер продолжал бы засыпать по расписанию, тихо нарушая оплаченную гарантию постоянной доступности. Удалите или отключите окна пробуждения, либо оставьте таймаут сна вместо «Никогда», чтобы включить «Всегда онлайн».
Затронутые эндпоинты: PATCH /v1/infra/servers/:id/sleep.
NEW-0710-13: placement.bind на коробочном Битрикс24 отдаёт понятный SESSION_REQUIRES_ADMIN для не-администратора
На коробочном (self-hosted) Битрикс24 привязка плейсмента через путь developer-key требует, чтобы пользователь ключа был администратором аккаунта. Раньше запрос не-администратора возвращал глухой 502 BITRIX_UNAVAILABLE.
Теперь POST /v1/placements/bind распознаёт отказ доступа со стороны Битрикс24 и возвращает 403 SESSION_REQUIRES_ADMIN с подсказкой: выполните привязку под учётной записью администратора аккаунта либо попросите администратора выдать эти права. Требование заранее видно в GET /v1/me — блок placements.bindPrerequisite для коробочных аккаунтов теперь включает код SESSION_REQUIRES_ADMIN.
FIX-0710-14: capabilities в GET /v1/me отражают режим только для чтения (READONLY)
Было
Для ключа в режиме READONLY GET /v1/me отдавал capabilities.managedBots.create, agents.create, servers.create и apps.* со значением available: true, хотя любая операция записи блокируется с 403 WRITE_BLOCKED_READONLY_KEY. Агент видел «можно создать» и упирался в отказ.
Стало
Для READONLY-ключа эти write-способности возвращаются как available: false с reason: "WRITE_BLOCKED_READONLY_KEY" и подсказкой переключить ключ в режим чтение+запись. Способности только для чтения и AI Router не меняются. Для ключей в режиме READWRITE ответ прежний.
FIX-0710-15: автор обращения без скоупа vibe:feedback снова может отвечать на своё обращение
Было
POST /v1/feedback/:id/comments отклонял автора обращения с 403 FEEDBACK_SCOPE_REQUIRED, если у ключа не было скоупа vibe:feedback — хотя ветка автора была задокументирована. Автор не мог ответить на своё же обращение в статусе AWAITING_USER, и обращение зависало.
Стало
Проверка на автора выполняется до скоуп-гейта: автор, отвечающий тем же ключом, которым создал обращение, попадает в авторскую ветку (authorType=USER, правило мяча AWAITING_USER → NEEDS_REVIEW) даже без скоупа vibe:feedback. Ключ, не являющийся автором и не имеющий скоупа, по-прежнему получает 403 FEEDBACK_SCOPE_REQUIRED.
NEW-0710-16: Пейсинг AI-квоты: поле pacing в ответе и код ошибки 429 ai_pacing_limited
Ответ GET /v1/ai/quota дополнен полем pacing — состоянием равномерного расходования месячной AI-квоты (сглаживание пиков через суточный и недельный лимит поверх общего месячного лимита; включается администратором портала в кабинете /ai). Значение null, если пейсинг выключен на платформе или не настроен для портала; иначе объект { mode, active, day, week }: mode — режим реакции на превышение (wallet/block/ignore), active — сработает ли превышение прямо сейчас в отказ (false в режиме наблюдения и всегда false при режиме ignore — окна считаются и только информируют, 429 не отдаётся), day и week — по { pctUsed, resetAt } в процентах от собственного лимита окна. Ответ по-прежнему кэшируется на 30 секунд, поэтому состояние пейсинга может отставать на этот срок.
При срабатывании суточного или недельного лимита запросы POST /v1/chat/completions, POST /v1/embeddings и POST /v1/audio/transcriptions могут вернуть 429 с телом { success: false, error: { code: "ai_pacing_limited", type: "rate_limit_exceeded", message, reason, overageDenied, resetAt, retryAfter } } и заголовком Retry-After. reason — какое окно пробито (day_window или week_window); overageDenied — причина отказа в платном превышении лимита (wallet_empty, breaker, wallet_off), либо null в режиме жёсткой блокировки. Повторять вызов раньше Retry-After/resetAt не имеет смысла — квота не станет доступнее за это время. По умолчанию пейсинг выключен — включается платформой.
FIX-0710-17: GET /v1/pages, /v1/sites и POST /v1/{pages,sites}/search теперь учитывают offset
Было
Запрос списка страниц или сайтов со смещением (GET /v1/pages?offset=50, POST /v1/pages/search с offset) возвращал ошибку 422 BITRIX_ERROR "Unknown parameter: start". Первая страница (без offset) работала.
Стало
offset для pages и sites обрабатывается корректно: возвращается запрошенное окно [offset, offset+limit), а meta.total и meta.hasMore считаются по числу строк. Менять клиентский код не нужно — вызовы без offset работают как раньше.
NEW-0710-18: /fields сущностей документов, каталогов, цен, телефонных линий, позиций корзины и лидов дополнены метаданными
GET /:entity/fields нескольких сущностей теперь несёт более полную метаданную схемы. У документов (GET /v1/documents/fields), каталогов (GET /v1/catalogs/fields), цен каталога (GET /v1/catalog-prices/fields) и телефонных линий по каждому полю добавлены человекочитаемые label и description по-русски.
У позиций корзины (GET /v1/basket-items/fields) поля weight, vatRate, measureCode, measureName, dimensions, productXmlId, catalogXmlId помечены флагом nullable — они могут прийти пустыми. У лидов (GET /v1/leads/fields) тем же флагом помечены secondName, sourceDescription, comments, а также объявлены ранее неописанные поля originatorId, dateClosed, lastCommunicationTime и метки utmSource/utmMedium/utmCampaign/utmContent/utmTerm — теперь по ним работают фильтр и сортировка, а dateClosed нормализуется к ISO-8601.
У сайтов лендингов объявлены измерения для группировки, поэтому POST /v1/sites/aggregate с groupBy (type, active, deleted, lang, tplId, domainId, createdById, modifiedById) больше не отвечает Available: .. У документов агрегация отключена (все числовые поля — идентификаторы): POST /v1/documents/aggregate возвращает 404.
Прежние вызовы работают без изменений — это дополнительные метаданные полей.
NEW-0710-19: Идемпотентное создание сервера — заголовок Idempotency-Key
POST /v1/infra/servers теперь принимает необязательный заголовок Idempotency-Key для создания отдельного сервера (standalone). Повторный запрос с тем же ключом — например, после потерянного ответа или разрыва сети — не создаёт второй сервер: он возвращает тот же самый сервер, что и первый запрос, со статусом 201 и заголовком ответа Idempotent-Replayed: true. Ключ — строка 1–255 символов из набора [A-Za-z0-9_.:-]; область действия — ваш API-ключ.
При повторе одноразовые SSH-учётные данные (ssh.privateKey / ssh.password) НЕ выдаются повторно — в теле ответа они null и добавлено поле note с пояснением. Сохраните учётные данные из ответа первого создания.
Новые коды ошибок: 400 INVALID_IDEMPOTENCY_KEY (ключ не проходит валидацию), 400 IDEMPOTENCY_UNSUPPORTED_WITH_GRADUATION (ключ вместе с graduateFrom для выделенного сервера не поддерживается), 409 IDEMPOTENCY_KEY_ALREADY_USED (ключ уже использован для сервера, который затем был удалён), 409 IDEMPOTENCY_CONCURRENT_RETRY (параллельный запрос с тем же ключом ещё выполняется — повторите чуть позже).
Заголовок учитывается только для standalone-серверов. На порталах с размещением в галактике корректный ключ игнорируется без ошибки, и защита от повторного создания на этот путь не распространяется.
Дополнительно: ответ создания теперь возвращает каноническое имя сервера (с суффиксом при разрешении конфликта имён), а не имя из запроса — при совпадении имён это ранее расходилось.
NEW-0710-20: локализованное сообщение при отказе переключения в режим OPEN
Ответы PATCH /v1/infra/servers/:id/mode с кодами OPEN_MODE_DISABLED (режим OPEN выключен на уровне платформы) и OPEN_MODE_NOT_ALLOWED (режим OPEN запрещён политикой портала) теперь дополнительно несут поле error.userMessage — локализованную человекочитаемую формулировку с подсказкой использовать Deploy API как штатную замену прямого SSH. Поле аддитивное: error.message (английская техническая строка), error.code и HTTP-статус не меняются. Совпадает по форме с уже существующим error.userMessage у отказа OPEN_MODE_REQUIRES_COMMERCIAL. Текст userMessage зависит от локали владельца ключа.
BC-0710-21: Поля конфигураций открытых линий приведены к camelCase и описаны в /fields
Поддержка старого формата до: 10.01.2027
Было
GET /v1/openline-configs, GET /v1/openline-configs/{id} и POST /v1/openline-configs/search возвращали большинство полей конфигурации в «родном» для Bitrix24 виде — в верхнем регистре через подчёркивание (CRM_CREATE, WELCOME_MESSAGE, QUEUE_TIME и другие). Справка GET /v1/openline-configs/fields описывала только 6 полей, поэтому остальные не были видны для программного обнаружения.
Стало
Все поля конфигурации приведены к единому camelCase (crmCreate, welcomeMessage, queueTime и так далее), а /fields описывает полный набор полей с человеческими названиями (label) и описаниями (description). Фильтрация и сортировка по новым camelCase-именам работают. При записи (create/update) по-прежнему принимаются оба регистра — прежние вызовы с верхним регистром в теле не ломаются.
Что делать интеграторам
Читать поля ответа по camelCase-именам: config.crmCreate вместо config.CRM_CREATE. Соответствие имён — прямая транслитерация из верхнего регистра в camelCase (WELCOME_BOT_ID → welcomeBotId, WORKTIME_TO → workTimeTo, LINE_NAME → name). Полный перечень новых имён — в справке /fields.
NEW-0710-22: Добавлен GET /v1/warehouses/fields — схема полей склада
Появился эндпоинт GET /v1/warehouses/fields, возвращающий схему 19 полей склада: для каждого поля — тип (type), признак «только для чтения» (readonly), человеческое название (label) и описание (description). Склады — кастомный роут (без сущностной схемы), поэтому раньше у них не было справки полей, которая есть у автогенерируемых сущностей. Ответ — { success: true, data: { fields: { … } } }. Требуется скоуп catalog.
FIX-0710-23: публикация приложения восстанавливается при рассинхроне плейсмента
Было
При публикации (POST /v1/apps/:id/publish) или обновлении плейсментов (PATCH /v1/apps/:id), если плейсмент был зарегистрирован на стороне Bitrix24, но отсутствовал в приложении (дрейф после снятия с публикации), привязка падала с ошибкой «Handler already binded» и плейсмент оставался несинхронизированным.
Стало
При такой ошибке платформа один раз снимает устаревшую привязку и повторяет её — плейсмент синхронизируется автоматически. Восстановление срабатывает только на подтверждённом конфликте, поэтому «живой» плейсмент никогда не снимается по ошибке; плейсменты, которым нужны непереносимые OPTIONS (чат-виджеты, фоновый обработчик), из авто-восстановления исключены и по-прежнему сообщают предупреждение.
FIX-0710-24: storage: sha256 объекта заполняется при прямой загрузке
Было
Поле sha256 в ответе на загрузку объекта хранилища всегда было null для пользовательских объектов, хотя схема описывала его как «вычисляется при загрузке».
Стало
При прямой загрузке (POST /v1/storage/objects/upload, файлы до 10 МБ) sha256 теперь содержит SHA-256-хэш содержимого объекта. По нему можно проверять целостность и находить дубликаты (одинаковое содержимое — одинаковый хэш). Для presigned- и multipart-загрузок байты идут напрямую в хранилище мимо платформы, поэтому там sha256 пока остаётся null.
FIX-0710-25: поле pacing.active в GET /v1/ai/quota больше не сообщает об активном лимите при нулевой квоте
Было
При включённом равномерном расходовании на портале с нулевой месячной квотой (жёсткая блокировка через переопределение monthlyVibes = 0 или ещё не инициализированный расход) поле data.pacing.active возвращало true — хотя отказ 429 ai_pacing_limited в этом состоянии структурно невозможен: запросы отклоняются месячным лимитом, а не оконным.
Стало
data.pacing.active возвращает true только когда превышение дневного или недельного окна действительно может привести к 429 ai_pacing_limited. При нулевой квоте поле честно отдаёт false. Форма ответа не изменилась; клиентам, строившим backoff-логику по active, действий не требуется — сигнал стал точнее.
2026-07-09
FIX-0709-1: транзиентная перегрузка БД теперь отдаёт 503 с Retry-After вместо 500
Было
В редкой форме кратковременной перегрузки БД (исчерпание коннектов) часть запросов (включая POST /v1/infra/servers) возвращала 500.
Стало
Такие запросы возвращают 503 с кодом POOL_EXHAUSTED и заголовком Retry-After. Ошибка транзиентная — повтори запрос с задержкой (backoff).
Влияние на интеграторов
Клиенты, повторяющие запросы при 5xx, теперь должны обрабатывать 503 и уважать Retry-After. POST /v1/infra/servers неидемпотентен — повтор может создать дубль сервера, поэтому повторяйте с backoff, а не немедленно.
FIX-0709-2: placements/bind честнее сообщает о необходимости подписки Маркетплейса
Было
При привязке плейсмента через ключ приложения на портале без активной подписки «BitrixGPT + Маркетплейс» Bitrix24 отвечал отказом доступа, а POST /v1/placements/bind возвращал непрозрачный 502 BITRIX_UNAVAILABLE без указания причины. Подписка требуется на пути через ключ разработчика независимо от коммерческого тарифа, но GET /v1/me не сообщал об этом предусловии заранее.
Стало
Отказ по подписке теперь классифицируется: POST /v1/placements/bind возвращает 403 с кодом B24_MARKET_SUBSCRIPTION_REQUIRED (или B24_MARKET_TRIAL_USED, если демо уже использован), понятным userMessage и ссылкой на оформление в details.upgradeUrl. У ключа приложения в GET /v1/me добавлен блок placements.bindPrerequisite — он заранее описывает предусловие Bitrix24 (путь через ключ разработчика требует активной подписки Маркетплейса, устаревший OAuth-путь — коммерческого тарифа) и перечисляет коды ошибок. Прочие отказы привязки (неизвестный clientId, устаревший embedding) по-прежнему возвращают 502 BITRIX_UNAVAILABLE.
Влияние на интеграторов
Менять ничего не нужно, успешные вызовы не затронуты. Тем, кто обрабатывал 502 BITRIX_UNAVAILABLE при привязке, стоит дополнительно ловить 403 B24_MARKET_SUBSCRIPTION_REQUIRED / B24_MARKET_TRIAL_USED и подсказывать пользователю оформить подписку на портале.
FIX-0709-3: bindPrerequisite в GET /v1/me учитывает регион портала
Было
Блок placements.bindPrerequisite у ключа приложения описывал предусловие привязки одинаково для всех порталов: subscriptionRequired: true, рецепт «попросите администратора активировать подписку Маркетплейса» и список кодов с B24_MARKET_SUBSCRIPTION_REQUIRED / B24_MARKET_TRIAL_USED. На порталах с тарифной моделью доступа отказ привязки приходит с кодом INT_TARIFF_REQUIRED, а подписки там нет — предложенный рецепт был невыполним.
Стало
Блок зависит от региона портала. На порталах с подписочной моделью доступа он прежний. На порталах с тарифной моделью subscriptionRequired равен false, текст note описывает требование коммерческого тарифа Битрикс24, а errorCodes содержит INT_TARIFF_REQUIRED и BITRIX_UNAVAILABLE — только те коды, которые портал действительно может получить.
Влияние на интеграторов
Успешные вызовы не затронуты. Если вы читали errorCodes из bindPrerequisite как полный перечень, учтите, что теперь он сужен до достижимых на конкретном портале кодов. Сами коды и поведение POST /v1/placements/bind не менялись.
BC-0709-4: Поля CRM, задач и лендингов приведены к реальному контракту Битрикс24
Поддержка старого формата до: 09.01.2027
Было
GET /v1/deal-categories возвращал isLocked строкой "Y"/"N", а GET /v1/payments — paySystemIsCash строкой "Y"/"N". GET /v1/leads в каждом ответе отдавал служебное поле searchContent (внутренний полнотекстовый индекс Битрикс24). Поля paySystemXmlId, dateMarked, dateResponsibleId у платежей приходили как есть, без нормализации даты.
Стало
isLocked (воронки) и paySystemIsCash (платежи) теперь имеют тип boolean (true/false). searchContent в ответах GET /v1/leads больше не отдаётся. У платежей добавлены задекларированные поля paySystemXmlId (строка), dateMarked и dateResponsibleId (даты нормализованы в единый формат ISO с суффиксом Z). У задач добавлены changedBy/closedBy/statusChangedBy (только для чтения — попытка записать возвращает 400 READONLY_FIELD). GET /v1/currencies/fields отдаёт человекочитаемые label для полей и поле lang; GET /v1/deal-categories/fields — label для полей; GET /v1/sites/fields — флаг nullable у полей, которые могут прийти пустыми.
Что делать интеграторам
Читать isLocked и paySystemIsCash как boolean, а не сравнивать со строкой "Y". Если код опирался на поле searchContent у лидов — перестать: оно было служебным и не задокументированным.
FIX-0709-5: создание сущности с пустым телом отклоняется явной ошибкой
Было
Создание сущности с пустым телом (или вовсе без тела, но с заголовком Content-Type: application/json) доходило до Битрикс24 и молча создавало сущность со значениями по умолчанию — включая сделки, лиды, контакты, компании, счета и смарт-процессы. Повторный вызов или клиент без тела так плодил мусорные записи в CRM. Это касалось всех трёх поверхностей создания: одиночного POST /v1/{entity}, пакетного POST /v1/{entity}/batch и общего POST /v1/batch.
Стало
Такой запрос возвращает 400 EMPTY_CREATE_BODY (в пакетных вызовах — ошибку элемента) до обращения к Битрикс24, на всех трёх поверхностях. Осмысленное создание всегда несёт хотя бы одно поле — передайте нужные поля в теле запроса. Сущности, у которых уже есть проверка обязательных полей, ведут себя как прежде.
FIX-0709-6: снятие блокировки сервера принимает пустое тело JSON
Было
DELETE /v1/infra/servers/:id/lock с заголовком Content-Type: application/json и пустым телом возвращал 400 (пустое JSON-тело). Чтобы снять зависшую блокировку, приходилось слать явное {} — не зная этого, клиент упирался в тупик.
Стало
Пустое тело при этом заголовке принимается как {}; запрос без тела отрабатывает штатно и снимает блокировку. Явное {} по-прежнему работает.
FIX-0709-7: события портала теперь будят спящее galaxy-приложение
Было
Событие Битрикса, отправленное на подписку спящего galaxy-приложения, не будило его — доставка уходила в повторные попытки и после их исчерпания терялась.
Стало
Платформа будит спящее galaxy-приложение при доставке события и доставляет его после подъёма — как для обычного сервера.
NEW-0709-8: camelCase-ключи внутри communications при создании дела
POST /v1/activities теперь принимает вложенные ключи элементов communications в camelCase (type, value, entityTypeId, entityId) — единообразно с остальным API. Раньше вложенные ключи принимались только в ВЕРХНЕМ регистре Bitrix24 (TYPE, VALUE, ENTITY_TYPE_ID, ENTITY_ID), а camelCase-форма молча отбрасывалась — communications: [{ "type": …, "value": … }] возвращал 422 «COMMUNICATIONS is not defined or invalid», тогда как [{ "TYPE": …, "VALUE": … }] создавал дело. ВЕРХНИЙ регистр по-прежнему работает; если в одном объекте заданы обе формы одного ключа, приоритет у ВЕРХНЕГО регистра.
FIX-0709-9: снятие с публикации убирает вкладку по всем обработчикам приложения
Было
POST /v1/apps/:id/unpublish снимал placement только по обработчику, совпадающему с ожидаемым платформенным адресом. Если вкладка была привязана к техническому адресу самого приложения, Битрикс24 не находил её и не удалял — вкладка оставалась висеть в карточке CRM, и через API её уже нельзя было убрать.
Стало
Снятие с публикации теперь убирает placement по всем обработчикам приложения, включая привязанные к техническому адресу сервера — осиротевшая вкладка исчезает.
NEW-0709-10: Самоописание ключей в GET /v1/guide
Ответ GET /v1/guide дополнен блоком data.keysAuth. Он описывает два эндпоинта самоописания — GET /v1/me и GET /v1/guide — и содержит ссылки на документацию: контракт ответа каждого из них, режим доступа ключа и общее описание типов ключей.
Оба эндпоинта работают по одному заголовку X-Api-Key, токен сессии для них не нужен.
Поле аддитивное: существующие клиенты не затронуты.
Затронутые эндпоинты: GET /v1/guide
FIX-0709-11: GET /v1/{entity}/:id теперь учитывает ?select=
Проекция полей ?select= на чтении одной записи по id раньше игнорировалась: ответ всегда приходил со всеми полями, хотя /v1/me заявляет, что ?select= работает «на списке, чтении по id и POST /search». Теперь чтение по id проецирует ответ так же, как список и поиск, — приводя поведение в соответствие с задокументированным.
Было
GET /v1/leads/42?select=id,title возвращал полную запись (все поля).
Стало
GET /v1/leads/42?select=id,title возвращает только id и title. Поддерживаются формы через запятую (?select=id,title), массивом (?select[]=id&select[]=title) и с индексами (?select[0]=id&select[1]=title); id включается в ответ всегда. Неизвестное имя поля молча пропускается — это не ошибка. При одновременном ?select= и ?include= связанная сущность в ответе сохраняется. Индексная форма (?select[0]=…) раньше возвращала 500 и на списке GET /v1/{entity} — теперь тоже проецирует корректно.
FIX-0709-12: /fields сущностей заказов, позиций корзины, пресетов реквизитов и шаблонов документов приведены к реальному контракту B24
Было
GET /:entity/fields (и генерируемая по нему OpenAPI-схема) объявлял поля, которые Bitrix24 не возвращает: provider у шаблонов документов; reserved, sumPaid, dateBill, datePayBefore, datePaid, empPaidId, userEmail, userName на верхнем уровне заказа; module, fUserId, lid, dateRefresh, subscribe, reserved, reserveQuantity у позиций корзины; originatorId у пресетов реквизитов. Фильтрация и сортировка по этим полям молча не срабатывали. При этом реально приходящие поля не были объявлены: requisiteLink у заказа, type/properties/reservations у позиции корзины. Пресеты реквизитов принимали countryId/entityTypeId на обновление, где Bitrix24 их молча игнорирует.
Стало
Несуществующие поля убраны из /fields и OpenAPI. Реально приходящие поля объявлены: у заказа — requisiteLink (объект requisiteId/bankDetailId/mcRequisiteId/mcBankDetailId, только чтение); у позиции корзины — type, properties, reservations (только чтение). У пресетов реквизитов countryId и entityTypeId помечены как задаваемые только при создании: обновление возвращает 400 READONLY_FIELD вместо тихого игнорирования.
Влияние на интеграторов
Ответы list/get не меняются — убранные поля и так никогда не приходили. Если запрос фильтровал или сортировал по убранному полю, теперь он вернёт 400 — используйте реальные поля из /fields (например, даты и суммы оплаты у заказа лежат внутри массива payments, а не на верхнем уровне). Обновление countryId/entityTypeId у пресета реквизитов теперь явно отклоняется — задавайте эти поля только при создании.
Затронутые эндпоинты: GET /v1/orders/fields, GET /v1/basket-items/fields, GET /v1/requisite-presets/fields, GET /v1/doc-templates/fields
NEW-0709-13: GET /:entity/fields отдаёт человекочитаемые label и описания для сделок, лидов, счетов, дел, справочников и комментариев таймлайна
Ответ GET /v1/deals/fields, /v1/leads/fields, /v1/invoices/fields, /v1/activities/fields, /v1/statuses/fields и /v1/timelines/fields теперь несёт по каждому полю человекочитаемые label и description по-русски. У полей со служебными кодами добавлены словари enum: у сделок — stageSemanticId (P — в работе, S — успех, F — провал); у дел — typeId, direction, priority, status, notifyType и descriptionType. Прежние вызовы работают без изменений — это дополнительные поля метаданных, форма ответа не меняется.
FIX-0709-14: POST /v1/batch — единый формат ошибок под-вызовов и totals только для list/search
Было
Ошибка Bitrix24 внутри успешного 200-ответа POST /v1/batch (например, get несуществующего элемента) попадала в data.errors[<id>] в исходной форме Bitrix24 { error, error_description } — не в общем для V1 конверте { code, message }, который используют ошибки валидации и любой другой ответ API. Поле data.totals[<id>] при этом заполнялось для любого действия, включая get/create/update/delete, где одиночное число рядом с единственной записью не имеет смысла.
Стало
Ошибка под-вызова приводится к { code, message } (error → code, error_description → message), как у остальных ошибок. data.totals[<id>] заполняется только для действий list и search — там, где счётчик совпадений реально имеет смысл.
FIX-0709-15: GET /v1/openline-configs — нормализация пустых значений в ответе
Было
Ответы GET /v1/openline-configs и GET /v1/openline-configs/:id отдавали служебные поля в неудобных для клиента формах: KPI_FIRST_ANSWER_LIST, KPI_FURTHER_ANSWER_LIST, DEFAULT_OPERATOR_DATA приходили как null (на них падал .map/.length); AUTO_CLOSE_TEXT для незаданного значения приходил как "" в карточке и как null в списке; WORKTIME_HOLIDAYS/WORKTIME_DAYOFF для пустого набора приходили как [""] (массив с одной пустой строкой).
Стало
Списки нормализованы: null → []. AUTO_CLOSE_TEXT приведён к единому null для пустого значения и в списке, и в карточке. WORKTIME_HOLIDAYS/WORKTIME_DAYOFF для пустого набора приходят как []. Список и карточка теперь отдают одинаковую форму этих полей.
FIX-0709-16: GET /v1/users/fields отдаёт возможные значения (items) для UF-полей-перечислений
Было
Пользовательское поле-перечисление (UF_USR_* типа «список») приходило в GET /v1/users/fields как { "type": "string", "label": "…" } — без списка возможных значений. Причина: метод user.fields возвращает у UF-полей только подпись, без типа и вариантов, поэтому перечисление было неотличимо от строки.
Стало
Такое поле приходит с настоящим типом и списком вариантов: { "type": "enumeration", "label": "…", "items": [ { "ID": "…", "VALUE": "…", "DEF": "…", "XML_ID": "…" }, … ] }. Значения дочитываются из user.userfield.list — для этого у ключа должен быть скоуп user.userfield; если он не выдан, поле по-прежнему отдаётся с подписью, но без items (мягкая деградация). Заодно у остальных UF-полей (money, date и т. п.) в ответе появляется их настоящий тип вместо string.
FIX-0709-17: подсказка hint при пустой очереди событий появляется по факту устойчивой пустоты
Было
GET /v1/bots/:botId/events увеличивал счётчик пустых ответов ровно на каждый запрос, и поле hint появлялось строго после пятого подряд пустого ответа. Число в тексте подсказки совпадало с количеством сделанных запросов.
Стало
Счётчик пустых ответов обновляется периодически, а не на каждый запрос, поэтому hint появляется после устойчивой пустоты очереди — при рекомендованном интервале опроса 2–5 секунд спустя примерно пару минут непрерывно пустого опроса. Число N в тексте отражает количество зафиксированных периодов пустоты, а не точное количество сделанных запросов. Правило «доставлено событие → счётчик и подсказка сбрасываются» не изменилось.
Влияние на интеграторов
Менять код не нужно. Если вы опирались на появление hint строго на пятом запросе или трактовали N как точное число запросов — используйте persisted и наличие событий как основной сигнал, а hint как диагностическую подсказку.
2026-07-08
FIX-0708-1: GET /v1/doc-templates и POST /search честят order и offset
Было
Параметры order и offset на GET /v1/doc-templates и POST /v1/doc-templates/search молча игнорировались: список всегда возвращался в порядке возрастания по id, а offset не смещал окно выборки. Причина — метод Bitrix24 отдаёт шаблоны объектом с ключами-id, и заданный порядок терялся при разворачивании ответа.
Стало
Сортировка (order[поле]=asc|desc, в том числе по нескольким полям) и постраничная выборка (offset/limit) применяются на стороне Vibecode: набор шаблонов вытягивается полностью, сортируется и режется по запрошенному окну. total и hasMore считаются от фактически собранного набора.
Влияние на интеграторов
Тем, кто полагался на неявный порядок «по возрастанию id» при offset=0 без сортировки, менять ничего не нужно — это остаётся поведением по умолчанию. POST /v1/doc-templates/batch (batch-list) не затронут. Строковый порядок (name, region) — побайтовый, без учёта локали.
NEW-0708-2: GET /v1/apps/:id/sources — новое поле linkedServerSources
GET /v1/apps/:id/sources теперь дополнительно возвращает поле linkedServerSources — версии исходников, сохранённые под сервером (через POST /v1/infra/servers/:id/sources или авто-сохранение при деплое), сгруппированные по серверу, каждая со своим serverContext. Такие версии раньше не попадали в этот ответ, если сохранялись под личным ключом, — теперь они видны.
Поле аддитивное: versions, totalVersions, currentVersionId и totalSizeBytes не изменились и по-прежнему перечисляют только версии, привязанные к приложению. Рядом приходят linkedServerSourcesTruncated (признак усечения при очень большом числе версий) и linkedServerHint с указателем на GET /v1/infra/servers/:serverId/sources — авторитетный полный список и скачивание этих версий. Секция заполняется для автора приложения (личный ключ) и администратора портала; при вызове ключом OAuth-приложения она пуста, а при ?sha256=-пробе не вычисляется.
NEW-0708-3: поле preemptible в ответе списка тарифов серверов
Ответ GET /v1/infra/providers/:id/plans теперь формально описывает поле preemptible у каждого тарифа. Вытесняемый тариф дешевле, но облако принудительно перезапускает такую машину примерно раз в сутки — он не подходит для непрерывных 24/7-нагрузок. Для сервера, агента или бота, которые должны работать без перерывов, выбирайте невытесняемый тариф (preemptible равно false или отсутствует).
Поле уже отдавалось в ответе рантаймом — эта запись фиксирует его в OpenAPI и документации; менять существующие интеграции не требуется.
NEW-0708-4: GET /v1/models/:id теперь показывает цену преемника у снятых моделей и поле replaced_by
Для модели, снятой с публикации, запрос детали по идентификатору теперь возвращает поле replaced_by с идентификатором модели-преемника, на которую фактически уходят вызовы, а поле pricing показывает цену этого преемника — ту, по которой запрос и тарифицируется. Раньше pricing показывал собственную нулевую цену снятой строки, из-за чего модель выглядела бесплатной, хотя вызовы обслуживал платный преемник. Обычные модели и прежние вызовы не меняются.
FIX-0708-5: автопагинация списков сохраняет порядок строк при limit выше 550
Было
Списочные запросы с автопагинацией — GET /v1/{entity}?limit=… и POST /v1/{entity}/search — при limit выше ~550 возвращали строки с нарушенным порядком: внутренние страницы выдачи склеивались не в том порядке, в котором их отдал Битрикс24, поэтому параметр order на итоговом массиве не соблюдался. Если записей было больше, чем limit, обрезка окна могла выбросить строки из середины отсортированной выборки, оставив более поздние.
Стало
Страницы склеиваются строго в порядке выдачи Битрикс24: строки приходят в заказанной сортировке при любом limit, а обрезка по limit больше не выбрасывает строки из середины выборки из-за неверного порядка склейки.
Влияние на интеграторов
Менять ничего не нужно. Если вы пересортировывали большие выборки на своей стороне как обходной путь — это больше не требуется.
FIX-0708-6: автопагинация больше не теряет молча страницу при сбое пакетного подзапроса
Было
При limit > 50 список собирается пакетными подзапросами по 50 записей. Если Битрикс24 отклонял один подзапрос (чаще всего по лимиту запросов — QUERY_LIMIT_EXCEEDED), его страница молча выпадала из середины выборки: ответ оставался 200, в данных образовывалась необнаружимая дыра в 50 записей (например, записи 1–200 и 251–600 без 201–250), а meta.total и meta.hasMore выглядели непротиворечиво.
Стало
Для списков, обычного поиска, пакетных подвызовов и агрегаций результат — всегда непрерывный префикс выборки: записи после сбойной страницы отбрасываются, meta.hasMore остаётся true, и в ответе появляется meta.pageErrorSample { code, message } с причиной сбоя — по образцу meta.windowErrorSample оконного поиска. Поле добавлено в ответы списков (например GET /v1/deals), в POST /v1/{entity}/search (например сделки), в meta подвызовов POST /v1/batch и в data.meta агрегаций POST /v1/{entity}/aggregate (там оно объясняет, почему recordsProcessed меньше totalRecords). В оконном поиске (широкий диапазон дат) набор собирается из окон, поэтому при потере страницы внутри одного окна ответ может недосчитаться хвоста этого окна — признак неполноты там именно meta.pageErrorSample, а не meta.hasMore. Во всех случаях поле появляется, только если итоговая страница действительно короче limit: полный ответ ложным сигналом не помечается. Неполный ответ не кэшируется: повторный запрос сразу идёт в Битрикс24.
Влияние на интеграторов
Менять клиентский код не нужно: выборки, которые раньше могли содержать незаметную дыру, теперь корректны, а причина недобора видна в meta.pageErrorSample. Дочитать остаток можно повторным запросом с offset, равным сумме исходного offset и числа полученных записей, — кроме оконного поиска по широкому диапазону дат (там offset не поддерживается: сузьте диапазон или повторите запрос позже).
BC-0708-7: структурированный вывод: обрезанный или пустой ответ теперь возвращает 422, а не пустой 200
Поддержка старого формата до: 08.07.2026
Было
POST /v1/chat/completions со response_format (json_object или json_schema) при обрыве генерации мог вернуть 200 с content: null (или обрезанной, непарсимой строкой JSON) и предупреждением, которое клиенты не замечали. Чаще всего это случалось на моделях рассуждения: фаза рассуждения расходовала весь бюджет max_tokens до того, как модель писала JSON. Ответ выглядел успешным, но разобрать его было нельзя.
Стало
Такой запрос возвращает 422 с code: "structured_output_truncated", полями finishReason, suggestedMaxTokens (рекомендованный увеличенный max_tokens для повтора) и param: "max_tokens". В потоковом режиме перед data: [DONE] приходит служебный кадр {"error":{"code":"structured_output_truncated"}} — читайте поток до [DONE]. Дополнительно: для бесплатных моделей рассуждения при слишком маленьком max_tokens платформа поднимает бюджет до безопасного минимума и помечает успешный ответ предупреждением MAX_TOKENS_RAISED. Обрезанная попытка по-прежнему расходует и тарифицирует токены.
Что делать интеграторам
Обрабатывайте 422 structured_output_truncated в ветке ошибок и повторяйте запрос с бóльшим max_tokens (можно взять значение из suggestedMaxTokens). Для строго-детерминированного JSON задавайте max_tokens с запасом или используйте обычную (не «thinking») модель.
2026-07-07
FIX-0707-1: smart-processes: linkedUserFields принимает Y/N и булевы значения
Было
POST /v1/smart-processes и PATCH /v1/smart-processes/:entityTypeId с linkedUserFields работали только когда значение флага было строго "true"/"false". Значение в конвенции "Y"/"N" (как у всех остальных полей смарт-процесса) или булево true/false молча игнорировалось: запрос возвращал success: true, но отображение в пользовательском поле не включалось.
Стало
Значения linkedUserFields нормализуются так же, как вложенный флаг relations[].isChildrenListEnabled: true/"Y"/"yes"/1 → включено, false/"N"/"no"/0 → выключено. Прежние вызовы с "true"/"false" продолжают работать без изменений.
Влияние на интеграторов
Ничего менять не нужно — вызовы, которые раньше «молча не срабатывали» с "Y", теперь применяются корректно.
BC-0707-2: Нормализация вложенных полей карточки заказа
Поддержка старого формата до: 06.01.2027
Было
GET /v1/orders/{id} отдавал вложенные массивы clients, payments, basketItems в сыром виде Bitrix24: булевы поля строками "Y" и "N" (payments[].paid, clients[].isPrimary, basketItems[].vatIncluded и другие), даты внутри payments и basketItems со смещением +03:00, а поле companyId со значением 0, когда компания не задана. Поле accountNumber при создании и обновлении молча игнорировалось.
Стало
Вложенные Y/N-поля приходят как boolean (true или false); вложенные даты нормализованы к UTC (оканчиваются на Z); companyId при отсутствии компании приходит null вместо 0; accountNumber стал полем только для чтения — попытка задать его при создании или обновлении возвращает 400 с кодом READONLY_FIELD.
Что делать интеграторам
Читать вложенные Y/N-поля как boolean, а не сравнивать со строкой "Y"; трактовать null вместо 0 как признак «компания не задана»; не передавать accountNumber в теле создания и обновления — номер присваивается автоматически.
FIX-0707-3: Спека /v1/openapi.json приведена к фактическому рантайму
Было
Машинная OpenAPI-спека генерировалась из статической метаданной сущностей и расходилась с реальными ответами: ни одно поле не помечено nullable, вложенные массивы карточки заказа типизировались как строка, у списочных методов не описаны filter и select, у операций объявлены только успешные коды и 403.
Стало
Спека теперь отражает контракт. Nullable-поля выводятся в форме type: ["<тип>", "null"]. Объектные и массив-объектов поля типизируются честно, включая вложенные clients, payments, basketItems, propertyValues у GET /v1/orders/{id}. На списочных методах описаны query-параметры filter и select. Операции несут стандартные коды ошибок 400, 401, 404, 422 в едином конверте { success:false, error:{ code, message } }. Схемы *Input объявляют обязательные при создании поля. Дополнительно GET /v1/orders/fields отдаёт clients как массив вместо object. SDK, сгенерированный из спеки, получает корректную типизацию.
BC-0707-4: /search: авто-оконный поиск при полном отказе отдаёт настоящую ошибку Bitrix24
Поддержка старого формата до: 07.09.2026
Было
Любой неуспешный авто-оконный POST /v1/{entity}/search возвращал 502 { "error": { "code": "WINDOWED_SEARCH_FAILED" } } с общим советом «добавьте autoWindow:false».
Стало
Ответ совпадает с тем же запросом на узком диапазоне — реальный код и сообщение: отклонённое поле фильтра/сортировки → 400 UNKNOWN_FILTER_FIELD / 400 INVALID_PARAMS; нет прав → 403; лимит запросов / перегрузка очереди → 429 + Retry-After; таймаут → 503; недоступность Bitrix24 → 502 BITRIX_UNAVAILABLE. Частичный отказ окон (статус 200) теперь несёт meta.windowErrorSample { code, message }.
Что делать интеграторам
Если вы ветвились на error.code === "WINDOWED_SEARCH_FAILED" (например, чтобы повторить с autoWindow:false) — ветвитесь на реальные коды. Обходной путь autoWindow:false остался; он полезен там, где действительно помогает (подсказка 429 QUEUE_TIMEOUT называет его). Ответ полного отказа больше не несёт блок meta (autoWindowed/windowCount/windowErrors) — сигнал теперь в самом коде/сообщении ошибки; meta.windowErrorSample остаётся на частичном отказе (статус 200).
FIX-0707-5: списки на портале без модуля стабильно отдают 409, а не 429
Было
На портале, где модуль «Универсальные списки» не включён, вызовы /v1/lists отдавали понятный 409 LISTS_MODULE_NOT_ENABLED только для первых нескольких запросов. После этого встроенная защита от циклов ошибок срабатывала и все последующие вызовы возвращали 429 ERROR_LOOP_DETECTED — реальная причина (модуль не подключён) переставала быть видна.
Стало
Сигнал «метод недоступен на портале» больше не учитывается защитой от циклов, поэтому вызовы lists.* на портале без модуля стабильно возвращают 409 LISTS_MODULE_NOT_ENABLED при любом числе повторов. Ответ остаётся действенным: подключите модуль на портале и повторите запрос.
NEW-0707-6: Подсказка error.hint на 400 при создании сервера без source и без provider/plan/region
POST /v1/infra/servers при 400 INVALID_REQUEST из-за отсутствующих provider/plan/region (и отсутствующего source) на портале с galaxy-размещением теперь дополнительно возвращает объект error.hint с полями reason (почему запрос отклонён на этом портале), recovery (рекомендованный one-shot путь и рабочая двухшаговая альтернатива) и example (готовый скелет тела one-shot запроса). Поля error.code и error.message не изменились — подсказка строго аддитивна; на порталах без galaxy-размещения ответ прежний, без hint.
Подсказка также возвращается в ветке 400 RUNTIME_PARAM_REMOVED (создание с runtime, но без source, на портале с galaxy-размещением), а тело с placement: "dedicated" получает отдельный вариант подсказки — под выделенный сервер, с сохранением намерения и добавлением недостающего tuple provider/plan/region, без увода в galaxy-контейнер.
FIX-0707-7: Galaxy-чек-лист деплоя в /v1/me приведён к фактическому контракту
Было: шаг 2 чек-листа deployment.galaxyApp.checklist предписывал POST /v1/infra/servers { name } без source и без provider/plan/region — такой вызов всегда завершался 400 INVALID_REQUEST. Правило CREATE не объясняло, что для двухшагового пути обязателен полный набор provider/plan/region, а newAppPlacement.note обещала выделенный standalone-VM там, где создание возвращает galaxy-слот с next: "deploy". Favicon-гайд направлял в этот же неработающий порядок; окно уборки недеплоенного слота указывалось как «~12-20 мин» при фактических ~20-25.
Стало: рекомендованный путь — один вызов POST /v1/infra/servers { name, source, runtime, start } (provider/plan/region опускаются). Двухшаговый путь описан правдиво: создание без source требует полный provider/plan/region (значения для galaxy информационны — приложение наследует хост), на galaxy-размещении возвращает слот с next: "deploy"; недеплоенный слот убирается в ERROR после ~20 мин (проверка каждые 5 мин). Favicon: основной путь — собственный /icon.svg в архиве (id не нужен); платформенный URL — альтернатива через two-step или re-deploy. Шаг опроса статуса получил ветку status=error → provisionError/buildLog → re-deploy.
Влияние на интеграторов: агенты, следующие чек-листу, деплоят с первого вызова. Поведение эндпоинтов не менялось — обновлены только тексты /v1/me и описание в /v1/openapi.json; существующие интеграции продолжают работать без изменений.
NEW-0707-8: AI-квота компании доступна через API
Новый эндпоинт GET /v1/ai/quota возвращает состояние месячной AI-квоты портала: процент израсходованного лимита (pctUsed, честное значение — при перерасходе больше 100), признак исчерпания (exhausted), дату сброса (resetAt, скользящее 30-дневное окно) и разбивку по моделям — количество запросов, токены и долю месячного лимита на каждую модель (byModel[].pctOfLimit). Абсолютные значения лимита в Вайбах не раскрываются — только проценты, как в кабинете. Требуется скоуп vibe:ai.
2026-07-06
NEW-0706-1: Поля pricing.perCall и pricing.perMinute в каталоге моделей
Ответы GET /v1/models и GET /v1/models/{model} дополнены необязательными полями в объекте pricing: perCall — стоимость одного вызова в Вайбах, perMinute — стоимость одной минуты аудио в Вайбах (для моделей распознавания речи). Поля появляются только у моделей, для которых соответствующая базовая цена больше нуля; у остальных моделей объект pricing не меняется — существующие запросы работают без изменений.
NEW-0706-2: Новый код ошибки 402 ai_quota_exhausted на AI-эндпоинтах
При включённом контроле месячной AI-квоты портала запросы POST /v1/chat/completions, POST /v1/embeddings и POST /v1/audio/transcriptions могут вернуть 402 с телом { success: false, error: { code: "ai_quota_exhausted", type: "insufficient_quota", reason, resetAt? } }. Поле reason различает три случая: breaker — сработал часовой предохранитель расходов сверх квоты, wallet_empty — квота исчерпана и на балансе портала нет средств, wallet_off — расход сверх квоты для портала недоступен. resetAt — момент, когда запросы снова начнут проходить (для wallet_off при полном отключении может отсутствовать). Пока квота портала не исчерпана, поведение эндпоинтов не меняется.
FIX-0706-3: Расход сверх AI-квоты списывается по базовой цене модели из каталога
Было
При активном контроле месячной AI-квоты портала запросы сверх квоты на POST /v1/chat/completions, POST /v1/embeddings и POST /v1/audio/transcriptions списывались с баланса портала по внутренним ставкам программы квот — со скидками непикового времени; итоговую цену нельзя было увидеть в каталоге моделей.
Стало
Расход сверх квоты списывается по базовой цене модели из публичного каталога — той же, что возвращается в поле pricing ответа GET /v1/models, включая новые perCall и perMinute для не-токенных моделей. Скидки непикового времени применяются только к списанию квоты, а не к денежному балансу. Расход в рамках квоты по-прежнему не списывается с баланса портала.
Влияние на интеграторов
Менять ничего не требуется. Стоимость работы сверх квоты теперь можно рассчитать заранее по каталожной цене модели.
NEW-0706-4: Модель эмбеддингов bitrix/embeddings доступна в API
Эндпоинт POST /v1/embeddings теперь обслуживается моделью bitrix/embeddings — преобразование текста в векторные представления для семантического поиска, кластеризации и retrieval (RAG). Модель бесплатная, тарификация только по входным токенам. Список моделей с поддержкой эмбеддингов — GET /v1/models.
FIX-0706-5: Деплой честно сообщает об ошибке, если новый билд не занял порт
Развёртывание через POST /v1/infra/servers/:id/deploy теперь проверяет, что порт держит именно новый сервис. Если предыдущий процесс продолжает слушать порт, а новый билд крэш-луп'ит с EADDRINUSE, деплой честно завершается ошибкой вместо ложного успеха; порт, занятый оставшимся процессом того же приложения, при возможности освобождается автоматически.
Было
Старая версия продолжала отвечать 200, деплой рапортовал успех, а новый билд так и не поднимался — без ошибки и без подсказки.
Стало
Шаг healthcheck возвращает ошибку с указанием EADDRINUSE и порта, а шаг stop_existing освобождает порт от оставшегося процесса приложения (или предупреждает и продолжает, если освободить нельзя).
NEW-0706-6: фильтр: оператор $nin (NOT IN) для исключения набора значений
Было
Отобрать записи, у которых поле НЕ входит в набор значений, было нельзя: оператор $in (IN) поддерживался, а обратного не было. Родные префиксы Битрикс24 @ (IN) и !@ (NOT IN) в имени поля ({ "!@categoryId": [1, 3] }) не транслировались — такой фильтр по сделкам возвращал 400 UNKNOWN_FILTER_FIELD.
Стало
Добавлен оператор $nin: { "filter": { "categoryId": { "$nin": [1, 3] } } } вернёт записи со всеми значениями, кроме перечисленных (NOT IN). Симметричен $in. Родные префиксы Битрикс24 @ / !@ в имени поля по-прежнему не поддерживаются, но теперь дают понятный 400 INVALID_FILTER_FIELD с подсказкой перейти на $in / $nin — вместо невнятной ошибки или молча проигнорированного (и потому возвращавшего весь набор) фильтра.
BC-0706-7: отдельный код ошибки для слишком длинной команды exec
Поддержка старого формата до: 06.01.2027
Было
Команда длиннее 10 000 символов у POST /v1/infra/servers/:id/exec отклонялась общим кодом VALIDATION_ERROR без указания причины и выхода.
Стало
Такой запрос возвращает 400 с отдельным кодом COMMAND_TOO_LONG и структурированным hint: большие данные и скрипты передаются через POST /v1/infra/servers/:id/upload, затем выполняются bash /путь/скрипт.sh. Остальные нарушения схемы по-прежнему возвращают VALIDATION_ERROR.
Что делать интеграторам
Если ваш клиент обрабатывает VALIDATION_ERROR этого эндпоинта как общий случай ошибки валидации — добавьте обработку кода COMMAND_TOO_LONG (или обрабатывайте любой 400 единообразно).
NEW-0706-8: подсказка в ошибке таймаута exec
Ошибка EXEC_TIMEOUT у POST /v1/infra/servers/:id/exec теперь несёт структурированное поле hint (reason / recovery / recoveryAction): почему процесс был остановлен (по истечении timeout процесс-группа завершается принудительно, без grace-паузы) и что делать — запустить длинную операцию фоновой задачей и следить за ней через GET /v1/infra/servers/:id/logs, поднять timeout до 600 секунд или использовать режим ?stream=true. Поле аддитивное: прежний формат code / message не изменился, подсказка приходит и в JSON-режиме, и в SSE-событии error.
2026-07-05
FIX-0705-1: Отправка сообщения в чат — понятная ошибка при пустом тексте
Текст сообщения передаётся в поле message. Раньше вызов POST /v1/chats/{dialogId}/messages с текстом под неизвестным именем поля (например {"text": "hi"}) молча отбрасывал это поле, и Битрикс24 возвращал 422 BITRIX_ERROR о пустом сообщении — хотя контент был передан.
Было
{"text": "hi"} → 422 BITRIX_ERROR о пустом сообщении, без указания причины.
Стало
Тот же вызов сразу возвращает 400 MESSAGE_REQUIRED и перечисляет нераспознанные поля, подсказывая поле message. Пустой текст по-прежнему допустим вместе с блоком attach (сообщение только с вложением).
Влияние на интеграторов
Корректные вызовы с полем message не меняются. Ошибка при неверном имени поля стала точной.
FIX-0705-2: Ключ в заголовке Authorization: Bearer — понятная ошибка вместо INVALID_SESSION
API-ключ передаётся в заголовке X-Api-Key. Раньше, если ключ по ошибке клали в Authorization: Bearer (это место — для сессионного токена OAuth-приложения), сервер возвращал 401 INVALID_SESSION, и интегратор искал проблему в OAuth-сессии, хотя причина была в неверном заголовке.
Было
Ключ vibe_app_* в Authorization: Bearer → 401 INVALID_SESSION.
Стало
Тот же запрос возвращает 401 WRONG_AUTH_SCHEME с подсказкой: ключ OAuth-приложения (vibe_app_*) передаётся в X-Api-Key, а Authorization: Bearer несёт сессионный токен (vibe_session_*) из POST /v1/oauth/token; клиенту, который умеет только Bearer, подойдёт личный ключ (vibe_api_*). Сессионные токены и личные ключи в Bearer не затронуты.
Влияние на интеграторов
Корректные вызовы с ключом в X-Api-Key и сессией в Authorization: Bearer не меняются.
FIX-0705-3: multipart/create отклоняет XSS-опасные типы содержимого для PUBLIC-объектов
Было
Для PUBLIC-объектов типы содержимого text/html, application/javascript, application/x-javascript и image/svg+xml отклонялись при прямой и presigned-загрузке, но не при инициализации многочастевой (multipart) загрузки. Вызов POST /v1/storage/objects/multipart/create с visibility = PUBLIC и таким типом создавал сессию, и после завершения объект отдавался встроенно в браузере.
Стало
POST /v1/storage/objects/multipart/create с visibility = PUBLIC и XSS-опасным типом содержимого возвращает 415 STORAGE_FORBIDDEN_CONTENT_TYPE — так же, как прямая и presigned-загрузка. Многочастевая сессия при этом не открывается. PRIVATE-объекты по-прежнему допускают любой тип содержимого.
Влияние на интеграторов
Поведение приведено к задокументированному в разделе «Хранилище»: XSS-опасные типы содержимого недопустимы для PUBLIC-объектов на всех путях загрузки. Чтобы загрузить такой файл многочастевой загрузкой, используйте visibility = PRIVATE либо безопасный тип содержимого.
FIX-0705-4: привязка плейсмента на технический адрес сервера теперь ведёт через платформенный обработчик
Было
POST /v1/placements/bind принимал handler, указывающий на технический Black Hole-адрес приложения (app-*.vibecode…), и регистрировал его в Битрикс24 как есть. Битрикс24 отправлял iframe плейсмента напрямую на этот адрес, минуя платформу: сессия не выпускалась, и на сервере с доступом «только для пользователей Битрикс24» открытие плейсмента зацикливало авторизацию (на публичном сервере приложение отдавало собственную ошибку 404).
Стало
Такой handler автоматически переписывается на платформенный обработчик приложения (/v1/bitrix-handler) — плейсмент открывается и авторизуется штатно. В ответе появляются handlerRewritten: true и requestedHandler с исходным значением. Внешние (не Black Hole) обработчики не изменяются. Если платформенный обработчик приложения не удаётся определить, привязка отклоняется с кодом PLATFORM_HANDLER_UNRESOLVABLE вместо регистрации нерабочего адреса. Дополнительно GET /v1/placements помечает уже неправильно привязанный обработчик: data.handlers[].misbound: true плюс текстовый warnings[].
Кроме того, если плейсмент уже зарегистрирован в Битрикс24, но отсутствует в списке приложения (рассинхрон — например, после снятия с публикации без отвязки в Битрикс24), привязка больше не завершается ошибкой «Handler already binded»: платформа снимает устаревшую привязку и повторяет запрос один раз, восстанавливая рассинхрон. Если же привязка не проходит по другой причине (например, требуется коммерческий тариф Битрикс24), рабочий плейсмент не снимается.
NEW-0705-5: Новый код ошибки 402 ai_quota_exhausted на AI-эндпоинтах
При включённом контроле месячной AI-квоты портала запросы POST /v1/chat/completions, POST /v1/embeddings и POST /v1/audio/transcriptions могут вернуть 402 с телом { success: false, error: { code: "ai_quota_exhausted", type: "insufficient_quota", reason, resetAt? } }. Поле reason различает три случая: breaker — сработал часовой предохранитель расходов сверх квоты, wallet_empty — квота исчерпана и на балансе портала нет средств, wallet_off — расход сверх квоты для портала недоступен. resetAt — момент, когда запросы снова начнут проходить (для wallet_off при полном отключении может отсутствовать). Пока квота портала не исчерпана, поведение эндпоинтов не меняется.
2026-07-04
BC-0704-1: Перегрузочные отказы: 429/503 вместо 504
Поддержка старого формата до: 31.07.2026
Перегрузочные отказы сменили HTTP-статусы (коды в теле ответа НЕ изменились — меняется только статус). Правило: 429 — запрос НЕ был обработан, безопасно повторить через Retry-After (заголовок теперь ставится всегда); 503 — платформе или апстриму плохо, повторите позже, для write-операций сначала проверьте, применилось ли изменение. Прикладной 504 из API исключён.
Что изменилось: QUEUE_OVERFLOW 503→429; QUEUE_TIMEOUT 504→429; BITRIX_TIMEOUT (Bitrix24 не ответил за 15 секунд — для этого кода write мог примениться, перечитайте сущность перед повтором) →503; ai_provider_timeout 504→503; UPSTREAM_TIMEOUT (веб-поиск /v1/search — апстрим-провайдер не ответил вовремя) 504→503; RUNTIME_TIMEOUT / GATEWAY_TIMEOUT / WAKE_TIMEOUT 504→503.
NEW-0704-2: Новый код перегрузки AI: 429 ai_congested
AI-запросы к платформенному кластеру теперь проходят через admission-гейт: при перегрузке пула ответ — 429 с кодом ai_congested в теле и заголовком Retry-After. Повтор безопасен (запрос не выполнялся, списания нет). BYOK-ключи и внешние провайдеры гейтом не затрагиваются. По умолчанию гейт выключен — включается платформой.
BC-0704-3: ключи в режиме «только чтение» (READONLY) больше не выполняют запись на рукописных эндпоинтах
Поддержка старого формата до: 03.07.2026
Было
APP-ключ с режимом доступа «только чтение» (accessMode: READONLY) доходил до записи в Битрикс24 на части рукописных эндпоинтов (реквизиты и пресеты, пользовательские поля, timeline pin/note/bind, телефония, почта, диск, бизнес-процессы, приглашение и деактивация пользователя и другие) — гард проверял только скоуп, но не режим доступа ключа.
Стало
Любая попытка записи ключом в режиме «только чтение» возвращает 403 с кодом WRITE_BLOCKED_READONLY_KEY. Эндпоинты чтения не затронуты.
Что делать интеграторам
Если интеграция выполняла запись ключом «только чтение», переключите ключ в режим чтения и записи на странице управления ключами.
FIX-0704-4: деплой galaxy-приложения корректно распаковывает архивы с обёрткой и понятно сообщает о пустом source.content
Было
POST /v1/infra/servers/:id/deploy для galaxy-приложения (kind=GALAXY_APP), где файлы проекта в source.content лежали внутри обёрточной папки (типичный zip, собранный в macOS), собирал приложение с пустым контекстом сборки и падал в рантайме с npm error enoent Could not read package.json. Если же source.content вообще не распаковывался в файлы (передан versionId, путь или пустой архив) — деплой давал ту же непонятную ошибку сборки.
Стало
Такие архивы деплоятся корректно: файлы приложения оказываются в корне контекста сборки. А если source.content распаковался в пустой контекст, деплой сразу возвращает GALAXY_APP_BUILD_FAILED с понятным сообщением о пустом контексте сборки и подсказкой, что source.content должен быть base64-архивом (tar.gz или zip) файлов вашего проекта, а не versionId и не путём.
2026-07-03
FIX-0703-1: привязка размещения по ключу разработчика больше не возвращает 500 после цикла снятия и повторной публикации
Было
POST /v1/placements/bind для приложения, управляемого ключом разработчика (тип local.*), мог стабильно возвращать 500 (BITRIX_UNAVAILABLE, INTERNAL_SERVER_ERROR) при привязке размещения (например CRM_DEAL_DETAIL_TAB или CRM_CONTACT_DETAIL_TAB) после того, как приложение снимали с публикации и публиковали заново. Прежняя регистрация размещения на стороне Битрикс24 сохранялась, и попытка зарегистрировать поверх неё новую завершалась внутренней ошибкой. Повторные вызовы давали ту же ошибку.
Стало
Перед регистрацией размещения запрос сначала снимает его прежнюю регистрацию на стороне Битрикс24, поэтому привязка проходит успешно и после цикла снятия с публикации и повторной публикации. Менять вызов не нужно.
FIX-0703-2: /v1/sites — фильтр по типу «база знаний» (KNOWLEDGE) и «группа» (GROUP) больше не игнорируется
Было
POST /v1/sites/search (а также GET /v1/sites и POST /v1/sites/aggregate) с filter[type]=KNOWLEDGE или filter[type]=GROUP молча возвращал обычные сайты-лендинги (PAGE / STORE / VIBE) вместо баз знаний или страниц групп. Причина на стороне Битрикс24: метод landing.site.getList привязывает фильтр по TYPE к внутренней области (scope), и без параметра scope типы KNOWLEDGE / GROUP не входят в область по умолчанию — фильтр по типу тихо отбрасывался. Обойти можно было только вручную, добавив scope (см. Список сайтов).
Стало
Если в фильтре указан один такой тип и scope не передан явно, Вайбкод сам подставляет соответствующую область (type=KNOWLEDGE → scope=KNOWLEDGE, type=GROUP → scope=GROUP) — и запрос возвращает именно базы знаний / страницы групп. Явно переданный scope всегда в приоритете и не переопределяется. Если тип задан списком или оператором (например {"type":{"$in":["KNOWLEDGE","PAGE"]}}), где одну область выбрать нельзя, в meta.warnings приходит подсказка с кодом TYPE_REQUIRES_SCOPE.
Влияние на интеграторов
Действий не требуется. Запросы с filter[type]=PAGE / STORE / VIBE и запросы без фильтра по типу работают как раньше. MAINPAGE — это область, а не тип сайта (её сайты имеют тип VIBE), поэтому из фильтра по типу область MAINPAGE не выводится. Для /v1/pages поведение не изменилось.
FIX-0703-3: агрегация страниц и сайтов снова возвращает count
Было
POST /v1/pages/aggregate и POST /v1/sites/aggregate возвращали count: 0 и meta.totalRecords: 0 даже при наличии страниц и сайтов — во всех формах: без фильтра, с фильтром, с выражением count и как верхний count при groupBy. Счётчики отдельных групп при groupBy при этом были корректными.
Стало
count и meta.totalRecords отражают фактическое число записей; верхний count при groupBy равен сумме счётчиков групп.
Влияние на интеграторов
Действий не требуется — ответ стал корректным.
NEW-0703-4: Параметры качества и таймстампов в расшифровке аудио
Расшифровка аудио POST /v1/audio/transcriptions принимает пять новых необязательных полей. Качество распознавания: prompt — контекстная подсказка (тема разговора, стиль, правильное написание терминов, до 2000 символов), hotwords — список спец-слов через запятую (редкие термины, бренды, имена, до 500 символов), vad_filter — фильтр тишины перед распознаванием (меньше галлюцинаций на записях с паузами). Управление результатом: temperature — температура декодера от 0 до 1, timestamp_granularities[] — детализация таймстампов word/segment (только с response_format=verbose_json; со значением word каждый сегмент дополняется массивом words с таймингом и вероятностью каждого слова). Поля передаются в multipart/form-data рядом с file и совместимы с OpenAI-контрактом. Невалидные значения отклоняются кодами invalid_prompt, invalid_hotwords, invalid_temperature, invalid_vad_filter, invalid_timestamp_granularities.
FIX-0703-5: Приложения, созданные через API, корректно открываются как плейсменты
Было
Часть приложений, созданных через POST /v1/apps, не открывалась при вызове плейсмента в Битрикс24 — вместо интерфейса приложения пользователь видел ошибку распознавания приложения.
Стало
Созданные приложения корректно резолвятся и открываются как плейсмент-виджеты в Битрикс24. Ответ создания не изменился — приложение сразу пригодно для публикации и привязки плейсментов.
Влияние на интеграторов
Ничего менять не нужно. Ранее не открывавшиеся приложения нужно пересоздать (удалить и создать заново) — новое приложение открывается корректно.
NEW-0703-6: удаление ключа блокируется при привязанном агенте или боте
DELETE /v1/keys/:id теперь возвращает 409 с кодом KEY_HAS_LINKED_AGENT, если ключ является управляющим ключом живого AI-агента или управляемого бота.
Было
Удаление такого ключа осиротляло агента и каскадно удаляло бота вместе с его токеном — идентичность бота в Bitrix24 терялась безвозвратно, без предупреждения.
Стало
Тело ответа: { success: false, error: { code: "KEY_HAS_LINKED_AGENT", message, details: { linkedAgentCount, linkedBotCount, agents: [{ id, name, status }] } } }. Перед удалением перепривяжите ресурсы к другому ключу либо удалите сам агент/бот; для восстановления доступа осиротевшему агенту используйте кабинетное действие «Восстановить доступ». Проверка идёт до синхронизации с Bitrix24 — при 409 учётные данные на стороне Bitrix24 не затрагиваются. Сиблинг существующего KEY_HAS_ACTIVE_SERVERS.
NEW-0703-7: История изменений задачи и стадии канбана
Добавлены два метода только для чтения (скоуп task). GET /v1/tasks/:taskId/history возвращает историю изменений задачи целиком за один вызов: смены стадий канбана, перемещения в спринт и бэклог, статусы и другие события. Фильтр по типу события — ?field=STAGE (несколько типов через запятую, например ?field=STAGE,MOVE_TO_SPRINT); сортировка — ?order=asc или ?order=desc (по умолчанию по возрастанию даты создания). Каждая запись содержит id, createdDate, field, объект value с прежним и новым значением и user с идентификатором автора. GET /v1/tasks/stages/:entityId возвращает текущие колонки канбана рабочей группы (N) или личного плана (0).
FIX-0703-8: bizproc-activities: понятная ошибка вместо «Wrong handler URL» при отсутствии handler
Было
POST /v1/bizproc-activities без поля handler (или с URL обработчика, ошибочно переданным в поле handlerUrl) возвращал непрозрачную ядровую ошибку 422 BITRIX_ERROR: Wrong handler URL.
Стало
Поля code, name, handler проверяются до вызова Битрикс24: при отсутствии handler метод возвращает 400 MISSING_REQUIRED_FIELDS с сообщением Body field "handler" is required to create bizprocActivity, подсказывая правильное имя поля. Успешные вызовы с корректным полем handler не затронуты.
FIX-0703-9: POST /v1/batch — ответы update и delete теперь нормализованы, как create и get
Было
В глобальном пакетном вызове POST /v1/batch подвызов update возвращал ответ в сырой обёртке (вложенный объект вместо плоской записи), а подвызов delete возвращал пустой массив без признака успеха. Это расходилось с create и get в том же эндпоинте и с одиночным PATCH /v1/{entity}/:id, которые отдают плоскую нормализованную запись.
Стало
Подвызов update возвращает плоскую нормализованную запись (camelCase-поля) — как create, get и одиночный PATCH. Подвызов delete возвращает признак успеха вида { id, deleted: true }.
FIX-0703-10: POST /v1/{entity}/batch — create, update и delete снова работают для сущностей CRM
Было
По-сущностный пакетный вызов POST /v1/{entity}/batch с действием create, update или delete для сделок, контактов, компаний, лидов, предложений и счетов возвращал по каждому элементу ошибку «Could not find value for parameter {entityTypeId}», и запись не создавалась, не менялась и не удалялась. Одиночные вызовы (POST /v1/{entity}) и глобальный POST /v1/batch на тех же сущностях при этом работали.
Стало
По-сущностный пакетный create, update и delete для этих сущностей выполняется корректно — так же, как одиночные вызовы и глобальный пакетный эндпоинт.
FIX-0703-11: Пропуск поля model в чате снова подставляет модель по умолчанию
Было
POST /v1/chat/completions без поля model возвращал 400 no_default_model на порталах, где модель по умолчанию не была явно назначена, — даже когда на портале была доступная бесплатная модель. При этом GET /v1/me мог показывать в defaultModel модель, которую нельзя вызвать.
Стало
Если поле model не передано, запрос автоматически берёт первую доступную для вызова модель портала — как и описано в документации. GET /v1/me в поле defaultModel теперь всегда показывает вызываемую модель, ту же самую, которую подставит чат.
FIX-0703-12: include по связанным сущностям снова возвращает сами сущности, а не null
Было
GET /v1/deals/:id?include=contact,company возвращал _included.company = null, а _included.contacts — только метаданные связи (sort/isPrimary/roleId) без полей самой сущности, хотя сделка ссылалась на существующие компанию и контакты.
Стало
_included.company содержит полный объект компании, а _included.contacts — полные объекты контактов (id, name, …) вместе с метаданными связи. Исправление затрагивает include по всем CRM-сущностям (deals, leads, quotes и другим).
FIX-0703-13: поиск сделок отклоняет неизвестное поле фильтра вместо тихой отдачи всех строк
Было
GET /v1/deals и POST /v1/deals/search с неизвестным полем фильтра (опечатка в имени, поле не из схемы) молча пропускали его в Битрикс24, который игнорирует незнакомые ключи фильтра и возвращает весь набор сделок с ответом 200. Клиент, отправивший фильтр с ошибкой в имени поля, получал не пустой результат и не 400, а полную таблицу — как будто фильтр применился.
Стало
Неизвестное поле фильтра теперь отклоняется до вызова Битрикс24 ответом 400 с кодом UNKNOWN_FILTER_FIELD и списком доступных полей в сообщении — как уже делают contacts, companies, leads, quotes, invoices и items. Объявленные поля (включая псевдонимы вроде amount), пользовательские поля (UF_CRM_* и ufCrm*) и id работают как прежде.
FIX-0703-14: Каталог: список и поиск отдают чистый 400 при отсутствии iblockId
Было
Список и поиск GET /v1/catalog-products, POST /v1/catalog-products/search, GET /v1/catalog-sections и POST /v1/catalog-sections/search без iblockId в фильтре доходили до Битрикс24 и возвращали мутный 422 BITRIX_ERROR («Field iblockId is not specified in the filter»).
Стало
Каталожные список и поиск требуют iblockId в фильтре — при отсутствии сразу возвращается 400 MISSING_REQUIRED_FILTER с примером, запрос до Битрикс24 не доходит.
Влияние на интеграторов
Менять ничего не нужно — корректные запросы (с filter[iblockId]) работают как прежде. Изменились только код и ясность ошибки для запросов, которые и так не выполнялись.
FIX-0703-15: Контакты — реальные поля дат createdTime/updatedTime вместо фантомных createdAt/updatedAt
Было
GET /v1/contacts/fields объявлял поля createdAt и updatedAt, но в ответах контактов они никогда не появлялись — Битрикс24 отдаёт даты под ключами createdTime/updatedTime, и именно они были в теле контакта. При этом фильтр и select по createdTime (имя, которое клиент реально видит в ответе) отклонялись как неизвестное поле, а по фантомному createdAt — «работали», хотя само поле в ответе не читалось.
Стало
Схема объявляет реальные ключи createdTime и updatedTime (тип datetime, только чтение): они присутствуют в /fields, фильтр и select по ним работают, а значение нормализуется к ISO-8601 в UTC. Фантомные createdAt/updatedAt больше не объявлены — фильтр или select по ним возвращает 400 UNKNOWN_FILTER_FIELD.
Влияние на интеграторов
Чтение не меняется — ключи createdTime/updatedTime и раньше были в теле ответа, теперь ещё и нормализованы. Если вы фильтровали или проецировали контакты по createdAt/updatedAt, замените имена на createdTime/updatedTime.
FIX-0703-16: Список открытых линий теперь учитывает параметр limit
Было
GET /v1/openline-configs и одноимённый поиск игнорировали limit: нижележащий метод Битрикс24 возвращает весь набор конфигураций, а обёртка отдавала все строки. Поле hasMore при этом было неверным — false, даже когда за пределами запрошенного limit оставались ещё записи.
Стало
Ответ обрезается до limit на стороне обёртки. hasMore: true, когда Битрикс24 вернул больше записей, чем запрошенный limit (есть следующая страница), иначе false. total — число записей в текущем окне.
Влияние на интеграторов
Ответ на запрос с limit теперь содержит не больше limit записей. Клиенты, полагавшиеся на возврат всего набора без учёта limit, увидят усечённый список — используйте offset для следующей страницы.
2026-07-02
NEW-0702-1: фильтр и сортировка задач по реальному статусу (realStatus)
GET /v1/tasks, POST /v1/tasks/search и POST /v1/tasks/aggregate теперь принимают поле realStatus в filter (а список и поиск — ещё и в sort) — фильтрация по фактически сохранённому статусу задачи: 1 — новая, 2 — ждёт выполнения, 3 — выполняется, 4 — ожидает контроля, 5 — завершена, 6 — отложена, 7 — отклонена. Раньше filter[realStatus] молча игнорировался и запрос возвращал весь набор.
В отличие от filter[status], который на стороне Bitrix24 работает как виртуальный (мета-)фильтр (значения −1 просрочена, −2 не просмотрена, −3 почти просрочена) и не совпадает со значением поля status в ответе, realStatus фильтрует именно по хранимому статусу. Поле доступно только для чтения (статус меняется через status) и участвует только в filter/sort — в ответе реальный статус задачи уже отдаётся в поле status.
FIX-0702-2: создание приложения переиспользует упавший одноимённый слот
Было
Повторный POST /v1/infra/servers с тем же name после неудачного деплоя создавал новый слот приложения. Упавшие слоты накапливались и удалялись автоочисткой только через 7 дней.
Стало
Если у владельца ключа на портале уже есть слот с тем же name в статусе error (или созданный, но так и не получивший ни одного деплоя), повторный вызов возвращает этот же слот: его id сохраняется, ошибка и лог сборки сбрасываются, статус возвращается в provisioning — деплойте в него. Слоты, в которые ни разу не отправляли код, теперь удаляются автоочисткой через 24 часа вместо 7 дней (слоты с упавшей сборкой по-прежнему хранятся 7 дней вместе с логом сборки).
Влияние на интеграторов
Изменений в запросах не требуется. Если ваш сценарий пересоздавал слот с тем же именем после ошибки, вы начнёте получать прежний id вместо нового — это ожидаемо: деплой в возвращённый слот работает как обычно. Слоты других пользователей портала и работающие приложения под переиспользование не попадают.
FIX-0702-3: ключи «только чтение» больше не пишут через /v1/bots
Было
Ключ API в режиме «только чтение» (accessMode: READONLY) мог выполнять операции записи через эндпоинты бота (POST /v1/bots, отправка и удаление сообщений, добавление участников в чат, регистрация и удаление бота и другие) — вызов возвращал 200 вместо 403. Остальные проксирующие поверхности Битрикс24 такие записи уже блокировали.
Стало
Запись через /v1/bots/* ключом «только чтение» возвращает 403 с кодом WRITE_BLOCKED_READONLY_KEY. Операции чтения не затронуты, включая получение контекста сообщения (GET /v1/bots/:botId/messages/:messageId/context) и скачивание файла (GET /v1/bots/:botId/files/:fileId).
Влияние на интеграторов
Если бот-интеграции нужна запись — переключите ключ в режим «чтение и запись» в разделе /keys.
FIX-0702-4: include=storage у папок теперь резолвится
Было
GET /v1/folders/:id?include=storage (и список GET /v1/folders?parentId=...&include=storage) не добавляли _included в ответ, хотя GET /v1/folders/fields объявляет для связи storage признак includable: true.
Стало
Связанное хранилище резолвится: в ответе появляется _included.storage с карточкой хранилища, найденной по storageId. Связь описана в GET /v1/folders/fields.
FIX-0702-5: PAGE_BACKGROUND_WORKER: привязка больше не падает с 500
Было
POST /v1/placements/bind для placement PAGE_BACKGROUND_WORKER подставлял обязательный для Битрикс24 параметр options.errorHandlerUrl только когда вызов шёл через OAuth-сессию. Если приложение привязывалось по ключу разработчика или на коробочном портале, параметр не добавлялся и Битрикс24 отвечал 500 (BITRIX_UNAVAILABLE, «Field errorHandlerUrl is empty»), хотя остальные placement привязывались нормально.
Стало
Для PAGE_BACKGROUND_WORKER значение options.errorHandlerUrl по умолчанию подставляется равным handler независимо от способа привязки. Явно переданный options.errorHandlerUrl по-прежнему имеет приоритет. В ответе поле options теперь отражает применённое значение (с подставленным errorHandlerUrl).
Влияние на интеграторов
Действий не требуется — вызов, который раньше возвращал 500, теперь проходит.
FIX-0702-6: POST /search сообщает об отсутствии обязательных параметров чистой ошибкой
Было
POST /v1/{entity}/search для сущностей, чей метод списка Битрикс24 требует обязательные параметры, при их отсутствии не проверял этого и передавал запрос в Битрикс24 как есть. Наружу утекала сырая ошибка Битрикс24 (BITRIX_ERROR, например «Invalid value of parameter [ $id ]» или «Не задан обязательный параметр type»), тогда как у эквивалентного GET-списка та же ситуация давала понятный 400 MISSING_REQUIRED_PARAMS. Затрагивало POST /v1/calendar-events/search (нужен type), POST /v1/files/search (нужен folderId) и POST /v1/folders/search (нужен parentId).
Стало
POST /v1/{entity}/search проверяет обязательные параметры до вызова Битрикс24 — так же, как это давно делает GET-список. При отсутствии параметра приходит 400 с кодом MISSING_REQUIRED_PARAMS и перечнем недостающих полей, без обращения к Битрикс24. Обязательный параметр можно передать в filter, а параметр-родитель (folderId для файлов, parentId для папок) — также на верхнем уровне тела запроса.
NEW-0702-7: Цена research в самоописании ключа и сумма к пополнению в ответе 402
GET /v1/me теперь отдаёт cost у каждого провайдера в блоке webResearch.providers[] — по образцу блока webSearch. Поле несёт цену режима research в Ꝟ (cost.research) и валюту (cost.currency), так что агент видит стоимость глубокого поиска прямо в самоописании ключа, без отдельного вызова.
Ответ 402 при недостатке средств (INSUFFICIENT_BALANCE, а также BILLING_FROZEN) на POST /v1/search и POST /v1/research теперь содержит поле required — сумму в Ꝟ, необходимую для запроса. Прежние поля userMessage и hint не изменились.
Клиентам со строгой валидацией схемы по additionalProperties нужно учесть новые поля ответа.
NEW-0702-8: POST /v1/triggers/fire поддерживает счета (SmartInvoice)
Эндпоинт POST /v1/triggers/fire принимает новое значение entityType — invoice. Передайте entityType: "invoice" и entityId счёта (его выдаёт GET /v1/invoices), чтобы запустить триггер автоматизации по смарт-счёту. Прежние значения (deal, lead, contact, company, quote, item) работают как раньше.
Раньше запустить триггер по счёту было нельзя, а попытка через entityType="item" с entityTypeId=31 отклонялась сообщением, которое уводило в тупик. Теперь item с зарезервированным entityTypeId (в том числе 31) подсказывает перейти на соответствующий entityType — для счёта это invoice.
NEW-0702-9: GET /v1/ai/usage отдаёт длительность аудио по моделям транскрибации
GET /v1/ai/usage в блоке byModel[] теперь возвращает поле audioSeconds — суммарное количество секунд аудио, переданных на транскрибацию по каждой модели за выбранный период. Поле заполняется для вызовов speech-to-text (Whisper) и равно 0 для текстовых моделей, где длительность аудио неприменима.
Поле аддитивное, существующие интеграции продолжают работать без изменений.
BC-0702-10: categories: code и isDefault помечены read-only (были phantom-writable)
Поддержка старого формата до: 01.10.2026
Было
GET /v1/categories/:entityTypeId/fields объявлял code и isDefault записываемыми (readonly: false), но запись этих полей в crm.category.add/update молча игнорировалась (значения не сохранялись, ответ 200).
Стало
Оба поля помечены readonly: true. /fields теперь честно показывает их как read-only, а попытка записать code или isDefault возвращает 400 READONLY_FIELD вместо тихой потери данных.
Что делать интеграторам
Раньше передача code/isDefault в теле POST/PATCH /v1/categories/:entityTypeId принималась (200, значения молча игнорировались). Теперь такой запрос возвращает 400 READONLY_FIELD. Уберите code и isDefault из тела запросов create/update категорий — на запись эти поля больше не принимаются.
BC-0702-11: telephony-lines: поле crmAutoCreate нормализовано в boolean и появилось в /fields
Поддержка старого формата до: 01.10.2026
Было
GET /v1/telephony-lines/fields отдавал только number, serverName, name. Поле автосоздания CRM протекало в ответах списка сырым UPPER-именем CRM_AUTO_CREATE строкой "Y"/"N" — единственное UPPER-поле среди camelCase, и его не было в /fields. На запись camelCase crmAutoCreate молча отбрасывался.
Стало
Поле объявлено как crmAutoCreate (boolean). Теперь оно присутствует в /fields, в ответах list приходит нормализованным (true/false) вместо сырого "Y"/"N", а на create/update принимается camelCase boolean (сырое UPPER-имя ещё принимается на запись для совместимости). Клиенты, читавшие data[].CRM_AUTO_CREATE, должны перейти на data[].crmAutoCreate (boolean).
NEW-0702-12: workgroups: раскрыта операция aggregate и groupBy по полям
Было
POST /v1/workgroups/aggregate работал, но нигде не был заявлен: операции не было в машинном индексе /v1/guide, а groupBy возвращал 400 на любом поле (Available: .), потому что список агрегируемых полей был пуст.
Стало
Объявлен список агрегируемых полей: membersCount (числовые sum/avg/min/max) плюс категориальные active, isProject, ownerId для группировок. Теперь операция видна в /v1/guide и /fields, а groupBy по этим полям работает.
FIX-0702-13: /fields: полнота метаданных у doc-templates и bookings
Было
GET /v1/doc-templates/fields не содержал полей isDefault и productsTableVariant, хотя они приходят в ответах списка. У GET /v1/bookings/fields обязательные resourceIds и datePeriod не были помечены required, поэтому их обязательность не была видна в схеме.
Стало
doc-templates: объявлены isDefault и productsTableVariant (только чтение) — теперь состав /fields совпадает с ответами. bookings: resourceIds и datePeriod помечены required: true, обязательность видна в /fields.
FIX-0702-14: orders: /fields синхронизирован с ответом, убран псевдо-ключ order, companyId фильтруется
Было
GET /v1/orders/fields содержал лишний псевдо-ключ order (артефакт разбора sale.order.getFields) и не содержал полей, реально приходящих в ответах: companyId, clients, dateMarked, personTypeXmlId, statusXmlId, version. Из-за отсутствия companyId в схеме фильтр по нему падал с UNKNOWN_FILTER_FIELD.
Стало
Состав /fields теперь собирается из схемы: псевдо-ключ order убран, объявлены шесть недостающих полей (companyId — записываемое число; clients — объект, только чтение, приходит в get; dateMarked/personTypeXmlId/statusXmlId/version — только чтение). Фильтр и сортировка по companyId теперь работают.
FIX-0702-15: users: limit > 50 теперь работает, meta.hasMore честный
Было
GET /v1/users?limit=500 возвращал только 50 записей, хотя meta.total показывал больше. meta.hasMore всегда был false — документированная пагинация по hasMore молча теряла данные за первой страницей.
Стало
Сущность опирается на легаси-метод user.get (без суффикса .list), поэтому авто-паджинатор не включался. Добавлен флаг paginateViaStart (как у отделов): при limit > 50 идёт многостраничная загрузка через start, а meta.hasMore отражает реальное наличие следующих записей.
FIX-0702-16: POST /v1/batch отклоняет отключённые операции записи и прокидывает обязательные параметры списка
Было
Глобальный POST /v1/batch с действием create, update или delete для сущности, у которой эта операция отключена (например openline-configs — запись вынесена в отдельные роуты), выполнял вызов напрямую в Bitrix24 в обход нормализации и мог тихо создать или изменить запись. Отдельно: действие list или search для сущности с обязательными параметрами метода (например calendar-events — type и ownerId) возвращало AUTO_PAGINATION_FAILED «missing required parameter», хотя прямой запрос списка с теми же параметрами работал. Кроме того, batch-list для folders и files отправлял родительскую папку под именем parentId или folderId, которое метод disk.folder.getchildren игнорирует, поэтому список тихо возвращался не по той папке; а batch-list для calendar-events с лишним ключом filter молча прокидывал его в Bitrix24, и метод возвращал весь календарь без ошибки.
Стало
Отключённая операция записи в под-вызове отклоняется с кодом ACTION_NOT_SUPPORTED до обращения к Bitrix24 — так же, как в POST /v1/{entity}/batch. Обязательные параметры списка и параметры верхнего уровня метода прокидываются в Bitrix24 с исходными именами, поэтому batch-list работает так же, как прямой список, а при их отсутствии возвращается понятный MISSING_REQUIRED_PARAMS вместо сырой ошибки Bitrix24. Для folders и files родительская папка приводится к имени id, которого ждёт метод, поэтому batch-list возвращается по нужной папке. Для сущностей, у метода которых нет конверта filter (calendar-events), лишний ключ filter теперь отклоняется с кодом UNSUPPORTED_FILTER до обращения к Bitrix24 — так же, как в прямом списке.
Влияние на интеграторов
Ничего менять не нужно. Если под-вызов batch раньше опирался на выполнение отключённой операции записи — переведите его на выделенный роут сущности. Для batch-list по сущностям с обязательными параметрами (calendar-events) передавайте type и ownerId в params под-вызова. Если batch-list для calendar-events использовал filter — уберите его или перенесите в параметры верхнего уровня, иначе под-вызов вернёт UNSUPPORTED_FILTER.
NEW-0702-17: GET /:entity/fields отдаёт label и description полей
Ответ GET /v1/{entity}/fields теперь по каждому полю может нести человекочитаемое короткое имя label и пояснение description по-русски — раньше поле описывалось только парой {type, readonly}. Это позволяет ИИ-агенту и интерфейсу показывать название и назначение поля, не обращаясь к документации. Поля добавлены для сущностей: Отделы, Смарт-процессы, Хранилища, Папки, Файлы, Рабочие группы, Шаблоны документов, Бронирования, События календаря, Задачи, Реквизиты, Сотрудники. Для Сотрудников дополнительно исправлена подстановка подписей: GET /v1/users/fields теперь возвращает настоящие названия полей Битрикс24 из user.fields вместо технических кодов. Изменение аддитивное: новые ключи появляются дополнительно к прежним, существующие вызовы продолжают работать без изменений.
FIX-0702-18: ключ только со скоупами vibe:* теперь выписывается, а не падает
Было
Создание ключа авторизации POST /v1/keys, у которого все запрошенные права — внутренние права Вайбкода vibe:* (например только vibe:infra), на портале с режимом dev-key (коробочная Битрикс24 или подключённый облачный портал) отклонялось с 502 DEVKEY_MINT_FAILED. Права vibe:* не передаются в Битрикс24, поэтому набор прав для вебхука Битрикс24 оказывался пустым, и Битрикс24 отклонял выписку, требуя хотя бы одно право.
Стало
Ключ только с правами vibe:* теперь выписывается успешно. Вебхук Битрикс24 для него не создаётся — он не нужен, такой ключ не обращается к REST Битрикс24, — а ключ работает с внутренними возможностями Вайбкода по своим правам.
Влияние на интеграторов
Действий не требуется: прежде падавший запрос теперь возвращает созданный ключ.
FIX-0702-19: пагинация /v1/storages — limit больше 50 отдаёт все записи, meta.hasMore корректен
Было
GET /v1/storages с limit больше 50 возвращал максимум 50 записей, а meta.hasMore был всегда false — даже когда в портале записей больше. Клиент с ?limit=50 при 489 хранилищах видел hasMore: false и не знал, что нужно запросить следующую страницу. То же на POST /v1/storages/search и в /v1/batch.
Стало
limit больше 50 проходит авто-пагинацию (как у остальных списков) и отдаёт запрошенное число записей, а meta.hasMore равен (offset + число записей) < meta.total и для limit не больше 50. Изменение распространяется на GET /v1/storages, POST /v1/storages/search и список storages в /v1/batch.
Примечание: корректный расчёт meta.hasMore для ответов списка при limit не больше 50 теперь распространяется на все сущности (GET /v1/{entity} и POST /v1/{entity}/search), а не только на storages — ранее на этом пути meta.hasMore был всегда false.
FIX-0702-20: infra: кириллица в displayName при создании приложения
Было
При POST /v1/infra/servers с кириллическим displayName название могло сохраниться как последовательность знаков вопроса (??????) — повреждение кодировки при передаче в Битрикс24.
Стало
Название передаётся в кодировке UTF-8, кириллица сохраняется корректно.
Влияние на интеграторов
Действий не требуется. Кириллические названия больше не искажаются.
FIX-0702-21: items: фильтрация по полям связи parentId
Было
Фильтр по динамическому полю связи (например parentId2 — связанная сделка) на GET /v1/items/:entityTypeId и POST /v1/items/:entityTypeId/search отклонялся с 400 UNKNOWN_FILTER_FIELD, хотя поле присутствует в GET /v1/items/:entityTypeId/fields и возвращается в ответах.
Стало
Поля вида parentId<N> принимаются в фильтре и передаются в запрос как есть. Найти смарт-процесс, связанный с конкретной родительской сущностью, теперь можно напрямую через обёртку items.
Влияние на интеграторов
Действий не требуется. Запросы, ранее получавшие 400, теперь отрабатывают.
FIX-0702-22: смарт-процессы: сохранение списка значений и названия пользовательского поля
Было
На POST /v1/items/:entityTypeId/userfields поле-список (userTypeId: enumeration) создавалось, но варианты значений не сохранялись (список приходил пустым), а название поля, переданное строкой, в интерфейсе оставалось пустым.
Стало
Варианты значений принимаются как в enum, так и в list и корректно сохраняются. Название, переданное строкой, автоматически оборачивается в языковую карту и заполняет подписи в форме, колонке и фильтре.
Влияние на интеграторов
Действий не требуется. Ранее «молча терявшиеся» значения списка и название теперь сохраняются.
FIX-0702-23: req-family: /fields у адресов и preset-fields отдают ключи в camelCase
Было
GET /v1/addresses/fields и GET /v1/requisite-presets/:presetId/fields/schema возвращали описание полей с сырыми ключами в UPPER_SNAKE_CASE (TYPE_ID, ADDRESS_1, FIELD_NAME, IN_SHORT_LIST), хотя данные этих сущностей (GET /v1/addresses, список полей пресета) уже приходили в camelCase — схема не совпадала с реальными именами в данных.
Стало
Оба эндпоинта нормализуют ключи описания в camelCase (typeId, address1, fieldName, inShortList), как и весь остальной V1. Внутренние дескрипторы поля (type, isRequired, isReadOnly, title) не меняются.
Влияние на интеграторов
Ключи в ответе /fields теперь совпадают с именами полей в данных. Клиент, читавший camelCase-имена из данных, получает согласованную схему; действий не требуется.
2026-07-01
FIX-0701-1: Загрузка файла на Диск принимает файлы больше 1 МБ
Было
POST /v1/files/upload отклонял тело запроса больше ~1 МБ ошибкой FST_ERR_CTP_BODY_TOO_LARGE. Файл передаётся в base64 в JSON-теле, а base64 раздувает размер примерно на треть — поэтому даже файл 1,1 МБ не проходил. Способа загрузить файл крупнее не было.
Стало
Лимит тела этого маршрута поднят до 70 МБ — этого хватает на файл около 50 МБ с учётом base64 и JSON-обёртки (запись звонка, типовые вложения). Битрикс24 по-прежнему применяет собственное ограничение на размер файла Диска: при его превышении ошибка приходит в стандартном конверте. Для файлов в сотни МБ нужен отдельный способ загрузки (multipart / presigned) — он пока не реализован.
NEW-0701-2: иконка приложения сервера: загрузка SVG, анонимная отдача, фавикон
Появился способ задать иконку приложения для сервера. POST /v1/infra/servers/:id/icon (multipart/form-data, поле file, только SVG до 256 КБ, без скриптов, обработчиков событий и внешних ссылок) загружает иконку; она отдаётся анонимно по стабильному GET /api/server-icons/:id и показывается в каталоге приложений Bitrix24. Чтобы иконка стала фавиконом во вкладке браузера, впишите <link rel="icon" type="image/svg+xml" href="<базовый-URL>/api/server-icons/:id"> в HTML приложения во время сборки — после этого перезаливка иконки обновляет каталог и фавикон автоматически. Формат, требования и порядок — Иконка приложения.
NEW-0701-3: Профиль текущего пользователя сообщает права администратора
Ответ GET /v1/users/me теперь возвращает рабочее поле isAdmin: true — пользователь администратор портала, false — нет, null — определить не удалось (временный сбой; профиль при этом всё равно возвращается). Поле пригодно для серверной проверки прав в вашем бэкенде. На GET /v1/users/:id и в списке пользователей поле по-прежнему недоступно — вердикт отдаётся только для текущего пользователя сессии.
NEW-0701-4: Расширенный статический контракт полей в /v1/guide и указатель schema-discovery в /v1/me
По каждой сущности в ответе GET /v1/guide добавлено поле data.entities[].fieldsDetailed — расширенный статический контракт полей: type, readonly, required, createOnly и декодирование enum (например, значения status и priority у задач). Оно доступно по одному заголовку X-Api-Key, без сессии, и предназначено для маппингов и кодогенерации до появления пользовательской сессии. Компактное поле fields сохранено без изменений.
Ответ GET /v1/me для ключа авторизации без пользовательской сессии (без заголовка Authorization: Bearer) теперь содержит блок schemaDiscovery — указатель, где брать статическую схему без сессии (/v1/guide) и как получить живые и пользовательские поля (Bearer-сессия или персональный ключ). Эндпоинты GET /v1/<entity>/fields и GET /v1/userfields/* не изменились.
BC-0701-5: categoryId в ответе постов теперь массив чисел
Поддержка старого формата до: 01.01.2027
Было
GET /v1/posts отдавал categoryId с типом, зависящим от количества категорий поста: null без категорий, число (11) для одной, строка со списком ID через запятую ("5,7,9") для нескольких. Типизированный клиент с полем categoryId: number | null работал на постах с одной категорией, но ломался на постах с двумя и более.
Стало
categoryId — всегда массив чисел number[]: [] без категорий, [11] для одной, [5, 7, 9] для нескольких. Тип единый независимо от количества категорий.
Что делать интеграторам
Читайте categoryId как массив: post.categoryId.length вместо проверки на null, post.categoryId[0] для первой категории. Прежнюю ветку «число или строка» можно убрать.
FIX-0701-6: единые коды ошибок токена и скоупа в разделе Диска
Было
Пользовательские операции Диска — POST /v1/files/:id/moveto, copyto, POST /v1/files/upload, GET /v1/files/:id/download и аналоги для папок — при отсутствии токенов портала возвращали 401 NO_TOKENS, а при нехватке скоупа disk — 403 SCOPE_MISSING. Сгенерированные CRUD-операции того же раздела (list/get/create/update/delete) для тех же условий уже возвращали 401 TOKEN_MISSING и 403 SCOPE_DENIED — поэтому в пределах одного раздела клиент видел два разных кода для одной ошибки.
Стало
Все операции Диска возвращают единые коды — 401 TOKEN_MISSING и 403 SCOPE_DENIED, как и остальной V1 API. HTTP-статусы (401 и 403) не изменились.
Влияние на интеграторов
Если код ветвился по строкам NO_TOKENS или SCOPE_MISSING на операциях moveto/copyto/upload/download, переключитесь на TOKEN_MISSING / SCOPE_DENIED (или проверяйте HTTP-статус). Остальным менять ничего не нужно.
FIX-0701-7: placement CALL_CARD убран из списка допустимых
Было
Код placement CALL_CARD числился допустимым: POST /v1/placements/bind пропускал его через валидацию и передавал в Битрикс24, а GET /v1/placements/available выдавал его в списке. Но ни один модуль Битрикс24 не регистрирует этот placement, поэтому placement.bind завершался внутренней ошибкой, которую платформа отдавала как INTERNAL_SERVER_ERROR.
Стало
CALL_CARD удалён из списка допустимых: он больше не появляется в GET /v1/placements/available, а POST /v1/placements/bind с ним отклоняется сразу понятной ошибкой VALIDATION_ERROR, без обращения к Битрикс24.
Влияние на интеграторов
Привязка CALL_CARD не работала и раньше (возвращала непонятную ошибку 500), поэтому рабочих интеграций изменение не ломает. Для панели приложений в карточке звонка используйте актуальные placement'ы из GET /v1/placements/available.
FIX-0701-8: workday open/close/pause: поле userId теперь применяется
Было
Документированное поле тела userId в POST /v1/workday/open, POST /v1/workday/close и POST /v1/workday/pause молча игнорировалось: операция всегда выполнялась над владельцем токенов ключа, даже если был передан другой сотрудник. Ответ приходил success, но действие затрагивало не того пользователя.
Стало
userId транслируется в параметр Битрикс24 USER_ID, поэтому операция выполняется над указанным сотрудником (при наличии прав администратора или руководителя). Несуществующий userId теперь возвращает ошибку Битрикс24, а не мнимый успех. Некорректный userId (не положительное целое) отклоняется как 400 INVALID_PARAMS. Поведение совпадает с уже работавшим GET /v1/workday/status.
Влияние на интеграторов
Кто не передавал userId, изменений не заметит — операция по-прежнему применяется к владельцу токенов. Кто передавал userId, теперь получит корректное действие над указанным сотрудником.
2026-06-30
NEW-0630-1: POST /v1/cowork/deploy-key — получить проектный ключ для деплоя из Cowork/Code
Ключ Cowork/Code (vibe:cowork) работает только с data-plane и блокируется на control-plane инфраструктуры с 403 INFRA_FORBIDDEN_FOR_COWORK_KEY. Новый эндпоинт POST /v1/cowork/deploy-key позволяет агенту самому получить отдельный проектный ключ с правом деплоя: вызовите его этим же Cowork-ключом, возьмите поле key из ответа (тело — плоский объект, без обёртки data) и используйте его как заголовок X-Api-Key для деплоя/provision/exec под /v1/infra/*.
Возвращаемый ключ несёт скоупы vibe:infra + vibe:storage (без vibe:cowork), действует 7 дней и привязан к владельцу и порталу Cowork-ключа. На каждый вызов выдаётся свежий ключ, прежний проектный ключ при этом отзывается (активен всегда один). Требуется скоуп vibe:cowork и активная подписка Cowork/Code; коды отказа — 403 INSUFFICIENT_SCOPE / 403 COWORK_NOT_ACTIVATED / 503 DEPLOY_KEY_DISABLED / 503 INFRA_DISABLED.
NEW-0630-2: Self-hosted placement получает одноразовый код авторизации на appUrl
Для приложения с собственным appUrl (вне Black Hole), открытого как placement, платформа теперь добавляет к редиректу на appUrl одноразовый код авторизации (?code=...) вместо внутреннего gateway-токена. Приложение обменивает этот код на vibe_session существующим запросом POST /v1/oauth/token — redirect_uri должен точно совпадать с настроенным appUrl. Раньше такие приложения получали невостребуемый токен и не могли авторизовать пользователя.
NEW-0630-3: Новый эндпоинт POST /v1/oauth/placement-session для self-hosted приложений
Self-hosted приложение (на собственном сервере, не на Black Hole), открытое как placement (iframe) в Битрикс24, теперь может обменять токен пользователя Битрикс24, полученный в placement-колбэке на своём обработчике, на vibe_session. Запрос POST /v1/oauth/placement-session с телом { app_key, access_token, member_id, domain } (необязательно refresh_token, expires_in) идёт сервер-к-серверу — токен сессии не попадает в браузер. Раздел авторизации документации описывает обе топологии placement.
FIX-0630-4: PATCH права OAuth-app-ключа: честный отказ вместо ложной выдачи
Было
PATCH /v1/keys/:id с добавлением права Bitrix24 к ключу OAuth-приложения (vibe_app_*), как и PATCH /v1/apps/:id с расширением scopes приложения, возвращал 200 и сохранял новый набор прав. Но права OAuth-приложения фиксируются при выпуске и от такой правки на стороне Bitrix24 не меняются, поэтому GET /v1/me затем сообщал право, которого Bitrix24 не выдавал, а реальный вызов отклонялся.
Стало
Добавление права Bitrix24 к ключу OAuth-приложения или к приложению теперь отклоняется с 403 OAUTH_SCOPE_CHANGE_REQUIRES_REISSUE. Снятие прав и изменение vibe:*-прав работают по-прежнему. Чтобы получить новое право, выпустите новый ключ авторизации с нужным набором.
FIX-0630-5: ключ со скоупом `tasks` теперь реально открывает методы задач
Было
Ключ, выписанный со скоупом tasks (множественное число — его предлагает UI-пикер), на dev-key-портале минтил вебхук, который Bitrix24 принимал, но к REST-методам модуля Задач не привязывал. GET /v1/me рапортовал tasks, но вызовы методов задач отклонялись на стороне Bitrix24. Скоуп task (единственное число) работал.
Стало
При выписке и правке ключа набор прав канонизируется к написанию, которое Bitrix24 реально привязывает к методам (tasks → task), поэтому ключ открывает методы задач независимо от выбранного написания. На уже выписанные ключи изменение не распространяется ретроактивно — перевыпустите ключ.
FIX-0630-6: неверный формат FILES в комментарии таймлайна отклоняется с 400
Было
POST /v1/timelines и PATCH /v1/timelines/:id принимали поле FILES в любом виде и отвечали 200/201. Если форма отличалась от массива пар [[имяФайла, base64Содержимое]] — например плоский массив строк или одиночная пара без внешнего массива — комментарий создавался, но файл прикреплялся как мусорный (со случайным именем и нечитаемым содержимым) либо терялся молча, без признаков ошибки.
Стало
Поле FILES, переданное не в виде массива пар [[имяФайла, base64Содержимое]], отклоняется до обращения к Битрикс24 ошибкой 400 INVALID_FILES_SHAPE с подсказкой о правильной форме. Пустой FILES ([]) и отсутствие поля по-прежнему допустимы. Проверка действует на одиночных POST/PATCH и в батче (POST /v1/batch, POST /v1/timelines/batch).
Влияние на интеграторов
Кто передаёт FILES в задокументированной форме [[имяФайла, base64Содержимое]] — изменений нет. Кто полагался на другие формы — теперь получит явную 400 вместо молча испорченного вложения, и сможет исправить запрос.
FIX-0630-7: пустые поля ботов и сотрудников приходят как null/[], а не false/{}
Было
В ответах карточки бота (GET /v1/bots/:botId, POST /v1/bots, PATCH /v1/bots/:botId) незаполненные поля в users[] отдавались неверным примитивом: даты lastActivityDate, mobileLastDate, desktopLastDate приходили как булево false, а пустой список phones — тоже как false. То же поле lastActivityDate в GET /v1/users приходило как пустой объект {}. Из-за этого new Date(lastActivityDate) молча давал начало эпохи, а phones.map(...) падал с ошибкой типа.
Стало
Незаполненная дата на всех путях кодируется единообразно как null, а пустой список телефонов — как []. Заполненная дата по-прежнему приходит ISO-строкой, заполненный список — массивом.
Влияние на интеграторов
Менять ничего не нужно — типы стали корректными. Код, который опирался на сравнение с false для пустых значений, перестанет срабатывать: проверяйте дату на null, а список телефонов — как массив.
Затронутые эндпоинты: GET /v1/bots/:botId, POST /v1/bots, PATCH /v1/bots/:botId, GET /v1/users
NEW-0630-8: Пересвязка OAuth-credentials приложения без удаления
Новый эндпоинт POST /v1/apps/:id/relink-oauth обновляет bitrixClientId и bitrixClientSecret у существующего приложения, не удаляя его. Это нужно, когда локальное OAuth-приложение пересоздали на портале Битрикс24 и у него сменился client_id: раньше единственным путём было удалить приложение (что рвало связанные бот, openline и привязки) и создать заново.
Тело запроса: { bitrixClientId, bitrixClientSecret } (оба обязательны). Парный ключ, бот и привязки сохраняются. Если этот client_id уже привязан к другому приложению — 409 OAUTH_CLIENT_ID_IN_USE. Ключом самого OAuth-приложения вызвать нельзя — 403 OAUTH_APP_KEY_CANNOT_RELINK (нужен личный ключ или кабинет). После пересвязки переустановите приложение на портале — это восстановит подписку на события.
NEW-0630-9: Веб-поиск: полный текст страниц, изображения, режим новостей и фильтры по доменам в research
Было
POST /v1/search принимал include_raw_content как булев флаг, но полный текст найденных страниц в ответ не попадал. Не было параметров для режима новостей и для запроса изображений. POST /v1/research принимал include_domains и exclude_domains, но молча их отбрасывал.
Стало
POST /v1/search получил два новых необязательных параметра: topic (general или news, по умолчанию general) и include_images (булев, по умолчанию false). Булев include_raw_content теперь действительно возвращает полный текст: у каждого результата появилось поле rawContent (полный текст страницы, ограниченный по размеру). В ответе добавились верхнеуровневые images (массив объектов с полем url) и ignored_filters (массив строк — переданные фильтры, которые провайдер не смог применить). Эти поля приходят и в синхронном теле ответа, и в кадре done потоковой передачи. Заголовок X-Search-Filters-Ignored сохранён и теперь может перечислять topic и include_images. POST /v1/research теперь применяет include_domains и exclude_domains (до 20 каждый) у провайдеров с поддержкой и сообщает о превышении через ignored_filters в кадре done. Какие именно возможности доступны у выбранного движка — возвращает GET /v1/search/providers. Клиентам со строгой валидацией схемы по additionalProperties нужно учесть новые поля ответа.
NEW-0630-10: Чтение AI-расшифровок звонков клиентов через API
Новый эндпоинт GET /v1/activities/:activityId/transcript возвращает готовую AI-расшифровку звонка клиента по идентификатору CRM-активности «Звонок». Метод только читает уже готовую расшифровку — генерацию не запускает. Требует скоуп crm. Если расшифровки для звонка ещё нет, поле data.transcription равно null — это штатный ответ, а не ошибка.
2026-06-29
FIX-0629-1: поиск узлов оргструктуры теперь ищет по названию
Было
POST /v1/humanresources/nodes/search проксировал в humanresources.node.list: тип узла задавался внутри filter, поиска по названию не было вовсе, а тело { "type": ..., "name": ... } на верхнем уровне (или запрос без тела) возвращало 400 или 500.
Стало
Эндпоинт обёрнут на humanresources.node.search. Обязательны два поля на верхнем уровне тела — type (DEPARTMENT или TEAM) и name (подстрока названия). Необязательны parentId и pagination.limit (по умолчанию 50, максимум 200). Возвращаются узлы, чьё название содержит name, в плоском data с meta (total, hasMore). Поля filter, order и select больше не принимаются.
Влияние на интеграторов
Присылайте { "type": "TEAM", "name": "<подстрока>" } на верхнем уровне вместо прежнего { "filter": { "type": "TEAM" } }. Чтобы перечислить все узлы типа без поиска по названию, используйте GET /v1/humanresources/nodes с ?type=....
NEW-0629-2: Расписание выгодных часов AI-квоты (off-peak)
Добавлен эндпоинт GET /v1/off-peak — расписание скидок «выгодных часов» (Time-of-Use) для AI-квоты. Ответ содержит множитель цены прямо сейчас (currentMultiplier), ближайшее окно, когда станет дешевле (nextWindow), сетку 24×7 по часам и дням недели (grid), текущую ячейку сетки (nowCell) и часовой пояс расписания (timezone). Скидка применяется только к квотируемому расходу — в эти часы квота расходуется медленнее; оплата за токены по кошельку не затрагивается. Необязательный параметр model=<идентификатор> возвращает расписание конкретной модели вместо общего по умолчанию. Требуется скоуп vibe:ai. Пока выгодные часы не включены, ответ — { "enabled": false }.
BC-0629-3: удалён слаг провайдера vibe-search
Поддержка старого формата до: 26.12.2026
Было
Поле provider в POST /v1/search и POST /v1/research принимало слаг vibe-search — отдельный платформенный движок, добавленный 06.06.2026. Он также присутствовал в перечне слагов GET /v1/search/providers.
Стало
Слаг vibe-search удалён. Платформенный поисковый движок на всех инстансах называется bitrix-search — конкретный апстрим за ним зависит от инстанса. Запрос с provider: "vibe-search" теперь возвращает 400 INVALID_REQUEST (значение не проходит валидацию). Поддержка research для bitrix-search тоже зависит от инстанса — читайте GET /v1/search/providers.
Что делать интеграторам
Если в запросе явно передавался provider: "vibe-search", замените его на bitrix-search либо опустите поле provider, чтобы использовать движок по умолчанию инстанса (его показывает поле defaultProvider в GET /v1/me). Слаг vibe-search не был движком по умолчанию ни на одном проде, поэтому затронуты только интеграции, прописавшие его явно.
FIX-0629-4: Значение null в поле через POST /v1/batch больше не пишет в поле строку «null»
Было
В составном POST /v1/batch создание или обновление со значением поля null (например {"entity":"deals","action":"update","entityId":123,"params":{"comments":null}}) записывало в поле литеральную строку "null".
Стало
Поле получает пустое значение, которое Bitrix24 трактует по типу поля: текстовое — очищается, числовое — становится 0, датовое — остаётся без изменений. Литеральная строка "null" больше не пишется, ошибки не возникает. Это совпадает с поведением одиночного PATCH /v1/{entity}/:id с null. Постраничный путь /v1/{entity}/batch по-прежнему пропускает null-поле целиком (оставляет значение без изменений у всех типов).
FIX-0629-5: Поиск и список с фильтром по null теперь отдают больше 50 строк
Было
Запрос POST /v1/{entity}/search или GET /v1/{entity} с фильтром по пустому значению (например {"filter": {"closedDate": null}}) и limit больше 50 возвращал максимум 50 записей, хотя meta.total показывал реальное число совпадений и meta.hasMore был true. Автопагинация молча обрывалась после первой страницы, и типовой обход «читать, пока строк ровно limit» получал неполный результат без единой ошибки.
Стало
Такой запрос отдаёт до limit записей, как и с любым другим фильтром. Значение null в фильтре трактуется как «поле пусто» одинаково на всех страницах выборки.
Влияние на интеграторов
Клиентам, которые из-за обрыва листали вручную через offset шагом 50, ручной обход больше не нужен — можно запросить до 5000 записей одним вызовом.
FIX-0629-6: смена порта и автомаршрутизация деплоя работают на новых серверах-приложениях из коробки
Было
Обычный сервер «Опубликовать приложение» поднимался с фиксированным портом агента, поэтому PATCH /v1/infra/servers/:id/port и шаг автомаршрутизации деплоя отвечали 409 PORT_NOT_APPLIED (NO_SCANNER), а публичный URL отдавал служебную страницу Black Hole, пока сервис слушал не порт по умолчанию.
Стало
Новые обычные серверы-приложения поднимаются с авто-определением порта: сервис на любом порту доступен через туннель сразу, а смена порта и автомаршрутизация деплоя проходят успешно. Серверы агентов и galaxy-хостов поведение не меняют.
FIX-0629-7: установка приложения через /v1/apps возвращает понятный код ошибки вместо общего BOX_APP_INSTALL_FAILED
Было
При сбое установки OAuth-приложения POST /v1/apps всегда возвращал 502 BOX_APP_INSTALL_FAILED, а в error.message подставлялся сырой ответ Битрикс24 целиком.
Стало
Ответ при сбое классифицируется: 403 B24_INSUFFICIENT_SCOPE (служебная интеграция потеряла права на портале), 410 STALE_DEVELOPER_KEY (доступ изменили или удалили — восстановить автоматически нельзя), 502 RECOVERY_FAILED (временный сбой, можно повторить) или 502 DEVKEY_MINT_FAILED (прочее). error.message больше не содержит сырой ответ Битрикс24 — диагностика переехала в очищенное поле error.details.b24Body.
Влияние на интеграторов
Обработка «ответ не 201 — установка не удалась» продолжает работать без изменений. Если код различал именно BOX_APP_INSTALL_FAILED, добавьте обработку новых кодов выше.
FIX-0629-8: поиск по широкому диапазону дат больше не возвращает пусто
Было
POST /v1/deals/search (и аналогично для leads, contacts, companies, quotes, invoices, items) с фильтром по дате и нижней границей (>= / >) за период шире 14 дней возвращал 200 с пустым data и meta.total: 0, хотя записи за этот период существовали.
Стало
Такой запрос возвращает все совпадающие записи. Поведение GET /v1/deals, узких диапазонов (≤ 14 дней) и параметра autoWindow: false не менялось.
NEW-0629-9: Новый код ошибки CONNECTOR_APP_INSTALL_FORBIDDEN при установке приложения
POST /v1/apps на коробочном портале теперь возвращает 403 с кодом CONNECTOR_APP_INSTALL_FORBIDDEN, когда администратор портала Битрикс24 запретил пользователю устанавливать приложения. Поле error.message содержит понятное локализованное объяснение с подсказкой обратиться к администратору портала. Прежде такой отказ отдавался как общий 502 CONNECTOR_APP_INSTALL_FAILED без объяснения причины; этот код по-прежнему используется для прочих сбоев установки.
NEW-0629-10: Перенос владения ботом на другой ключ
Добавлен эндпоинт POST /v1/bots/:botId/transfer — переносит владение ботом на другой API-ключ того же аккаунта Битрикс24 и того же пользователя (или администратора аккаунта). Решает ситуацию, когда бот «осиротел» после пересоздания приложения: ключ-владелец отозван, и рантайм бота переставал работать. Тело: { "targetApiKeyId": "<id>" }. Целевой ключ должен быть активен, в том же аккаунте, с областью imbot. После переноса проверьте B24-привязку нового ключа через POST /v1/bots/:botId/reauth.
2026-06-28
FIX-0628-1: редактирование scopes ключа применяется к вебхуку Битрикс24
Было
PATCH /v1/keys/:id со списком scopes сохранял новый набор в Вайбкоде, но на self-hosted (коробочных) порталах Битрикс24 не переносил его на вебхук портала. Вебхук оставался со старым набором scopes, и вызов метода из только что добавленного scope отклонялся Битрикс24 (403), хотя по данным Вайбкода ключ этот scope уже имел.
Стало
Изменение scopes теперь применяется к вебхуку Битрикс24 в том же запросе. Если синхронизацию выполнить не удалось, ключ не обновляется (Вайбкод и Битрикс24 остаются на прежнем наборе), а ответ несёт код ошибки: INVALID_SCOPES (400) — портал не выдаёт один из запрошенных scope, STALE_DEVELOPER_KEY (410) или RECOVERY_FAILED (502) — ключ доступа портала недействителен, BOX_NO_DEVELOPER_KEY (400) — у владельца ключа нет ключа доступа портала, DEVKEY_SCOPE_SYNC_FAILED (502) — прочий отказ Битрикс24.
Влияние на интеграторов
Менять ничего не нужно — scopes, добавленные через PATCH /v1/keys/:id, теперь работают сразу. Если в ответ пришёл один из кодов выше, набор scopes остался прежним: устраните причину и повторите запрос.
2026-06-27
NEW-0627-1: Свойства товаров каталога — чтение и управление схемой свойств
Добавлен раздел /v1/catalog-product-properties — определения пользовательских свойств торгового каталога (идентификатор, название, тип). Поддерживаются список, получение, создание, изменение, удаление, поиск и справочник полей. Свойства-списки товара приходят в /v1/catalog-products полями вида propertyNNN, где NNN — идентификатор свойства. Новый раздел сопоставляет этот идентификатор с названием и типом свойства. Фильтр filter[iblockId] ограничивает выборку одним каталогом, идентификатор берётся из /v1/catalogs. Требуется скоуп catalog.
2026-06-26
FIX-0626-1: catalog-prices: системные поля priceScale, extraId, timestampX объявлены в схеме
Было
GET /v1/catalog-prices и GET /v1/catalog-prices/:id возвращали поля priceScale, extraId и timestampX, но они не были объявлены в схеме: проходили без нормализации (поле timestampX приходило в формате со смещением, например 2024-06-17T16:53:24+03:00) и отсутствовали в ответе GET /v1/catalog-prices/fields.
Стало
Три поля объявлены как доступные только для чтения. Теперь они перечислены в GET /v1/catalog-prices/fields, а timestampX нормализуется в ISO 8601 UTC (2024-06-17T13:53:24.000Z) — единообразно с остальными полями типа datetime.
Влияние на интеграторов
Момент времени в timestampX не меняется — меняется только его строковое представление (UTC вместо локального смещения). Клиенты, разбирающие значение стандартным парсером дат, продолжают работать без изменений.
FIX-0626-2: Скачивание исходников сервера и приложения: подписанный URL больше не отдаёт 403
Было
GET /v1/infra/servers/:id/sources/:versionId/download и GET /v1/apps/:id/sources/:versionId/download возвращали 200 с подписанным URL, но скачивание по этому URL падало с 403 AccessDenied, если запрос шёл под личным ключом (vibe_api_*). Листинг версий при этом работал, и файл физически присутствовал в хранилище.
Стало
Подписанный URL теперь привязан к фактическому расположению объекта в хранилище, поэтому скачивание возвращает содержимое архива. Исправление покрывает и снапшоты, у которых ключ хранения принадлежит другому семейству (legacy-снапшоты приложения с привязкой к серверу).
Влияние на интеграторов
Контракт эндпоинтов не меняется — это восстановление задокументированного поведения «200 + рабочий подписанный URL». Никаких изменений на стороне клиента не требуется.
NEW-0626-3: Обновление api-bearer токена и причина отказа Gateway
Новый эндпоинт POST /v1/infra/servers/:id/access-tokens/:tokenId/refresh выпускает свежий JWT (до 10 минут) для уже существующего токена режима api-bearer — без создания новой записи, не расходуя лимит активных токенов и лимит выпусков в час. Долгоживущий клиент (CI, AI-агент) обновляет токен перед истечением jwtExpiresAt вместо повторного выпуска. Один отзыв гасит и исходный, и все обновлённые JWT одного токена.
Ответ 401 BH_LOGIN_REQUIRED, который Gateway возвращает на субдомене приложения при отклонении заголовка Authorization: Bearer, теперь содержит поле reason с конкретной причиной: expired, signature, subdomain, type, revoked, malformed или invalid. Поле аддитивное — прежние клиенты его игнорируют.
2026-06-25
FIX-0625-1: нейтральные идентификаторы провайдера, плана и региона в инфраструктурном API
Было
На международной (.com) поверхности GET /v1/infra/providers, GET /v1/infra/providers/:id/plans и GET /v1/infra/providers/:id/regions отдавали идентификаторы провайдера, планов, типа диска и регионов в сыром виде нижележащей инфраструктуры, а не в нейтральном пространстве имён бренда.
Стало
Те же поля приведены к нейтральному пространству имён бренда Bitrix Cloud, как в российском сегменте: провайдер bitrix-cloud, планы bc-small/bc-medium/bc-large/bc-xlarge, diskType: "network-ssd", регионы bc-eu-central/bc-eu-west/bc-us-east/bc-us-west/bc-ap-southeast. Поля name и country остаются человекочитаемыми (например, «Frankfurt (EU Central)», DE). Те же значения возвращаются в полях provider/plan/region ответов GET /v1/infra/servers и GET /v1/infra/servers/:id.
Влияние на интеграторов
Стандартный сценарий не требует изменений: идентификатор, полученный из каталога, по-прежнему передаётся в POST /v1/infra/servers без правок. При создании сервера принимаются и новые идентификаторы (bitrix-cloud/bc-small/bc-eu-central), и идентификаторы из прежнего каталога, поэтому существующие интеграции продолжают работать. Поправьте только код, который сверяет идентификаторы ответа с захардкоженными строками из прежнего каталога провайдера, планов или регионов.
FIX-0625-2: offset внутри подвызовов /v1/batch теперь листает страницы
Было
В POST /v1/batch подвызовы list и search молча игнорировали offset в params: B24 получал неизвестный ключ offset вместо start, поэтому каждая страница возвращала один и тот же первый набор записей. Три подвызова contacts.search с offset 0, 50 и 100 отдавали идентичную первую страницу.
Стало
offset в подвызове list/search переводится в start Bitrix24 (как в одиночном POST /v1/{entity}/search). Те же три подвызова теперь возвращают три разные непересекающиеся страницы. Поведение одиночного эндпоинта не изменилось.
FIX-0625-3: Файлы и папки: deletedBy у не удалённых объектов теперь null
Было
GET /v1/files/:id, GET /v1/files и эндпоинты папок возвращали deletedBy: 0 у не удалённого объекта, хотя поле объявлено как number | null с «null — объект не удалён». Поле-сосед deletedAt при этом корректно отдавало null, так что два поля с одинаковым контрактом вели себя по-разному, и проверка deletedBy !== null ошибочно считала каждый активный объект удалённым.
Стало
deletedBy нормализуется в null для не удалённых объектов (Битрикс24 хранит в колонке DELETED_BY ноль как «нет пользователя»; пользователя с id 0 не существует). У удалённых объектов поле по-прежнему содержит id пользователя, выполнившего удаление.
Влияние на интеграторов
Поведение приведено к задокументированному контракту number | null. Клиенты, проверявшие deletedBy === null / deletedBy !== null, теперь получают корректный результат для активных объектов.
NEW-0625-4: чтение Базы знаний 2.0 — список баз, документы, дерево, поиск
Read-доступ к Базе знаний 2.0: список доступных баз знаний (курсорная пагинация), получение базы знаний и документа по идентификатору (документ — с Markdown-содержимым), дерево документов базы знаний и полнотекстовый поиск по документам. Скоуп note.
Затронутые эндпоинты: GET /v1/note/collections (список), GET /v1/note/collections/:id (одна база), GET /v1/note/collections/:collectionId/documents (дерево), GET /v1/note/documents/:id (документ с Markdown), GET /v1/note/documents/search (поиск по query).
FIX-0625-5: методы Базы знаний 2.0 (создание, изменение, загрузка файлов) теперь работают
Было
Создание и изменение баз знаний и документов (POST /v1/note/collections, PATCH /v1/note/collections/:id, POST /v1/note/documents, PATCH /v1/note/documents/:id) возвращали 400 с ошибкой валидации Битрикс24, а загрузка вложения (POST /v1/note/documents/:documentId/files) сохраняла файл, но не возвращала его идентификатор в data.id.
Стало
Методы работают: создание и изменение возвращают 200, а ответы создания баз знаний, документов и файлов содержат идентификатор в data.id. Архивирование, удаление и получение файла работали и раньше.
FIX-0625-6: /v1/me: supportedVisibilities хранилища теперь в верхнем регистре
Было
GET /v1/me в блоке storage отдавал supportedVisibilities: ["private","public"] в нижнем регистре, а эндпоинты загрузки принимают только PRIVATE/PUBLIC (верхний регистр). Агент, скопировавший значение из манифеста, получал STORAGE_INVALID_VISIBILITY.
Стало
supportedVisibilities отдаётся как ["PRIVATE","PUBLIC"] — ровно те значения, которые принимает параметр visibility при загрузке.
Влияние на интеграторов
Если клиент брал значение visibility из /v1/me и приводил его к верхнему регистру сам — ничего не меняется. Если передавал как есть — теперь загрузка проходит без ошибки.
2026-06-24
FIX-0624-1: логи galaxy-приложения отдают вывод контейнера
Было
GET /v1/infra/servers/:id/logs для galaxy-приложения (kind=GALAXY_APP) возвращал только системный журнал хоста (journalctl), а не логи самого контейнера приложения — увидеть stdout/stderr упавшего приложения было нельзя.
Стало
Для galaxy-приложения эндпоинт читает stdout/stderr контейнера (docker logs). Чтение read-only: если хост-галактика спит или недоступен, ответ — пустой data.logs плюс data.hint, хост не будится. Параметр since для galaxy-приложений принимает только длительность (10m) или метку RFC3339 — человекочитаемые формы journalctl («1 hour ago») допустимы лишь для Black Hole-серверов.
NEW-0624-2: код ошибки GALAXY_APP_START_FAILED при деплое
POST /v1/infra/servers/:id/deploy для galaxy-приложения возвращает 502 GALAXY_APP_START_FAILED, когда приложение успешно собралось, но упало или ушло в OOM-перезапуск сразу после старта. Это отдельный код от GALAXY_APP_BUILD_FAILED (ошибка сборки): по нему видно, что сборка прошла, а проблема в рантайме (например, превышение лимита памяти). Хвост логов контейнера приходит в поле buildLog.
FIX-0624-3: окно блокирующего пробуждения сервера увеличено до ~5 минут
Было
Блокирующее пробуждение — POST /v1/infra/servers/:id/wake с ?wait=true и автопробуждение спящего сервера при POST /v1/infra/servers/:id/deploy — ждало готовности (статус RUNNING плюс подключённый туннель) примерно до 3 минут, после чего возвращало 504 WAKE_TIMEOUT.
Стало
Основная фаза ожидания увеличена с ~3 до ~5 минут, а с учётом фазы перезагрузки полный потолок до 504 — около 6.5 минуты. Глубоко «остывший» хост (например, спавшая несколько дней галактика) успевает загрузиться и подключиться, а не получает ложный таймаут. Код ошибки, форма ответа и потолок со стороны прокси прежние.
Влияние на интеграторов
Если клиент задаёт собственный таймаут на эти вызовы, заложите около 6.5 минуты вместо 3. Прочее поведение прежнее, переписывать интеграцию не нужно.
NEW-0624-4: параметры placement и graduateFrom при создании сервера
POST /v1/infra/servers получил два необязательных параметра. placement — auto (по умолчанию, поведение прежнее) или dedicated: на портале с моделью размещения galaxies-only значение dedicated создаёт не приложение Galaxy, а отдельную виртуальную машину, проходя те же проверки, что и обычное создание сервера — политику serverCreation и квоту серверов на пользователя. graduateFrom принимает идентификатор вашего приложения Galaxy (kind=GALAXY_APP): после создания выделенного сервера это приложение удаляется. graduateFrom ограничен владельцем — чужой или не-Galaxy идентификатор вернёт 404, ничего не удаляя.
Это аддитивно: без placement или с placement: "auto" запрос ведёт себя в точности как раньше.
Кроме того, при OOM-падении приложения Galaxy ответ POST /v1/infra/servers/:id/deploy с кодом 502 GALAXY_APP_START_FAILED теперь содержит структурную подсказку error.hint с recoveryAction: "graduate-to-dedicated-vm" — как пересоздать приложение на выделенном сервере через placement: "dedicated" и graduateFrom. Подсказка добавляется только когда причина падения — OOM (превышение лимита памяти контейнера), а не обычный краш.
FIX-0624-5: создание сервера с кодом в source.content принимает архивы до 500 МБ
Было
POST /v1/infra/servers с inline-архивом в source.content (одношаговое создание приложения Galaxy) возвращал 413 FST_ERR_CTP_BODY_TOO_LARGE уже на архивах больше ~750 КБ, хотя в документации поля source.content заявлен лимит 500 МБ на тело запроса. Маршрут наследовал глобальный лимит тела 1 МБ.
Стало
Маршрут принимает тело до 500 МБ — как и POST /v1/infra/servers/:id/deploy и POST /v1/infra/servers/:id/upload. Лимит из документации теперь действует на самом деле.
NEW-0624-6: группировка реквизитов по ИНН/ОГРН/КПП в aggregate
POST /v1/requisites/aggregate теперь принимает groupBy по строковым идентификаторам реквизита: rqInn, rqKpp, rqOgrn, rqOgrnip, rqOkpo, rqVatId, rqResidenceCountry, rqCompanyName, а также presetId, entityTypeId, active. Раньше группировка по этим полям возвращала 400 INVALID_PARAMS с пустым списком доступных полей.
Группировка по rqInn — самый быстрый способ найти дубли реквизитов одним вызовом: группы с count > 1 содержат повторяющиеся ИНН. Прежние вызовы (groupBy по entityTypeId/presetId) работают без изменений. Числовые функции (sum/avg/min/max) по этим строковым полям по-прежнему недоступны — они только для группировки.
FIX-0624-7: availableActions спящего сервера показывает wake/start; repair-status сразу `running`
Было
Для спящего сервера без туннеля (SLEEPING + blackholeStatus: DISCONNECTED — обычное состояние остановленного сервера) поле availableActions в ответе 422 SERVER_WRONG_STATE и в GET /v1/me (infra.unhealthyServers) содержало только ["repair","delete"] — без очевидного способа поднять сервер. Отдельно: сразу после POST /v1/infra/servers/:id/repair опрос repair-status в первые миллисекунды мог вернуть {status:"idle"}, и цикл опроса завершался преждевременно.
Стало
availableActions для любого спящего сервера теперь содержит ["wake","start","repair","delete"] — оба действия реально принимаются эндпоинтами /wake и /start. А repair-status выставляет running синхронно при старте ремонта, поэтому первый же опрос видит running, а не idle. Прежние вызовы продолжают работать без изменений.
FIX-0624-8: stage-history?entityType=invoice теперь отдаёт историю смарт-счёта (31)
Было
GET /v1/stage-history?entityType=invoice возвращал историю упразднённого старого счёта (entityTypeId 5, status-based: statusId/statusSemanticId). Текущий смарт-счёт (31) был доступен только под ключом entityType=new-invoice. Клиент, работающий со счетами через /v1/invoices (тип 31), запросив историю под invoice, получал чужой упразднённый тип.
Стало
entityType=invoice отдаёт историю текущего смарт-счёта (entityTypeId 31, stage-based: stageId/stageSemanticId/categoryId) — в одном ряду с /v1/invoices. Ключ new-invoice сохранён как алиас на 31 для обратной совместимости, ломать ничего не нужно.
NEW-0624-9: эндпоинт эмбеддингов bitrix/embeddings
Появился OpenAI-совместимый эндпоинт POST /v1/embeddings — преобразование текста в векторные представления (эмбеддинги) для семантического поиска, кластеризации, дедупликации и поиска похожих карточек CRM. Модель bitrix/embeddings бесплатная и платформенная, свой ключ провайдера не требуется. В поле input принимается строка или массив строк, ответ возвращается в сыром OpenAI-формате: поле object со значением list, массив data с объектами вида { object: "embedding", embedding, index } и блок usage. Поддерживаются необязательные параметры encoding_format (float или base64) и dimensions. Список доступных моделей и их возможностей — GET /v1/models, у модели эмбеддингов выставлена возможность embeddings.
FIX-0624-10: типы полей календарных событий приведены к реальным ответам
Было
GET /v1/calendar-events/fields объявлял rrule как string, а dateCreate и updatedAt — как datetime, хотя на чтение rrule приходит объектом, а dateCreate и updatedAt — строкой в формате региональных настроек портала (не ISO 8601). В схеме числилось поле ownerType, которого в ответах нет. В объекте rrule приходили служебные ключи ~UNTIL и UNTIL_TS, а в элементах списка повторяющихся событий — служебный ключ RINDEX.
Стало
/fields объявляет rrule как object, а dateCreate и updatedAt — как string. Фантомное поле ownerType убрано из схемы. Служебные ключи ~UNTIL, UNTIL_TS и RINDEX больше не попадают в ответы.
Влияние на интеграторов
Документированные поля не изменились — клиент, читавший только их, продолжает работать. Для абсолютной метки времени используйте from и to (ISO 8601). Значения dateCreate и updatedAt не разбирайте фиксированным парсером — их формат зависит от региональных настроек портала.
NEW-0624-11: поле provisionReason в ответе серверов
GET /v1/infra/servers и GET /v1/infra/servers/:id теперь возвращают поле provisionReason со значением oom, crash или null — структурный признак причины ошибки galaxy-приложения. Раньше его отдавали только сессионные роуты кабинета, и в Vibecode API приходилось разбирать свободный текст provisionError. Значение oom — сигнал к «выпуску» приложения на выделенный сервер: создайте сервер с параметрами placement равным dedicated и graduateFrom. Поле необязательное и аддитивное — прежние интеграции работают без изменений.
FIX-0624-12: graduation-сигнал срабатывает при любой нехватке памяти galaxy-приложения
Было
Приложение Galaxy, которому не хватило памяти контейнера — и упёршееся в лимит с перезапусками у предела, и исчерпавшее память сразу на старте (например, грузит большую модель), — классифицировалось как обычный крэш: GET /v1/infra/servers/:id возвращал provisionReason crash, а ответ POST /v1/infra/servers/:id/deploy с 502 GALAXY_APP_START_FAILED шёл без graduation-подсказки. И наоборот, не-OOM краш-луп (необработанное исключение) мог ошибочно помечаться oom.
Стало
Реальная нехватка памяти в любой форме — и мгновенная на старте, и постепенный рост до лимита — надёжно даёт provisionReason oom и error.hint с recoveryAction graduate-to-dedicated-vm. Сборочные ошибки и приложения, которые вообще не стартовали (битая команда запуска), остаются crash без graduation.
Влияние на интеграторов
Менять ничего не нужно: значение поля и подсказка теперь точнее отражают нехватку памяти. Агент может надёжно ловить provisionReason oom для любого исчерпания памяти и «выпускать» приложение на выделенный сервер.
2026-06-23
NEW-0623-10: подсказка по настройке push-доставки в ошибках подписки на события
Ответы об ошибке 400 NOT_OAUTH_APP и 400 NO_USER_TOKEN у POST /v1/infra/servers/:id/event-subscriptions теперь содержат поле error.hint — текстовую инструкцию, как получить сервер под ключом авторизации (vibe_app_) для push-доставки: создать ключ авторизации через POST /v1/apps, авторизовать приложение на портале, создать новый сервер под этим ключом. Отдельной «миграции» существующего сервера с обычного ключа нет. Поле аддитивное — прежние клиенты не затронуты.
FIX-0623-1: список действий бизнес-процессов
Было
GET /v1/bizproc-activities возвращал каждый код действия как объект с числовыми ключами по символам — например, {"0":"D","1":"i", …} вместо строки "DiskRead". Проверка Array.includes(code) не работала.
Стало
Эндпоинт возвращает коды действий массивом строк, как и задокументировано.
FIX-0623-2: ключи ответа поиска дубликатов в camelCase
Было
POST /v1/duplicates/find возвращал ключи объекта data в верхнем регистре (LEAD, CONTACT, COMPANY), в отличие от остального API в camelCase.
Стало
Ключи приходят в camelCase (lead, contact, company); значения (массивы идентификаторов) не меняются.
FIX-0623-3: поле files эпика Scrum массивом идентификаторов
Было
GET /v1/scrum/epics/:id отдавал поле files сырым UF-объектом Битрикс24 (с VALUE_RAW, USER_TYPE_ID и прочими внутренними метаданными).
Стало
files — массив идентификаторов вложений ([417]) или пустой массив, в едином стиле с остальным API.
BC-0623-4: создание ключа авторизации только для администраторов
Поддержка старого формата до: не предусмотрена, ограничение действует сразу
Было
Создать ключ авторизации (POST /v1/keys) мог любой пользователь портала.
Стало
Создание ключа доступно только администраторам портала, остальным запрос отклоняется.
Что делать интеграторам
Создавайте ключи под учётной записью с правами администратора Битрикс24.
FIX-0623-5: заголовок Retry-After при ограничении частоты
Было
При ответе 429 (превышение лимита частоты) заголовок Retry-After не возвращался, и интегратор не знал, через сколько повторить запрос.
Стало
Ответ 429 несёт Retry-After с интервалом в секундах. Используйте его как паузу перед повтором.
FIX-0623-6: удалённый сервер снова отдаёт 404
Было
GET /v1/infra/servers/:id для мягко удалённого сервера возвращал 200 с полным телом и status: "deleted", хотя документация обещает 404. Клиент, опрашивающий эндпоинт и ожидающий 404 как подтверждение удаления, его не получал.
Стало
Эндпоинт возвращает 404 NOT_FOUND для удалённого сервера — так же, как список и удаление, и как описано в документации.
Влияние на интеграторов
Если ваш код полагался на 200 с status: "deleted", переключитесь на проверку 404 (либо на отсутствие сервера в списке) как на признак удаления.
NEW-0623-7: Универсальные списки — полный REST API
Появился раздел Списки (scope lists): программное управление универсальными списками Битрикс24 — самими списками, их полями, разделами и элементами. Это 24 эндпоинта под /v1/lists поверх методов lists.*.
Список адресуется типом инфоблока (iblockTypeId — lists, lists_socnet или bitrix_processes, по умолчанию lists) и идентификатором: числовой сегмент пути трактуется как IBLOCK_ID, строковый — как символьный IBLOCK_CODE. Поля, разделы и элементы доступны вложенными путями.
Если модуль «Универсальные списки» не подключён на портале, вызов возвращает 409 LISTS_MODULE_NOT_ENABLED — это признак выключенного модуля, а не ошибка интеграции.
Затронутые эндпоинты: /v1/lists, /v1/lists/:iblockId, /v1/lists/:iblockId/fields, /v1/lists/:iblockId/sections, /v1/lists/:iblockId/elements
FIX-0623-8: флаг isChildrenListEnabled у связей смарт-процессов
Было
Вложенный флаг связи isChildrenListEnabled принимался только как true/false. Значение Y/N, как у остальных флагов смарт-процесса, молча сохранялось как выключенное.
Стало
POST /v1/smart-processes и PATCH /v1/smart-processes/:entityTypeId приводят Y/N (а также 1/0, yes/no) к true/false для isChildrenListEnabled в связях.
FIX-0623-9: фильтр и выбор пользовательских полей сделок
Было
При фильтрации и выборе пользовательских (UF) полей сделки в форме UF_CRM_* поле отклонялось с UNKNOWN_FILTER_FIELD в фильтре и молча пропускалось из select.
Стало
Пользовательские поля сделок указываются в camelCase (ufCrmCheckOut) и работают без изменений в фильтре и выборе.
Затронутые эндпоинты: GET /v1/deals, POST /v1/deals/search
2026-06-22
NEW-0622-1: связь сделки с контактами
Управление набором контактов сделки: чтение, добавление, замена всего набора, удаление. PUT заменяет весь набор разом. Скоуп crm.
Затронутые эндпоинты: GET/POST/PUT/DELETE /v1/deals/:id/contacts — Контакты сделки
BC-0622-2: список моделей содержит только GA-модели
Поддержка старого формата до: не предусмотрена, экспериментальные модели не входили в стабильный контракт
Было
GET /v1/models и список моделей в /v1/me включали экспериментальные не-GA модели.
Стало
Публичный список содержит только GA-модели. Экспериментальные исключены из списка и отклоняются при вызове.
Что делать интеграторам
Берите модель из актуального ответа GET /v1/models, не зашивайте идентификаторы экспериментальных моделей.
FIX-0622-3: авто-пагинация подразделений
Было
GET /v1/departments возвращал только первую страницу при limit > 50.
Стало
Авто-пагинация собирает все подразделения в один ответ.
2026-06-19
FIX-0619-1: создание документа
Было
POST /v1/documents возвращал 422 и не создавал документ.
Стало
Эндпоинт создаёт документ из шаблона и возвращает запись.
FIX-0619-2: авто-пагинация складов
Было
GET /v1/warehouses и остатки по складу возвращали только первую страницу при limit > 50.
Стало
Авто-пагинация собирает все записи в один ответ.
Затронутые эндпоинты: GET /v1/warehouses, GET /v1/warehouses/:id/stock
FIX-0619-3: сохранение значений списочных пользовательских полей
Было
При создании и обновлении пользовательского поля типа «список» значения списка терялись.
Стало
Значения списка сохраняются при создании и обновлении.
Затронутые эндпоинты: создание, обновление пользовательского поля
FIX-0619-4: частичное обновление позиции корзины
Было
PATCH /v1/basket-items/:id не выполнял частичное обновление позиции.
Стало
Частичное обновление работает, в теле обязательно поле quantity.
FIX-0619-5: фильтр и сортировка настроек открытых линий
Было
У настроек открытых линий фильтр и сортировка работали не для всех полей, а значения при записи не нормализовались.
Стало
Фильтр и сортировка учитывают схему полей, булевы значения при записи приводятся к формату Битрикс24 (Y/N).
2026-06-18
NEW-0618-1: группировка в агрегации сделок
POST /v1/deals/aggregate принимает groupBy: "stageSemanticId" — разбивка по семантике стадии (в работе, успех, провал) для аналитики воронки.
NEW-0618-2: пагинация и фильтр истории стадий
GET /v1/stage-history поддерживает пагинацию (meta.total, meta.hasMore) и фильтр по типу сущности entityTypeId.
FIX-0618-3: учёт времени задачи не переназначает автора
Было
PATCH /v1/tasks/:taskId/time/:id принимал поле userId, но Битрикс24 не переназначает автора записи — значение молча игнорировалось.
Стало
Поле userId отклоняется с 400 — автора записи учёта времени сменить нельзя.
Влияние на интеграторов
Не передавайте userId при обновлении записи учёта времени.
FIX-0618-4: таймзона события календаря
Было
PATCH /v1/calendar-events/:id мог сохранять время в таймзоне пользователя Битрикс24, а не самого события.
Стало
Таймзона события сохраняется при обновлении.
2026-06-17
NEW-0617-1: База знаний 2.0 (note.*)
Коллекции, документы и вложения базы знаний: создание, изменение, архивирование и удаление баз знаний и документов, загрузка вложений. Скоуп note.
Затронутые эндпоинты (методы записи): POST /v1/note/collections, POST /v1/note/documents, POST /v1/note/documents/:documentId/files, а также парные PATCH и DELETE. Методы чтения добавлены отдельной записью.
2026-06-16
NEW-0616-1: AI follow-up завершённых звонков
AI follow-up по завершённым звонкам. Скоуп call.
В процессе раскатки — методы выходят в обновлении Битрикс24 call 26.600.0 и доступны не на всех порталах. Пока обновление не приехало на портал, метод возвращает 422 METHOD_NOT_YET_AVAILABLE с целевой версией в ответе — это признак раскатки, а не ошибка интеграции.
Затронутые эндпоинты: POST /v1/calls/followups/list, GET /v1/calls/followups/:callId
FIX-0616-2: формат ответа транскрипции
Было
POST /v1/audio/transcriptions всегда возвращал JSON-объект, даже при response_format=text, srt или vtt.
Стало
text, srt, vtt отдают сырое тело в формате text/plain, SubRip или WebVTT. json и verbose_json отдают JSON-объект.
2026-06-12
BC-0612-1: поле payed заказа только для чтения
Поддержка старого формата до: не предусмотрена, поле стало read-only
Было
payed принимался в теле создания и обновления заказа.
Стало
payed доступно только для чтения — при записи отклоняется.
Что делать интеграторам
Уберите payed из тела POST /v1/orders и PATCH /v1/orders/:id.
Затронутые эндпоинты: POST /v1/orders, PATCH /v1/orders/:id
2026-06-11
NEW-0611-1: Scrum API
Эпики, привязка задач к эпикам и чтение чата задачи. Скоуп tasks.
Затронутые эндпоинты: /v1/scrum/epics, /v1/scrum/epics/:id, /v1/scrum/tasks/:taskId — раздел Scrum
NEW-0611-2: оценка звонка при завершении
POST /v1/calls/:callId/finish принимает оценку завершённого звонка и передаёт её в Битрикс24.