Для AI-агентов: markdown этой страницы — /docs-content/changelog/2026-09-17.md индекс документации — /llms.txt
Изменения API: 17 сентября 2026
← Журнал изменений · Сентябрь 2026
NEW-0917-2: у сервера появился режим работы: `runMode` и `workSchedule` в чтениях
GET /v1/infra/servers и GET /v1/infra/servers/{id} отдают два новых поля.
runMode — IDLE, SCHEDULE или ALWAYS. Он выводится из уже публичных полей: назначенного
расписания, createdVia и sleepAfterMinutes. По одному sleepAfterMinutes эти состояния было не
различить: у машины, которая просто не засыпает, и у агента, которому засыпать запрещено, там
одинаково null. У агента и бота runMode всегда ALWAYS — число в sleepAfterMinutes на это не
влияет.
workSchedule — null, пока сервер не работает по расписанию. Иначе объект:
{
"id": "…",
"name": "Смены склада",
"presetKey": null,
"timezone": "Europe/Moscow",
"windows": [
{ "isoDay": 1, "start": "08:00", "end": "13:00" },
{ "isoDay": 1, "start": "14:00", "end": "20:00" }
]
}
isoDay — 1…7 от понедельника, start и end — местное время расписания в виде ЧЧ:ММ. Окна
приходят списком, а не парой полей «начало/конец»: в одном дне их может быть два — например, смена с
перерывом. У готового расписания платформы name пустое, а опознаётся оно по presetKey.
Поля добавочные: прежние запросы и разбор ответов продолжают работать без изменений.
FIX-0917-4: удалённое приложение показывает понятное состояние места показа
Было
Оставшееся в Битрикс24 место показа локально удалённого приложения открывало сырой ответ 401 APP_RESOLVE_FAILED.
Стало
POST /v1/bitrix-handler возвращает 410 Gone с локализованной страницей о недоступности приложения и ссылкой назад в Битрикс24. Ответ содержит Cache-Control: no-store, Pragma: no-cache и Referrer-Policy: no-referrer.
Влияние на интеграторов
Изменения интеграции не требуются. Пользователь видит конечное состояние вместо ошибки авторизации.
FIX-0917-5: внешнее API приложений возвращает 503 при перегрузке
Было
При высокой нагрузке новые запросы внешнего API приложений не получали специальный ответ о временной недоступности.
Стало
При временной перегрузке внешний API приложений отклоняет новый запрос с HTTP 503 APP_API_UNAVAILABLE и заголовком Retry-After. Форматы успешных ответов приложений не изменились.
Затронутые эндпоинты: GET, POST, PUT, PATCH, DELETE, HEAD /v1/applications/:id/api/*.
Влияние на интеграторов
Изменения не требуются. После HTTP 503 APP_API_UNAVAILABLE клиент может повторить запрос через время, указанное в Retry-After.
FIX-0917-6: предупреждения успешной установки рантайма теперь видны в ответе деплоя
Было
Шаг runtime возвращал status: "ok" и длительность, когда установка завершалась успешно, и всё, что
установщик рантайма напечатал по ходу дела, до вызывающей стороны не доезжало — ни в обычном ответе, ни
в потоке ?stream=true. Установщик при этом мог сообщить, что поставил рантайм не задуманным путём:
например, один из источников пакетов он отложил в сторону, чтобы apt оставался читаемым, или взял
рантайм из запасного источника. Деплой был зелёный, узнать об этом можно было только зайдя на сервер.
Стало
Если установка прошла успешно, но установщик сообщил о себе, шаг runtime возвращает
status: "warning", текст сообщений лежит в stdout этого шага, а в warnings[] ответа появляется
строка-указатель на него. Сам ответ остаётся успешным: success: true и HTTP 200 не меняются, поле
duration шага на месте, форма ответа прежняя. Когда установщику сказать нечего, шаг по-прежнему
возвращает status: "ok" без stdout.
FIX-0917-8: создание папки через глобальный POST /v1/batch больше не отклоняется Битрикс24
Было
Под-вызов { "entity": "folders", "action": "create" } в POST /v1/batch отправлял поля папки одним конвертом fields[...], тогда как метод Битрикс24 ждёт родительскую папку верхним параметром id, а остальные поля — под data[...]. Каждое такое создание возвращалось с ошибкой ERROR_ARGUMENT, хотя одиночный POST /v1/folders и пер-сущностный POST /v1/folders/batch с тем же телом работали.
Стало
Глобальный batch собирает создание папки в той же форме, что и одиночный роут, поэтому под-вызов действительно создаёт папку. Пропущенный parentId отклоняется кодом MISSING_PARENT_ID до обращения к Битрикс24 — тем же, что и на одиночном роуте, вместо сырой ошибки портала.
Влияние на интеграторов
Менять ничего не нужно. Если вы обходили дефект одиночными вызовами создания папки, теперь папки можно создавать пачкой в одном batch.
FIX-0917-9: список записей учёта времени отвечает 404 на отсутствующую задачу
Было
GET /v1/tasks/:taskId/time на задачу, которой нет или которая недоступна ключу, отвечал 422 BITRIX_ERROR, а в поле message приезжал внутренний текст исключения Битрикс24 вида TASKS_ERROR_EXCEPTION_#1; Task not found or not accessible; 1/TE/TASK_NOT_FOUND_OR_NOT_ACCESSIBLE. Соседние маршруты этого же раздела на такой же случай уже отвечали 404.
Стало
Тот же запрос отвечает 404 с кодом TASK_NOT_FOUND и коротким сообщением Task not found or not accessible.; внутренний текст исключения наружу больше не уходит. Успешный ответ не изменился: задача на месте — по-прежнему 200 с тем же телом. Прочие ошибки чтения, включая отбой по лимиту запросов, сохраняют прежние статусы.
Влияние на интеграторов
Менять код не требуется. Если обработчик различал «задачи нет» по статусу 422 или по тексту сообщения, переключите его на 404 и код TASK_NOT_FOUND.
FIX-0917-10: отказ «на портале нет учёта рабочего времени» получил собственный код
Было
Состояние портала, при котором учёт рабочего времени недоступен, приезжало в операции раздела «Рабочий день» двумя разными ответами, и ни один не называл причину. Если модуль «Рабочее время» не установлен или не входит в тариф, Битрикс24 отвечает «Метод не найден» на любой метод timeman.* — и клиент получал 404 ENTITY_NOT_FOUND, то есть сообщение о ненайденной сущности, хотя сама сущность была в порядке и недоступен был метод. Этого кода не было ни в одной таблице ошибок раздела. Если же модуль установлен, но учёт рабочего времени выключен в настройках портала, тот же по сути отказ приезжал общим 422 BITRIX_ERROR с текстом Битрикс24, то есть был неотличим от ошибки данных.
Стало
Оба состояния отвечают 409 TIMEMAN_MODULE_NOT_ENABLED — код, который раздел уже использовал в GET /v1/workday/records. Сообщение называет обе возможные причины и действие: попросить администратора портала включить учёт рабочего времени. Ответ одинаков на всех операциях раздела, поэтому совет из документации истории рабочих дней — «позовите текущий статус, тот же отказ означает выключенный учёт» — теперь работает буквально. Такой отказ больше не учитывается защитой от циклов ошибок, то есть остаётся понятным при любом числе повторов, а не подменяется на 429 ERROR_LOOP_DETECTED.
Влияние на интеграторов
Действий не требуется: отказ как был, так и остаётся ответом класса 4xx. Интеграция, которая различала это состояние по тексту сообщения Битрикс24, может перейти на код 409 TIMEMAN_MODULE_NOT_ENABLED. Прежние 404 ENTITY_NOT_FOUND и 422 BITRIX_ERROR документацией раздела для этого состояния не обещались.
Затронутые эндпоинты: POST /v1/workday/open, POST /v1/workday/close, POST /v1/workday/pause, GET /v1/workday/status, GET /v1/workday/settings, GET /v1/workday/schedule, GET /v1/workday/records
BC-0917-11: команда в галактике без статуса выхода больше не считается успешной
Поддержка старого формата до: не предусмотрена
Было
POST /v1/infra/servers/:id/exec на приложении в галактике (kind: "GALAXY_APP"): если хост отвечал, но статус выхода не присылал, вызов возвращал success: true и exitCode: -1. Завершение при этом утверждалось там, где платформа его не наблюдала, а команда, действительно вышедшая с кодом -1, была от этого случая неотличима.
{ "success": true, "data": { "exitCode": -1, "stdout": "", "stderr": "", "duration": 0, "truncated": false } }
Стало
Тот же случай возвращает EXEC_NO_EXIT с success: false — так же, как это давно работает на отдельной виртуальной машине. Ответ остаётся HTTP 200. Полей exitCode, duration и truncated в нём нет: их значения платформе неизвестны, а накопленный до обрыва вывод приходит в data.
{ "success": false, "error": { "code": "EXEC_NO_EXIT", "message": "Exec stream ended without an exit status from the agent" }, "data": { "stdout": "", "stderr": "" } }
Что делать интеграторам
Окно поддержки прежнего формата не предусмотрено: он утверждал исход, которого не было, и сохранять его означало бы и дальше отдавать ложный успех.
Обработайте код EXEC_NO_EXIT там, где вызываете команду на приложении в галактике: случай «поток кончился без статуса выхода» теперь приходит именно так, а не успехом с exitCode: -1.
⚠️ Это не делает exitCode: -1 в ответе однозначным во всех случаях. Остаётся ещё одна ситуация, в которой приходит -1: агент прислал терминальный чанк, но кода в нём не назвал. Она встречается редко, форму ответа этим изменением не меняет и от настоящего выхода с кодом -1 в теле ответа по-прежнему неотличима. Различает их только запись операции: там код сохраняется, лишь когда агент его назвал, иначе приходит null. Поэтому если вам важно отличить «вышла с -1» от «кода не назвали», читайте исход по идентификатору через GET /v1/infra/operations/:operationId, а не по полю ответа.
BC-0917-12: пустое значение параметра в списке операций больше не подменяется умолчанием
Поддержка старого формата до: не предусмотрена
Было
GET /v1/infra/servers/:id/operations с пустым значением параметра — ?limit= — отвечал 200 и отдавал ленту с умолчанием, как будто параметр не передавали вовсе.
GET /v1/infra/servers/srv_123/operations?limit=
→ 200, 5 последних строк
Стало
Тот же вызов отвечает 400 INVALID_LIMIT. Пустое значение — это переданное значение, а не опущенный параметр: оно рождается из шаблона с незаполненной переменной, то есть сузить выдачу хотели и не сумели. Умолчания limit=5 и kind=deploy действуют только тогда, когда ключа в строке запроса нет вовсе.
{ "success": false, "error": { "code": "INVALID_LIMIT", "message": "limit must be a non-negative safe integer" } }
Что делать интеграторам
Окно поддержки прежнего поведения не предусмотрено: оно отдавало набор строк, за которым вызывающий не приходил, и на канале восстановления это читается как «записей нет».
Проверьте места, где строка запроса собирается шаблоном. Если значение может оказаться пустым, не подставляйте ключ вовсе — тогда действует умолчание. Тот же разбор применяется к ?kind=, но этот параметр в том же выпуске и появился, поэтому прежнего поведения у него не было.
NEW-0917-13: исход команды на сервере читается после обрыва ожидания
У команды POST /v1/infra/servers/:id/exec появился устойчивый идентификатор запуска, по которому исход читается отдельным запросом. Раньше исход существовал только внутри текущего ответа, поэтому транспортный таймаут или разрыв соединения теряли его навсегда, и узнать, выполнилась команда или нет, было нечем. Повтор при этом небезопасен: транспортный таймаут прекращает только ожидание на стороне клиента, а команда на сервере может доработать до конца уже после того, как клиент получил ошибку.
Идентификатор приходит полем operationId в теле (data.operationId при успехе, error.operationId при отказе) и заголовком ответа X-Vibe-Operation-Id. На отдельной виртуальной машине заголовок уходит до начала выполнения команды, поэтому доживает до клиента даже когда тело потеряно, а при ?stream=true то же значение приходит первым кадром event: operation. У приложения в галактике раннего канала нет: та ветка не открывает поток и отдаёт заголовок только с терминальным ответом, поэтому при обрыве ожидания идентификатора не будет — такой запуск ищется в списке операций сервера.
Исход читается через GET /v1/infra/operations/:operationId и хранится 7 суток. Поле kind в ответе этой ручки теперь принимает значение exec наряду с deploy, а у команды приходит exitCode — код возврата, если агент его подтвердил, иначе null. Статус succeeded у команды означает, что она отработала до конца, и ненулевой код возврата приходит с этим же статусом. Статус failed ставится, только когда платформа может доказать, что команда не запускалась, например при EXEC_BUSY. Пока команда идёт, статус — running. Во всех остальных случаях обрыва приходит unknown: команда может продолжать выполняться на сервере, поэтому повторять изменяющую команду по этому статусу нельзя без сверки.
В списке GET /v1/infra/servers/:id/operations появился параметр kind со значением deploy по умолчанию. Без параметра приходит тот же набор строк, что и раньше, — только выкладки, и каждая строка при этом дополнительно несёт поле kind. kind=exec показывает команды, kind=all — смешанную ленту. Недопустимое значение отвечает 400 INVALID_KIND.
Запись несёт исход попытки — статус, код возврата и причину отказа. Вывод команды в ней не сохраняется: если он нужен после обрыва, запускайте команду фоновой задачей и читайте журнал через GET /v1/infra/servers/:id/logs.
FIX-0917-14: у занятого сервера машинное поле восстановления называет ручку с проверкой
Было
При 409 EXEC_BUSY на отдельной виртуальной машине (kind: "STANDALONE") поле error.hint.recoveryAction называло DELETE /v1/infra/servers/:id/lock — вызов, который снимает замок безусловно, не проверяя, идёт ли операция. Поле это единственное в подсказке, по которому клиент действует, не читая прозу, поэтому автоматический вызов мог прервать живую выкладку.
{ "error": { "code": "EXEC_BUSY", "hint": { "recoveryAction": "DELETE /v1/infra/servers/:id/lock" } } }
Стало
Там же приходит POST /v1/infra/servers/:id/unstick — он сам отвечает 409 OPERATION_IN_PROGRESS, пока замок ещё наблюдается — и когда операция доказанно жива, и когда доказать её смерть нечем, — а снимает замок сам только если это exec-мьютекс, переживший собственный срок; когда признаков замка нет вовсе, отказывать нечему и вызов проходит, и дополнительно перетряхивает туннель агента. Снятие замка из контракта не исчезло: оно осталось в тексте error.hint.recovery вторым шагом. Право на него даёт только независимо подтверждённый простой — состояние сервера и логи. Отсутствие error.hint.holder таким подтверждением не является: чтение держателя fail-open, поэтому «держателя нет» приходит и тогда, когда прочитать его не удалось.
{ "error": { "code": "EXEC_BUSY", "hint": { "recoveryAction": "POST /v1/infra/servers/:id/unstick" } } }
Заодно текст подсказки перестал предлагать проверять «нет ли идущей операции» по списку операций. Список отдаёт только запуски вашего же ключа, поэтому операция, начатая другим ключом того же портала, в нём не видна, и пустой список ничего не доказывал. Признак держателя приходит в том же ответе — error.hint.holder.
Влияние на интеграторов
Клиент, который исполняет recoveryAction как есть, менять ничего не должен — он начнёт вызывать защищённую ручку сам. Если вызов DELETE /lock записан у вас в коде постоянной строкой, перечитайте error.hint.recovery: там сказано, при каком условии этот шаг уместен.
FIX-0917-15: приложения с интерфейсом больше не создаются в режиме «только API»
Было
POST /v1/apps отвечал 201 и возвращал mobile: true, но платформа Вайбкод не сообщала порталу режим установки, и портал подставлял свой умолчательный — «только API». Приложение с интерфейсом сохранялось без интерфейса: REST отвечал, а самого приложения не было ни в меню портала, ни в мобильном клиенте.
Стало
Режим передаётся явно. Приложение с интерфейсом устанавливается как приложение с интерфейсом; приложение без пунктов меню по-прежнему устанавливается как «только API».
Влияние на интеграторов
Ответ 201 и его состав не меняются, действий не требуется. Приложение, созданное раньше, платформа не переделывает — снимите режим «только API» в карточке приложения на портале либо создайте приложение заново.
BC-0917-16: создание приложения Коворк/Код: живое платное место и проверка скоупов
Поддержка старого формата до: не предусмотрена
Было
Аккаунт без доступа к платформе получал на POST /v1/cowork/applications отказ ещё до обращения к Битрикс24, даже когда у пришедшего было живое платное место Коворк/Код: 402 с причиной доступа либо 403, если план аккаунта прочитать не удалось.
Скоуп модуля-эксклюзива, недоступного аккаунту, эта ручка принимала молча: запрос отвечал 201, и ключ уезжал со скоупом, который соседние ручки выдачи ключей тому же аккаунту отказывают.
Стало
Живое платное место открывает эту ручку: приложение создаётся и ключ выдаётся. Прочие ручки ВЫПИСКИ ключа отвечают как раньше — правило касается только этой.
Скоуп недоступного аккаунту модуля теперь отклоняется явно: 403
SCOPE_NOT_AVAILABLE_ON_PORTAL, как на соседних ручках выдачи ключей. Отказ приходит до
создания записи, приложение не заводится и ключ не выписывается.
Послабление по месту приезжает РАСКАТКОЙ и включается по аккаунтам. Пока она не дошла до вашего аккаунта, вызов отвечает прежним отказом — 402 с причиной доступа либо 403; это признак раскатки, а не ошибки интеграции. Проверка скоупов раскаткой не гейтится и действует сразу.
Место снимает ТОЛЬКО отказ по доступу аккаунта к платформе, и только его. Исчерпанный
баланс им не лечится — счёт по-прежнему отвечает ACCOUNT_FROZEN, эта проверка идёт раньше.
Не снимаются местом и недоступный регион с коробочным аккаунтом без подтверждённого
партнёрства — такому аккаунту ждать раскатки незачем.
Что делать интеграторам
Передавайте в b24Scopes только те скоупы, которые доступны аккаунту. Состав доступных
модулей читается из карточки аккаунта; ранее выданные ключи не меняются. Если запрос
получил 403 SCOPE_NOT_AVAILABLE_ON_PORTAL, уберите названный скоуп из тела и повторите
запрос с новым ключом идемпотентности.
FIX-0917-17: порог простоя на сервере с расписанием больше не молчит
Было
PATCH /v1/infra/servers/{id}/sleep на сервере, которому назначено расписание работы, отвечал
200, записывал порог и не менял ничего: расписание сильнее порога, и машина продолжала работать
по своим окнам. Счёт приходил прежним, а по ответу это было не отличить от применённой настройки.
Сгенерированные расписанием строки пробуждения при этом попадали в GET /v1/infra/servers/{id}/wake-schedules наравне с обычными, хотя снять или поправить их было нельзя —
следующая синхронизация расписания возвращала их обратно.
Стало
Запись порога снимает расписание: число переводит сервер в режим «засыпает, когда нет запросов»,
null — в «круглосуточно». Ответ остаётся 200 и несёт новое поле workScheduleCleared — по нему
видно, что расписание было снято. Сгенерированные расписанием строки пробуждения в списке окон
больше не показываются, а обращение к такой строке по идентификатору в PATCH/DELETE отвечает
404 NOT_FOUND: этими строками управляет расписание сервера, а не список окон. Заведённые вручную
окна не затрагиваются.
BC-0917-18: схема счёта достроена: одиннадцать полей ответа объявлены, семь из них — только для чтения, contacts помечено как не возвращаемое
Поддержка старого формата до: не предусмотрена
Было
GET /v1/invoices/{id} (а также список, поиск и include) отдавал тринадцать полей, которых не было ни в схеме, ни в справочнике, ни в машинном описании OpenAPI: contactIds, observers, taxValue, locationId, webformId, lastActivityBy, utmSource, utmMedium, utmCampaign, utmContent, utmTerm, а также parentId2 и parentId7. Страница полей описывала восемь из них строками из живых метаданных портала — с типами Битрикс24 (user, crm_contact, double…) и без признака «только чтение»; пяти utm* не было нигде. Явный select любого из первых одиннадцати отвечал 200 с предупреждением UNKNOWN_SELECT_FIELD, хотя в GET /v1/invoices/{id} значение приходило. Семь из них Битрикс24 проставляет сам и присланное значение не хранит — taxValue, lastActivityBy и пять utm* — но создание, изменение и импорт отвечали успехом (201/200), а значение исчезало. Страница полей обещала, что contacts у счёта приходит в ответе и работает в select; на деле ключа в ответе нет никогда, а попытка записать contacts отвечала сырой ошибкой ORM Битрикс24 422 BITRIX_ERROR. Пустое locationId приходило пустой строкой.
Стало
Одиннадцать полей объявлены в схеме, справочнике, машинном описании и на странице полей с типами по живому замеру: contactIds, observers (массивы; изменение заменяет набор, пустой список снимает привязки), locationId (строка), webformId (число) — доступны на запись; taxValue, lastActivityBy, utmSource, utmMedium, utmCampaign, utmContent, utmTerm — только для чтения. Явный select этих полей больше не даёт предупреждения. Значение, присланное в поле только для чтения, — которое Битрикс24 и раньше не сохранял — теперь отклоняется: создание и изменение — 400 READONLY_FIELD, импорт — 400 IMPORT_ITEM_VALIDATION, пакетный запрос сущности — 400 BATCH_ITEM_VALIDATION, общий пакетный запрос — READONLY_FIELD под вызовом в data.errors. Поле contacts объявлено как не возвращаемое (notReturned) и только для чтения: страница и GET /v1/invoices/fields честно говорят, что ключа в ответе не бывает, а попытка записи отклоняется тем же гардом и с теми же кодами по дверям, что у полей только для чтения (создание и изменение — 400 READONLY_FIELD, импорт — 400 IMPORT_ITEM_VALIDATION, пакет сущности — 400 BATCH_ITEM_VALIDATION, общий пакет — READONLY_FIELD под вызовом) вместо сырой ошибки портала; привязанные контакты — в contactIds. Пустое locationId приходит как null, как и остальные пустые строки записи. Как у любого объявленного скалярного поля, объект или массив в locationId и webformId (и нечисловая строка в webformId) теперь отклоняются 400 INVALID_PARAMS до отправки на портал — раньше такое значение уезжало в Битрикс24 без проверки. Пустой список в contactIds или observers снимает привязки на PATCH /v1/invoices/{id}; пакетный запрос сущности и общий пакетный запрос пустой список передать не могут (подкоманда уходит строкой запроса, где у пустого массива нет записи — ключ пропадал, а ответ был успешным) и теперь отклоняют его: 400 BATCH_ITEM_VALIDATION и INVALID_PARAMS под вызовом в data.errors. Списки и поиск без select по-прежнему несут пользовательские поля ufCrm_* и связи parentId2/parentId7; явный select пяти utm* на списке и поиске Битрикс24 игнорирует — эти поля приходят только в полной записи. В filter и order эти поля, как и прежде, не принимаются (400 UNKNOWN_FILTER_FIELD / 400 UNKNOWN_SORT_FIELD). Поля связей parentId2 и parentId7 остаются динамическими: их тип и подписи по-прежнему берутся из живых метаданных портала, фильтр по ним работает, сортировка — нет.
Что делать интеграторам
Если вы отправляли taxValue, lastActivityBy или utm* при создании, изменении или импорте счёта — уберите их из тела: Битрикс24 их не хранил и раньше, налог задаётся через товарные позиции, автора активности и UTM-метки проставляет портал. Если вы отправляли contacts — замените на contactIds (полный список привязок). Если вы проверяли locationId на пустую строку — проверяйте на null. Если в select были поля из списка выше — предупреждение пропало, менять ничего не нужно; utm* на списке и поиске читайте без select. Если вы снимали привязки или наблюдателей пустым списком через пакетный запрос — это никогда не срабатывало; делайте это одиночным PATCH /v1/invoices/{id}.
Затронутые эндпоинты: GET /v1/invoices/{id}, GET /v1/invoices, POST /v1/invoices/search, GET /v1/invoices/fields, POST /v1/invoices, PATCH /v1/invoices/{id}, POST /v1/invoices/import, POST /v1/{entity}/batch, POST /v1/batch.
FIX-0917-19: Idempotency-Key защищает создание и переиспользование galaxy-приложений
Было
Первое galaxy-создание не резервировало Idempotency-Key: потеря ответа и повтор могли создать второй ресурс. Переиспользование слота также не сохраняло ключ и могло повторно запустить сборку.
Стало
После включения защиты POST /v1/infra/servers резервирует ключ при создании и переиспользовании galaxy-приложения. Ответ остаётся HTTP 201: повтор в течение 15 минут возвращает тот же ресурс с Idempotent-Replayed: true, без повторной сборки и выдачи SSH-секретов. Разные ключи могут указывать на один переиспользованный слот. После окна занятый ключ получает 409 IDEMPOTENCY_KEY_ALREADY_USED либо обычный отказ политики. Записанные ключи защищены и после выключения защиты; прежние galaxy-создания без записи ключа восстановить нельзя.
Влияние на интеграторов
Используйте тот же ключ при потере ответа. После ошибки сборки вызывайте явный deploy. Защита включается по порталам постепенно: пока она не включена, первое galaxy-создание ведёт себя как раньше, поэтому проверьте список ресурсов перед повтором.
BC-0917-20: ошибка режима ключа указывает на доступный переключатель
Поддержка старого формата до: не предусмотрена
Было
Текстовые подсказки о 403 WRITE_BLOCKED_READONLY_KEY в самоописании ключа и описаниях операций отправляли любой ключ в /keys. Ключи авторизации приложений на этой странице не показываются, поэтому переключить их режим по подсказке было невозможно.
Стало
Подсказки теперь учитывают вид вызывающего ключа. Форма ответа не изменилась: готовый путь по-прежнему находится в обязательном поле error.details.switchUrl и ведёт на /keys для личного ключа, /management-keys для management-ключа, /applications для ключа с карточкой Application и /apps для самостоятельного OAuth-приложения.
Влияние на интеграторов
Для WRITE_BLOCKED_READONLY_KEY менять обработку ответа не нужно. Если интерфейс показывает действие, используйте error.details.switchUrl как готовый путь.
Миграция ротации OAuth: конкурентная операция над тем же приложением теперь может вернуть 409 REISSUE_IN_PROGRESS, пока изменение выполняется, или 409 KEY_NOT_ACTIVE, если исходный ключ уже изменился. Перечитайте состояние ключа и повторяйте ротацию только для всё ещё действующей исходной строки. Прежний небезопасный конкурентный сценарий без явного отказа не поддерживается.
Затронутые эндпоинты: GET /v1/me, GET /v1/openapi.json, POST /v1/placements/bind, POST /v1/placements/unbind, PATCH /v1/apps/:id, POST /v1/apps/:id/relink-oauth, POST /v1/keys/:id/rotate.
BC-0917-21: ответы по контактам и сделкам без служебной строки поискового индекса Битрикс24
Поддержка старого формата до: не предусмотрена
Было
GET /v1/contacts/{id} и GET /v1/deals/{id} (а также список, поиск, include, пакетные запросы на чтение и ответы на создание, изменение, перенос сделки по стадиям и конвертацию лида) отдавали поле searchContent, которого нет ни в описании полей, ни в справочнике: служебную строку полнотекстового индекса Битрикс24 — номер записи, название транслитом, имя ответственного, стадия и даты, склеенные в одну строку. Запросить это поле через select или отфильтровать по нему было нельзя (400 UNKNOWN_SELECT_FIELD, 400 UNKNOWN_FILTER_FIELD); сортировка по нему у контактов отклонялась (400 UNKNOWN_SORT_FIELD), у сделок — проходила. Поле просто приходило в нагрузку. У лидов и предложений такая же строка из ответа уже исключена.
Стало
Поле searchContent исключено из ответов по контактам и сделкам; ответ на чтение остаётся 200, остальные поля не меняются. select и filter по этому полю, как и прежде, не принимаются; сортировка по нему (sort, order в запросе списка и в теле поиска) у сделок теперь тоже отвечает 400 UNKNOWN_SORT_FIELD, как у контактов. Параметр order внутри подвызова общего пакетного запроса по-прежнему передаётся в Битрикс24 как есть — используйте там sort. У компаний поле остаётся объявленным и описанным как служебное — там ничего не меняется.
Что делать интеграторам
Если вы читали searchContent из ответов по контактам или сделкам — поля больше нет; полнотекстовый поиск ведите фильтрами по обычным полям (name, lastName, title с $contains); ответственный — это assignedById, его имя — по этому идентификатору из GET /v1/users/{id}. Если вы сортировали сделки по searchContent — сортируйте по объявленному полю, например id. Если вы не обращались к этому полю — менять ничего не нужно.
Затронутые эндпоинты: GET /v1/contacts/{id}, GET /v1/contacts, POST /v1/contacts/search, POST /v1/contacts, PATCH /v1/contacts/{id}, GET /v1/deals/{id}, GET /v1/deals, POST /v1/deals/search, POST /v1/deals, PATCH /v1/deals/{id}, POST /v1/deals/{id}/move, POST /v1/leads/{id}/convert, POST /v1/{entity}/batch, POST /v1/batch.
FIX-0917-22: обновление smart-processes в batch использует документированный идентификатор
Было
POST /v1/batch мог отклонить корректное обновление smart-processes из-за неверного идентификатора, хотя в запросе был передан документированный публичный entityId.
Стало
POST /v1/batch корректно обновляет smart-processes, когда в запросе передан тот же документированный публичный entityId.
Влияние на интеграторов
Клиентам ничего менять не нужно.
FIX-0917-23: схема фильтров поиска учитывает возможности сущности
Было
Сгенерированная схема POST /v1/{entity}/search объявляла MongoDB-операторы фильтрации для всех сущностей, включая сущности без поддержки фильтров и сущности только с точным совпадением.
Стало
OpenAPI-схема выводит допустимые поля и формы фильтра из возможностей сущности. Для фильтров только с точным совпадением доступны скалярные значения и, где они поддерживаются, непустой массив или $in. Неподдерживаемые поля и операторы больше не объявляются допустимыми.
Влияние на интеграторов
Поведение API не изменилось. Генераторы клиентов и средства проверки запросов теперь точнее отражают уже действующие ограничения runtime.
FIX-0917-24: подсказка при недоступном каталоге пакетов Galaxy
Было
При недоступности каталога Node.js или Debian во время сборки Galaxy API возвращал 502 GALAXY_APP_BUILD_FAILED без категории и подсказки.
Стало
Такие отказы возвращают прежний 502 GALAXY_APP_BUILD_FAILED с категорией INSTALL_REGISTRY_UNAVAILABLE и подсказкой для повторного деплоя.
BC-0917-25: семь полей реквизитов клиента у предложения снова возвращаются и объявлены только для чтения
Поддержка старого формата до: не предусмотрена
Было
С записи BC-0914-21 семь колонок реквизитов клиента clientTitle, clientAddr, clientContact, clientEmail, clientPhone, clientTpId, clientTpaId были исключены из ответа GET /v1/quotes/{id} (а также списка, поиска и include) как служебные: через API они не заполнялись. Замер прямым вебхуком на тестовых порталах Битрикс24 показал иное: привязка компании и контакта их не заполняет, item-API, через который пишет платформа Вайбкод, их не принимает — но устаревшие методы crm.quote.add / crm.quote.update (ими пользуются старые интеграции) их сохраняют, и портал возвращает эти значения при чтении. На таких порталах читатель терял реквизиты клиента, а select=clientTitle отвечал 400 UNKNOWN_SELECT_FIELD. Присланное на запись значение принималось с ответом 201 / 200 и подсказкой UNRECOGNIZED_WRITE_FIELD, хотя портал его отбрасывал.
Стало
Семь полей client* объявлены в описании и справочнике как поля только для чтения: они возвращаются в ответах на чтение (ответ остаётся 200), принимаются в select, filter и order, а присланное на запись значение отклоняется вместо тихой потери: создание и изменение — 400 READONLY_FIELD, импорт — 400 IMPORT_ITEM_VALIDATION, пакетный запрос сущности — 400 BATCH_ITEM_VALIDATION, общий пакетный запрос — READONLY_FIELD под вызовом в data.errors. У предложений, в которые снимок никто не записывал (в том числе созданных через API), эти поля приходят как null, как и остальные незаполненные строковые поля.
Что делать интеграторам
Если вы отправляли client* при создании или изменении предложения — уберите их из тела (в том числе в пачках и импорте): портал их никогда не сохранял, теперь запрос отклоняется. Реквизиты клиента для новых предложений берите из привязанных контакта и компании по contactId и companyId. Если вы читаете предложения, заведённые старыми интеграциями, — снимок реквизитов снова доступен в этих полях.
Затронутые эндпоинты: GET /v1/quotes/{id}, GET /v1/quotes, POST /v1/quotes/search, GET /v1/quotes/fields, POST /v1/quotes, PATCH /v1/quotes/{id}, POST /v1/{entity}/import, POST /v1/{entity}/batch, POST /v1/batch.
NEW-0917-26: новое значение PARKED у состояния места Коворка
Поле состояния места теперь может принимать значение PARKED вдобавок к прежним — и в subscription.state ответа GET /v1/cowork/state, и в state ответа GET /v1/cowork/me. Это одна и та же колонка подписки, отданная двумя ручками. Место получает его, когда владелец перестал быть активным участником портала: за такое место не списывается, доступа оно не даёт, оплаченный срок и тариф на нём сохраняются, а администратор компании может отдать его другому сотруднику.
Стоянка не бессрочна, и это важно знать тому, кто на неё опирается: когда оплаченный срок такого места заканчивается, оно уходит в PAUSED и дальше живёт по прежним правилам приостановленного места. То есть PARKED означает «оплаченный остаток ещё есть», а не «место будет ждать вечно».
Прежние значения и их смысл не менялись, старые запросы работают как работали. Клиенту достаточно трактовать незнакомое значение как «доступа сейчас нет» — ровно так же, как PAUSED и CANCELLED.
BC-0917-27: Однозначный выбор записи и страницы при получении CRM-документов
Поддержка старого формата до: не предусмотрена
Было
GET /v1/crm-documents принимал повторённый entityId с ответом HTTP 200 и возвращал документы записи, указанной последней. Одновременная передача entityId и entityID также принималась, а исход зависел от порядка имён. Скобочные формы — entityId[]=7, entityId[0]=7, entityId[a][b][c]=7 — выглядели как непереданный параметр: фильтр по записи молча исчезал, и ответ приходил по всем записям указанного типа. Параметр start вёл себя так же: повтор давал последнее значение, а скобочная форма молча возвращала первую страницу вместо запрошенной.
Стало
Повтор параметра, оба имени вместе и скобочные формы возвращают HTTP 400 INVALID_ENTITY_ID для выбора записи и HTTP 400 INVALID_START для выбора страницы, даже если значения совпадают. Отказ приходит до обращения к Битрикс24. Одиночное положительное целое под именем entityId или entityID, одиночный start и пустое значение entityId= работают как прежде. Параметры select и order этим изменением не затронуты — их поведение не менялось.
Что делать интеграторам
Передавайте entityId (или entityID) и start не более одного раза, каждый одним значением. Не используйте массивы и объекты для выбора записи и страницы. Кодируйте пользовательский ввод при построении строки запроса: незакодированное значение может внести в запрос любой дополнительный параметр, включая те, что проверку однозначности проходят. Если идентификатор записи может оказаться незаполненным, опускайте параметр целиком вместо подстановки пустого значения — пустое значение означает выборку по всем записям типа.
BC-0917-28: карточка шаблонов документов объявляет оба права для примера с fileId
Поддержка старого формата до: не предусмотрена
Было
Карточка POST /v1/doc-templates показывала пример создания из файла Диска через fileId, но в машинном поле requiredScope называла только documentgenerator. Клиент, который брал права из api-reference.json, мог выпустить ключ без disk и получить 403 SCOPE_DENIED при повторении опубликованного примера.
Стало
Runtime не меняется: тело с base64-полем file по-прежнему требует только documentgenerator, а форма fileId требует documentgenerator и disk. Для этой карточки api-reference.json больше не публикует неполный requiredScope; вместо него приходит requiredScopes: ["documentgenerator","disk"], а badges карточки показывают оба права.
Что делать интеграторам
Если вы читаете api-reference.json, сначала проверяйте requiredScopes и запрашивайте весь список прав из него. Используйте requiredScope только когда requiredScopes отсутствует.
FIX-0917-29: параметр в скобочной форме получает 400 вместо 500
Было
Если параметр пришёл не строкой, а массивом или объектом (?portalId[]=x в query или массив в JSON-теле), GET /v1/keys, POST /v1/keys, POST /v1/feedback и PATCH /v1/feedback/{id} отвечали 500 с кодом внутренней ошибки. GET /v1/feedback под management-ключом отвечал 500 на ?portalId[]=x.
Стало
Такой запрос получает штатный 400 с тем же кодом, что и отсутствующее или неверное поле: MISSING_PORTAL_ID для portalId, VALIDATION_ERROR для category и resolution. Необязательный фильтр portalId у GET /v1/feedback в такой форме не применяется: ответ 200, как без фильтра. Для корректных строковых значений ответ не изменился, успешный HTTP 200 сохраняется.