Для AI-агентов: markdown этой страницы — /docs-content/changelog/2026-07-30.md индекс документации — /llms.txt
Изменения API: 30 июля 2026
← Журнал изменений · Июль 2026
NEW-0730-1: withTotal в списочных вызовах пакетного запроса
Списочный вызов внутри POST /v1/batch принимает в params параметр withTotal — тот же, что у одиночного GET /v1/{entity}. withTotal: false означает «количество не нужно»: подсчёт у Битрикс24 не заказывается, а data.totals.<id> и meta.<id>.total в ответе не приходят. Границей листания остаётся meta.<id>.hasMore.
Границы применимости стоит знать до того, как параметр окажется в коде. Он действует на вызовах с action: "list" — и на тех, у которых limit не больше 50 и offset равен нулю (именно такие уезжают в Битрикс24 одной командой пакета), и на тех, у которых limit больше 50. У action: "search" и у пакета одной сущности POST /v1/{entity}/batch параметр инертен — подсчёт заказывается как раньше, total приходит.
Правило присутствия total там же, что у одиночного вызова: если подсчёт не заказан, а страница пришла короче полной, точное количество всё равно приходит — оно известно из самой страницы.
У вызова, который подсчёт не заказывал, meta.<id>.hasMore выводится из полноты страницы, а не из отсутствующего количества: пришла полная страница — записи ещё есть, пришла короткая — коллекция закончилась. Цикл «читать, пока hasMore» доходит до конца и без total; если размер коллекции ровно кратен размеру страницы, последний вызов вернёт пустой список — это штатный признак конца, а не ошибка.
Вместе с этим у вызова, который подсчёт не заказывал, data.totals.<id> и meta.<id>.total перестают приходить и на позиционированной странице — при offset больше нуля. Так ведут себя и одиночные эндпоинты: количество не должно то появляться, то исчезать внутри одного обхода.
FIX-0730-2: meta.total приходит на короткой странице, даже когда подсчёт не заказывался
Было
Если вызов списка не заказывал подсчёт, meta.total в ответе отсутствовал всегда — в том числе там, где количество было известно точно. Страница короче запрошенного limit означает, что коллекция на ней и закончилась, то есть число записей равно числу отданных строк, — но поле всё равно не приходило.
Стало
В этом случае meta.total приходит и содержит точное число. Правило простое: total появляется только на вызове с offset = 0 и только если страница пришла короче запрошенного. Пустой результат — тоже число: "total": 0.
Явный withTotal=false в запросе по-прежнему означает «поля total не будет» — обещание про этот параметр не изменилось. Настройка totalDefault на API-ключе и платформенное умолчание точный total из короткой страницы не запрещают: про них такого обещания не давалось. Отсюда следствие, о котором стоит знать заранее: два одинаковых запроса от двух разных ключей могут вернуть ответы разной формы.
Правило действует и на списочных вызовах внутри POST /v1/batch.
Влияние на интеграторов
Менять ничего не нужно. meta.total остаётся необязательным полем: проверяйте его наличие в конкретном ответе, а не предполагайте. Границей цикла листания остаётся meta.hasMore — арифметика по meta.total для этого не годится ни до, ни после изменения.
FIX-0730-3: бесплатный тариф больше не получает 402 PLAN_NOT_ALLOWED_ON_TRIAL при деплое в галактику
Было
POST /v1/infra/servers с { name, source, runtime, start } на портале с galaxy-размещением возвращал 402 PLAN_NOT_ALLOWED_ON_TRIAL (details.allowedPlans: ["bc-micro"]), хотя GET /v1/me в capabilities.servers.create отдавал available: true с тем же bc-micro. Плана в запросе не было вовсе: платформа сама поднимала общий galaxy-хост на своём (не-bc-micro) плане, и этот внутренний выбор проверялся тем же списком планов, что и VM, создаваемая пользователем. Каждое galaxy-приложение при этом расходовало единственный слот бесплатного тарифа, поэтому второй деплой упирался в 402 TRIAL_PORTAL_LIMIT.
Стало
Одношаговый create-and-deploy работает на бесплатном тарифе. Общий galaxy-хост занимает единственный VM-слот, план ему выбирает платформа (минимальный стабильный план сегмента), а контейнеры-приложения на этом хосте слот не расходуют — их число ограничено только ёмкостью хоста. Список планов из capabilities.servers.create.limits.allowedPlans теперь относится только к VM, которую вы создаёте сами; deployment.galaxyApp._rules (правило QUOTA) описывает это явно. Для placement: "dedicated" ограничения бесплатного тарифа не изменились.
FIX-0730-4: /v1/me не предлагает эндпоинты токенов доступа, когда раздел выключен
Было
Блок data.infra.preview ответа GET /v1/me всегда содержал адреса mintUrl, listUrl, revokeUrl и refreshUrl — в том числе когда раздел токенов доступа выключен на платформе и все четыре эндпоинта отвечают 503 FEATURE_DISABLED. Соседние блоки data.capabilities.servers.preview и data.deployment.preview в том же ответе сообщали {"available": false, "reason": "FEATURE_DISABLED"}, то есть один ответ противоречил сам себе.
Стало
data.infra.preview следует тому же признаку, что и два соседних блока. При включённом разделе рядом с адресами приходит "available": true, при выключенном — {"available": false, "reason": "FEATURE_DISABLED"} без адресов. Правило data.api._rules про проверку деплоя через режим api-bearer называет эту проверку и запасной путь — шаги healthcheck и tunnel_routing в отчёте POST /v1/infra/servers/:id/deploy.
Влияние на интеграторов
Клиент, который читал адреса из data.infra.preview безусловно, при выключенном разделе получит блок без них. Проверяйте поле available перед обращением к адресам — вызовы по ним и раньше отвечали 503 FEATURE_DISABLED.
FIX-0730-5: заголовок X-Vibe-User-Id больше не смешивает идентификаторы
Было
Заголовок X-Vibe-User-Id, который платформа передаёт приложению на каждом запросе, документировался как числовой ID пользователя Битрикс24. На части маршрутов входа в него попадал внутренний идентификатор платформы Вайбкод без какого-либо признака или значение 0 — заглушка «пользователь не опознан». Приложение отличить это от настоящего ID не могло. Опасен именно внутренний идентификатор, начинающийся с цифр: Битрикс24 приводит строку к числу, поэтому 47a4cff2-… превращалось в 47 — валидный ID постороннего сотрудника портала. Приложение, подставлявшее заголовок в DIALOG_ID метода im.message.add, отправляло личное сообщение не тому человеку.
Стало
Значение заголовка всегда одно из двух: либо строка из одних цифр — ID пользователя портала Битрикс24, либо значение с явным префиксом, если ID в портале у посетителя нет: net_ — пользователь Битрикс24 Нетворк вне портала, share: — анонимный посетитель по гостевой ссылке, vibe: — пользователь платформы Вайбкод, чей ID в портале определить не удалось. Значение 0 не отправляется вовсе: если идентичность неизвестна, заголовка просто нет. При входе через кабинет Вайбкод платформа теперь определяет настоящий ID пользователя в портале и присылает именно его — там, где раньше приходил внутренний идентификатор.
Если приложение подставляет заголовок в параметры методов Битрикс24 (USER_ID, DIALOG_ID, RESPONSIBLE_ID и подобные) — проверяйте, что значение состоит только из цифр. Значение с префиксом означает, что у посетителя нет ID в портале, и передавать его в Битрикс24 нельзя. Для логов, аналитики и собственного ACL заголовок годится в любом виде. Полный контракт — Что приходит в приложение.
BC-0730-6: галактика не создаётся на бесплатном тарифе Битрикс24
Поддержка старого формата до: 30.08.2026
Было
Аккаунт Битрикс24 на бесплатном тарифе с включённым режимом галактик получал галактику под свои приложения. Одношаговое создание POST /v1/infra/servers с полем source разворачивало приложение контейнером на общем хосте, а GET /v1/me в блоке deployment описывал galaxy-контракт.
Стало
На бесплатном тарифе новая галактика не создаётся. Пока у аккаунта нет галактики, каждое приложение разворачивается на отдельной виртуальной машине, а создание с полем source возвращает 400 SOURCE_AT_CREATE_GALAXY_ONLY. GET /v1/me в этом состоянии не отдаёт deployment.galaxyApp, ставит deployment.primary в standalone и объясняет причину в новом поле deployment.placementNote.
Аккаунт, у которого галактика уже есть, продолжает разворачивать приложения в ней — ограничение касается только создания новой. Коммерческий тариф Битрикс24 снимает ограничение целиком.
Что делать интеграторам
Разворачивайте в два шага, как на обычном сервере: POST /v1/infra/servers с provider, name, plan, region (без source), дождитесь status: "running" и blackholeStatus: "CONNECTED", затем POST /v1/infra/servers/:id/deploy с source, runtime, start. Модель размещения определяйте по GET /v1/me — по наличию блока deployment.galaxyApp, а не по режиму портала.
FIX-0730-7: POST /v1/apps честнее сообщает о причине отказа на коробочном портале
Было
При установке приложения на коробочном портале без активной подписки «BitrixGPT + Маркетплейс» POST /v1/apps возвращал непрозрачный 502 CONNECTOR_APP_INSTALL_FAILED: причина отказа наружу не выдавалась вовсе, а сам код обещал временный сбой — повтор вызова выглядел осмысленным, хотя помочь не мог.
Стало
Отказ по подписке распознаётся и на коробочном портале: POST /v1/apps возвращает 403 с кодом, называющим причину. Аккаунт в подписочном регионе (Россия, Беларусь) без активной подписки получает B24_MARKET_SUBSCRIPTION_REQUIRED — вместе с понятным сообщением и ссылкой на оформление в error.details.upgradeUrl. Аккаунт на тарифной модели доступа, а также портал, на котором REST недоступен, получает INT_TARIFF_REQUIRED (нужен коммерческий тариф Битрикс24) без error.details.upgradeUrl: подписки, которую можно оформить, там нет. Прочие отказы установки через коннектор классифицируются как прежде.
Влияние на интеграторов
Менять ничего не нужно, успешные вызовы не затронуты. Тем, кто обрабатывал 502 CONNECTOR_APP_INSTALL_FAILED, стоит дополнительно ловить 403 B24_MARKET_SUBSCRIPTION_REQUIRED / INT_TARIFF_REQUIRED — первый приходит на аккаунте подписочного региона, второй на тарифной модели доступа и на портале с недоступным REST — и подсказывать пользователю оформить подписку на портале либо перейти на коммерческий тариф Битрикс24: этот отказ терминальный, повторять запрос бессмысленно.
NEW-0730-8: новый отказ 429 TIMEOUT_QUARANTINE: метод, который перестал отвечать, ставится на паузу
Если один и тот же метод несколько раз подряд не ответил вашему аккаунту Битрикс24 за отведённое вызову время, Вайбкод перестаёт отправлять к нему запросы и отвечает 429 с кодом TIMEOUT_QUARANTINE, заголовком Retry-After и полем error.retryAfter. Пауза действует на пару «аккаунт + метод»: остальные методы работают как обычно, и от того, каким ключом сделан вызов, она не зависит.
Это отказ на стороне Вайбкод, а не лимит Битрикс24: запрос до аккаунта не дошёл, поэтому ничего не изменилось — повтор безопасен даже для методов записи. Пауза снимается сама: время от времени один вызов пропускается для проверки, и первый успешный ответ снимает её немедленно. Вмешательство не требуется, отдельного эндпоинта для снятия нет.
В ответе появилось машиночитаемое поле error.scope: у этого отказа "portal" — пауза общая для ВСЕХ ключей аккаунта, включая чужие интеграции. У соседнего отказа OPERATION_TIME_LIMIT (Битрикс24 приостановил метод, исчерпавший бюджет рабочего времени) оно равно "apiKey" — там приостановлен только вызывающий ключ. Те же два значения error.scope уже приходят в отказе по квоте обращений FEEDBACK_QUOTA_EXCEEDED, словарь общий. Различие даёт ответ на вопрос «чинить свой код или ждать вместе с аккаунтом», не разбирая текст ошибки. Рядом приходит error.hint с действием — на русском. Оба кода теперь описаны в справочнике ошибок. Количество чужих ключей, их имена и объём их неудач в ответе не приходят — это данные других клиентов аккаунта.
Что делать: дождаться срока из Retry-After и повторить, добавив к паузе случайную задержку, — а не крутить повтор в цикле. Интервал повторов при этом не сокращайте: пока пауза действует, один вызов раз в 5 минут пропускается как проба восстановления, и агрессивный повтор занимает этот слот собой — метод остаётся закрытым для всего аккаунта дольше, чем если бы вы просто подождали. Если метод не отвечает стабильно, облегчите вызов: меньше полей в select, меньше страница, более узкий фильтр или более узкий интервал дат. Тяжёлый запрос и есть причина, по которой аккаунт не успевает ответить, — пауза снимется тем вызовом, который аккаунт сумеет выполнить.
Код может прийти на любом вызове, который Вайбкод проксирует в Битрикс24 под именем метода Битрикс24: на одиночных чтениях и записях — как 429 с заголовком, а на подвызовах батча, которые Вайбкод исполняет отдельными запросами (поиск и список с limit больше 50), — внутри ответа 200 строкой data.errors[<id>] вида { "code": "TIMEOUT_QUARANTINE", "message": …, "retryAfter": …, "scope": "portal", "hint": … }: конверт объединяет разные вызовы, поэтому заголовка Retry-After для отдельного подвызова у него нет, срок приходит полем. В батче одной сущности (POST /v1/{entity}/batch) отказ приходит так же внутри 200, но элементом массива data: { "error": { "code": "TIMEOUT_QUARANTINE", "message": …, "retryAfter": …, "scope": "portal", "hint": … } }. Сам конверт POST /v1/batch под паузу не попадает: он объединяет разные методы, и его собственная задержка не говорит о том, какие из них перестали отвечать. Опрос событий бота (GET /v1/bots/{botId}/events) тоже не попадает — у него свой ответ на таймаут, с подсказкой про восстановление подписки. В поиске с разбиением по датам (POST /v1/{entity}/search) отказ приходит либо тем же 429, либо — если часть окон успела прочитаться — в meta.windowErrorSample.code при ответе 200: это признак неполной выдачи.
Защита включается постепенно, по аккаунтам, поэтому этот код увидят пока не все.
FIX-0730-9: отказ OPERATION_TIME_LIMIT в батче приходит со своим кодом и сроком, а не под общим кодом
Было
Битрикс24 приостанавливает метод, исчерпавший бюджет рабочего времени, примерно на 5 минут, и Вайбкод отбивает такие вызовы на входе, зная, что пауза ещё действует. На одиночном вызове этот отказ приходил как 429 с кодом OPERATION_TIME_LIMIT, заголовком Retry-After и полями retryAfter и scope. А внутри батча тот же отказ терял и код, и срок: на подвызовах POST /v1/batch, которые Вайбкод исполняет отдельными запросами (поиск и список с limit больше 50), он приходил как data.errors[<id>] с общим кодом AUTO_PAGINATION_FAILED, а в батче одной сущности (POST /v1/{entity}/batch) — как data[i].error с кодом CALL_FAILED и текстом Internal error. Ответ был 200, поэтому отличить приостановленный метод от внутреннего сбоя было нечем, а срок повтора не приходил вовсе — оставалось повторять вслепую по методу, который аккаунт держит закрытым.
Стало
Обе батчевые поверхности отдают тот же отказ, что одиночный вызов: { "code": "OPERATION_TIME_LIMIT", "message": …, "retryAfter": …, "scope": "apiKey", "hint": … } — в data.errors[<id>] у POST /v1/batch и в data[i].error у POST /v1/{entity}/batch. Конверт объединяет разные вызовы, поэтому заголовка Retry-After для отдельного подвызова у него нет — срок приходит полем retryAfter. Поле scope равно "apiKey": приостановлена связка «ваш ключ + этот метод», другие методы работают, и другие ключи аккаунта тот же метод вызывать могут. Контраст — TIMEOUT_QUARANTINE со scope: "portal", где пауза общая для всего аккаунта. Локализованного поля userMessage в 200-конверте нет ни у одного отказа, поэтому нет и здесь. Ограничение снято и из справочника ошибок.
Влияние на интеграторов
Менять ничего не нужно: коды сузились с общих до конкретного, а поля добавились. Если ваш код ветвился на AUTO_PAGINATION_FAILED или CALL_FAILED, чтобы поймать приостановленный метод, теперь для этого есть OPERATION_TIME_LIMIT и срок в retryAfter — дождитесь его и повторите, а не крутите повтор в цикле.
NEW-0730-10: машинная схема описывает 51 эндпоинт, который работал, но в ней отсутствовал
Все они отвечали и раньше — но клиент, который строит вызовы по /v1/openapi.json (кодогенератор, ИИ-агент, наш собственный справочник), их не видел и не мог о них узнать.
Товарные позиции — работа с отдельной строкой и её схемой полей: GET|PATCH|DELETE /v1/{deals,leads,quotes,invoices}/{id}/products/{rowId} и GET /v1/{deals,leads,quotes,invoices}/{id}/products/fields. То же для смарт-процессов: /v1/items/{entityTypeId}/{id}/products/{rowId} и .../products/fields.
Схемы полей: GET /{сущность}/fields появился у 22 сущностей, у которых Битрикс24 не отдаёт метод схемы (заказы, документы, шаблоны документов, платежи, позиции корзины, статусы заказа, каталоги, разделы и цены каталога, свойства товаров, бронирования, события и группы, подразделения, сайты и страницы, шаблоны/активности/роботы бизнес-процессов, линии телефонии, конфигурации открытых линий, узлы оргструктуры). Ответ там — набор полей, объявленный обёрткой, без пользовательских (UF) полей: в описании операции это сказано прямо, чтобы никто не ждал большего.
Открытые линии: GET|POST /v1/openline-configs, PATCH|DELETE /v1/openline-configs/{id}, POST /v1/openline-configs/search и read-only POST /v1/openline-configs/batch (в батче доступны только list, get, fields — записи идут через перечисленные выше операции). У списка описан его настоящий конверт: total — размер выданного окна, а не всего набора, поэтому цикл постраничного чтения ограничивают по hasMore.
Бронирования: GET /v1/bookings и POST /v1/bookings/search. Окно дат (dateFrom, dateTo) описано как обязательное — без него метод Битрикс24 молча вернул бы пустой список, поэтому обёртка отклоняет такой вызов.
Конвертация лида: POST /v1/leads/{id}/convert — в описании операции сказано, что она не идемпотентна (повторный вызов создаст второй набор сущностей).
Затронутые эндпоинты: GET /v1/openapi.json
FIX-0730-11: текст ошибки проверки BYOK-ключа больше не содержит сам ключ
Было
Если провайдер отвечал на проверку учётных данных ошибкой и повторял в ней присланный ключ, этот текст возвращался вызывающему и сохранялся в поле lastError, которое отдаёт GET /v1/ai/credentials. Скрывались только ключи вида sk-…, адреса и пары «логин:пароль@хост», поэтому ключи прочих провайдеров попадали в ответ и в поле дословно — и были доступны любому участнику портала.
Стало
Присланный ключ и адрес прокси удаляются из текста ошибки по значению, независимо от их формата: и в ответе POST /v1/ai/credentials, PATCH /v1/ai/credentials/{id}, POST /v1/ai/credentials/{id}/test, и в сохраняемом lastError на их месте стоит редакция. Коды ошибок и статусы не изменились.
FIX-0730-12: многостраничный список отдаёт начало выборки вместо ошибки, когда подсчёт не удался
Было
Многостраничный вызов списка — GET /v1/{entity} и POST /v1/{entity}/search с limit больше 50, а также их списочные подзапросы в POST /v1/batch — начинался с подсчёта записей. Подсчёт коллекции стоит Битрикс24 несоразмерно дорого, и если он срывался (тайм-аут, лимит запросов, ошибка портала), вызов возвращал ошибку целиком — без единой записи, хотя первая страница уже была получена.
Параметр withTotal=false на таких вызовах не действовал: подсчёт заказывался всё равно, meta.total приходил.
Стало
Подсчёт выполняется, только когда без него не обойтись, и его срыв больше не отменяет ответ. Если записи получены, а подсчёт не удался, приходит 200 с непрерывным началом выборки: meta.hasMore равен true, meta.total отсутствует, а в meta.pageErrorSample лежат code и message с причиной. Два новых значения code — оба коды самого Вайбкод, а не Битрикс24:
PAGE2_COUNT_FAILED— подсчёт не удался: тайм-аут, лимит запросов или ошибка портала.LAZY_COUNT_NO_PROGRESS— обход остановлен: следующая страница не принесла ни одной новой записи, хотя в коллекции их больше, чем уже отдано.
В пакетном запросе то же самое приходит в data.meta.<id>: hasMore равен true, pageErrorSample заполнен, а total и data.totals.<id> отсутствуют — количество не сосчитано, и число отданных строк его не заменяет.
Заодно withTotal=false начал действовать при limit больше 50 — на GET /v1/{entity}, на POST /v1/{entity}/search и на вызовах action: "list" внутри POST /v1/batch: теперь meta.total не приходит ни при каком исходе. Границы применимости у action: "search" внутри пакета и у пакета одной сущности POST /v1/{entity}/batch не изменились. Без этого параметра количество приходит как раньше.
Оговорка про нагрузку: при limit больше 50 этот параметр — про форму ответа, а не про стоимость. Подсчёт платформе всё равно нужен, чтобы спланировать обход, поэтому дешевле вызов не станет, а точное количество, которое короткая первая страница отдаёт бесплатно, будет отброшено. Подсчёт параметр отменяет только при limit не больше 50.
Влияние на интеграторов
Проверьте, как код узнаёт о неполном ответе. Раньше признаком служила сама ошибка: цикл с повтором по 429 и 5xx срабатывал сам собой. Теперь неполнота уезжает в тело успешного ответа — обработчик ошибок не сработает, а заголовка Retry-After, который приходил вместе с 429, в таком ответе нет.
Признак неполноты — meta.pageErrorSample рядом с meta.hasMore. Продолжить обход можно двумя способами, и порядок между ними не произвольный.
Курсором — это первый выбор. meta.nextAfterId передаётся обратно как filter[>id], смещение при этом остаётся нулевым, и продолжение снова идёт тем же путём, на котором подсчёт заказывается не всегда. Приходит курсор не везде: только у сущностей с курсорным обходом и только при сортировке строго по id по возрастанию.
Смещением — запасной путь. Новый offset равен исходному плюс число отданных записей, но вызов с ненулевым offset уходит на счётный путь и заказывает ровно тот подсчёт, который только что не удался. После PAGE2_COUNT_FAILED это с высокой вероятностью тот же тайм-аут, поэтому повторяйте с паузой, а не в плотном цикле.
У GET /v1/tasks курсора нет — задачи идут без курсорного обхода, и nextAfterId в их ответах не приходит. Для них смещение остаётся единственным способом продолжить, со всеми оговорками выше.
Границей цикла листания по-прежнему остаётся meta.hasMore, а не арифметика по meta.total: поле было необязательным и до этого изменения.
FIX-0730-13: спека объявляет обязательные поля на создании и перестала требовать их на обновлении
Было
Создание через POST /v1/bizproc-robots, POST /v1/bizproc-activities и POST /v1/folders отклонялось, если не передать code, name и handler (для папки — name), а спека объявляла эти поля необязательными: клиент или SDK, сгенерированный по ней, получал отказ на первом же вызове. Обратная сторона той же причины: описание тела запроса было общим для создания и обновления, поэтому там, где обязательные поля всё же были объявлены — POST /v1/activities, POST /v1/documents, POST /v1/bizproc-templates — их требовал и PATCH, хотя частичное обновление одного поля API принимает.
Стало
Требование объявлено на операции создания, а не в общем описании тела. POST перечисляет поля, без которых откажет рантайм; PATCH их не требует и принимает частичное обновление, как и раньше на деле. Поведение не изменилось — изменилось описание, которое теперь соответствует обеим операциям. Те же поля помечены обязательными и на двух других описательных поверхностях: GET /v1/folders/fields с аналогами и GET /v1/guide.