Для AI-агентов: markdown этой страницы — /docs-content/changelog/2026-08-01.md индекс документации — /llms.txt
Изменения API: 1 августа 2026
← Журнал изменений · Август 2026
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.
Отдельно: англоязычная подсказка про модуль «Универсальные списки» больше не содержит русского названия на международных порталах.
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 — описывают отношение к задаче того пользователя, от имени которого работает ключ, а не свойство самой задачи: другой ключ на том же портале увидит другие значения.