Журнал изменений API Вайбкод

История изменений API Вайбкод: новые возможности, исправления и изменения с потерей обратной совместимости. Записи расположены от новых к старым.

Префиксы записей

  • NEW — новая возможность: новый эндпоинт, новое необязательное поле или параметр, новый код ошибки в новом сценарии. Прежние запросы клиентов продолжают работать.
  • FIX — исправление поведения. Ответ меняется на корректный, действий со стороны клиента не требуется.
  • BC — изменение с потерей обратной совместимости. Требует действий со стороны клиента. Старый формат поддерживается указанный срок, затем прекращается.

Формат кода записи: {ТИП}-{ММДД}-{N}, где ММДД — дата публикации, N — сквозной номер в рамках даты.

Часть записей NEW помечена в процессе раскатки: метод вышел в конкретном обновлении Битрикс24 и доступен не на всех порталах. Пока обновление не приехало на портал, вызов возвращает 422 METHOD_NOT_YET_AVAILABLE с целевой версией — это признак раскатки, а не ошибка интеграции.

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.

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 или 422400 с кодом 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 теперь допускаются параметры между типом и ;base64data: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-configsPATCH) молча игнорировал нераспознанные поля тела — 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 Ꝟ/мес.

Стало

Линейка переименована со сдвигом: STARTPRO, PROMAX, MAXULTRA; набор значений теперь 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 — подсказка правильного имени (deployModeplacement), в 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 принимали userIdduration у 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 отдавал недокументированные сырые поля Битрикс24 GAPI_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-поля из ответа.

Затронутые эндпоинты:

FIX-0714-8: PATCH catalog-product-properties снова работает (был неустранимый catch-22)

Было

Обновить свойство товара было невозможно ни при каком теле: PATCH без iblockId422 («Required fields: iblockId» — B24 требует его на каждом update), а PATCH с iblockId400 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, 1e31, 1.51.
  • Глобальный 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-processes 12abc усекался parseInt-ом до 12 и попадал в ЧУЖОЙ тип.

Стало

  • Чек-лист: родительская задача проверяется до создания пункта; несуществующая (или недоступная ключу) задача → 404 TASK_NOT_FOUND.
  • Склады и шаблоны документов: значение не того типа → 400 INVALID_PARAMS без вызова Битрикс24 (числовая строка в numeratorId по-прежнему принимается).
  • Сущности с явно типизированным числовым id: не-канонично-целый :id400 INVALID_PARAMS до вызова Битрикс24 (id 0 — основная воронка сделок 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} c label — тихий no-op на обновлении: ответ 200, но подпись поля не менялась (Битрикс24 crm.*.userfield.update игнорирует LABEL).
  • POST /v1/humanresources/nodes/{id} c parentId — ложный «перенос удался»: 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.
  • Подсказки об известных ограничениях привязаны к релевантности сообщения ошибки, а не только к имени метода.

Затронутые эндпоинты:

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, deleteids, чтения — 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-productsiblockId); нет обязательного list-параметра — 400 MISSING_REQUIRED_PARAMS (напр. calendar-eventstype,ownerId; humanresources-nodestype). Как у 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-item 400 до вызова Битрикс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_FAILEDGALAXY_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. Поля аддитивные: если причина не распознана, они отсутствуют (buildHintnull), а 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-семантика): не переданные необязательные поля сбрасываются к значениям по умолчанию — enabledtrue, 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/completionsPOST /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) этим лимитом не затрагивался.

Было

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 без entityTypeId400 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:00Z (тот же момент времени). В фильтре и сортировке используйте 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 (на .tech — по-русски, на .com — по-английски).

У позиций корзины (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_IDwelcomeBotId, WORKTIME_TOworkTimeTo, LINE_NAMEname). Полный перечень новых имён — в справке /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/paymentspaySystemIsCash строкой "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/fieldslabel для полей; 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 (на .tech — по-русски, на .com — по-английски). У полей со служебными кодами добавлены словари 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 } (errorcode, error_descriptionmessage), как у остальных ошибок. 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=errorprovisionError/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: Bearer401 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=KNOWLEDGEscope=KNOWLEDGE, type=GROUPscope=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 принимает новое значение entityTypeinvoice. Передайте 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}. Это позволяет ИИ-агенту и интерфейсу показывать название и назначение поля, не обращаясь к документации. Тексты локализованы по сегменту: русские на .tech, английские на .com. Поля добавлены для сущностей: Отделы, Смарт-процессы, Хранилища, Папки, Файлы, Рабочие группы, Шаблоны документов, Бронирования, События календаря, Задачи, Реквизиты, Сотрудники. Для Сотрудников дополнительно исправлена подстановка подписей: 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, а при нехватке скоупа disk403 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/tokenredirect_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 реально привязывает к методам (taskstask), поэтому ключ открывает методы задач независимо от выбранного написания. На уже выписанные ключи изменение не распространяется ретроактивно — перевыпустите ключ.

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 }.

Поддержка старого формата до: 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 получил два необязательных параметра. placementauto (по умолчанию, поведение прежнее) или 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.*.

Список адресуется типом инфоблока (iblockTypeIdlists, 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.

Смотрите также