Для AI-агентов: markdown этой страницы — /docs-content/changelog/2026-07-14.md индекс документации — /llms.txt
Изменения API: 14 июля 2026
← Журнал изменений · Июль 2026
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.
FIX-0714-2: Общая 5xx-ошибка на AI-эндпоинтах теперь в OpenAI-совместимом конверте
AI-эндпоинты (/v1/ai/*, /v1/models, /v1/chat/*, /v1/audio/*) документированы с OpenAI-совместимым форматом ошибок. При исчерпании пула соединений это уже соблюдалось, но общая (непредвиденная) 5xx-ошибка на этих эндпоинтах отдавала обычный V1-конверт.
Было
Общая 5xx-ошибка на AI-эндпоинте: {"success": false, "error": {"code": "...", "message": "..."}} — не тот формат, что SDK-клиенты OpenAI ожидают для этих путей.
Стало
Тот же случай теперь отдаёт {"error": {"message": "...", "type": "...", "code": "..."}} — единый конверт для всех ошибок AI-эндпоинтов, включая общие 5xx.
Влияние на интеграторов
Клиенты на OpenAI SDK, уже читающие error.type/error.code (штатный путь для этих эндпоинтов), не заметят изменения. Код, который на AI-эндпоинтах ожидал success/error.code в верхнем уровне именно на общих 5xx, должен переключиться на error.type/error.code.
FIX-0714-3: `versions` в списке версий исходников приложения ограничен 500 записями
GET /v1/apps/:id/sources возвращал весь список версий без ограничения — для приложений с очень длинной историей это была неограниченная выборка.
Было
data.versions — весь список версий без ограничения по размеру; data.totalVersions всегда равнялся data.versions.length.
Стало
data.versions содержит не более 500 последних версий (по savedAt, убывание). data.totalVersions и data.totalSizeBytes по-прежнему считаются по полной выборке — точность агрегатов не зависит от кэпа.
Влияние на интеграторов
Для приложений с историей до 500 версий поведение не меняется. Для приложений с историей больше 500 версий data.versions.length теперь может быть меньше data.totalVersions — код, полагавшийся на их равенство, должен ориентироваться на data.totalVersions/data.totalSizeBytes для агрегатов и не считать data.versions полным списком.
FIX-0714-4: Телефония: `userId`/`duration` теперь по-настоящему валидируются, а не только на truthy
POST /v1/calls/register, /v1/calls/:callId/show, /v1/calls/:callId/hide, /v1/calls/:callId/finish принимали userId (и duration у finish) без проверки типа/формы — любое truthy-значение (например, строка "abc" или объект) проходило в Битрикс24 и падало там уже непрозрачной ошибкой апстрима.
Было
{"userId": "abc"} (или любое другое truthy, не являющееся положительным целым) проходил валидацию и уходил в Битрикс24; ошибка Required: userId (number) появлялась только на полностью пустом/falsy значении.
Стало
userId принимается как положительное целое число либо как числовая строка ("42"), иначе — чистый 400 MISSING_PARAMS с уточнённым текстом Required: userId (positive integer) (для register — плюс phoneNumber). duration у finish аналогично — неотрицательное число либо числовая строка, иначе 400.
Влияние на интеграторов
Корректные вызовы (userId — число или числовая строка) не меняются. Вызовы с ранее «проходившим» некорректным userId/duration (не число, не числовая строка) теперь получают явный 400 вместо непрозрачной ошибки со стороны Битрикс24.
FIX-0714-5: Создание комментария к несуществующей задаче — понятная ошибка вместо ложного успеха
POST /v1/tasks/:taskId/comments и его пакетный вариант POST /v1/tasks/:taskId/comments/batch (action: create) обращаются к Битрикс24 для создания комментария на старой карточке задачи. Если задача не существует или недоступна ключу, Битрикс24 не создаёт комментарий и не возвращает идентификатор.
Было
Оба эндпоинта отвечали успехом с пустым идентификатором — одиночный вызов 201 {"success": true, "data": {"id": null}}, пакетный — элементом {"success": true, "id": null}. Комментарий не создавался, но интегратор не мог отличить это от штатного случая.
Стало
Одиночный вызов возвращает 404 TASK_NOT_FOUND. Пакетный вариант помечает соответствующий элемент как {"success": false, "error": "TASK_NOT_FOUND"}, не отменяя обработку остальных элементов пакета. Отдельный, не связанный с этой ошибкой случай сохраняется: на новой карточке задачи комментарий уходит в чат, и если система не смогла восстановить его id отдельным поиском, ответ по-прежнему success: true с id: null — комментарий в этом случае реально создан.
Влияние на интеграторов
Код, проверяющий data.id / data[i].id на null как признак ошибки, продолжит работать без изменений и получит более точный код ошибки. Код, полагавшийся на молчаливый успех с id: null при недоступной задаче, должен обрабатывать 404 / TASK_NOT_FOUND явно.
FIX-0714-6: Лимит запросов `/v1/search`, `/v1/research`, `/v1/batch` теперь считается на портал
Было
Лимиты (/v1/search 60/мин, /v1/research 20/мин, /v1/batch 30/мин) фактически считались по IP-адресу, а не по порталу. Портал с несколькими API-ключами (или несколько порталов за одним общим egress-IP) мог получить больше документированного лимита, а само ограничение обходилось ротацией IP.
Стало
Лимит считается на портал: все API-ключи одного портала делят единый бакет (60 / 20 / 30 запросов в минуту соответственно). Документированный лимит на тенант теперь применяется корректно и не обходится ни числом ключей, ни сменой IP.
Влияние на интеграторов
Если ваш портал распределял нагрузку на /v1/search / /v1/research / /v1/batch между несколькими API-ключами, суммарный предел теперь — единый лимит портала, а не сумма по ключам. При достижении предела возвращается 429 с заголовком Retry-After (как и раньше).
FIX-0714-7: Календарь: рабочий batch-delete секций, чистые ошибки update и без утечки sync-полей
Было
POST /v1/calendar-sections/batchсaction: "delete"не имел канала дляtype/ownerId, которые требуетcalendar.section.delete— каждый элемент падал на обеих платформах.PATCH /v1/calendar-sections/{id}безtype/ownerId/nameуходил в Битрикс24 и возвращал сырой422с именем внутреннего метода.GET /v1/calendar-sectionsотдавал недокументированные сырые поля Битрикс24GAPI_CALENDAR_ID,CAL_DAV_CON,SYNC_TOKEN,PAGE_TOKEN,EXTERNAL_TYPE(три из них — токены синхронизации).GET /v1/calendar-eventsиGET /v1/calendar-events/{id}отдавали внутреннее полеattendeesEntityList— схема пыталась его вырезать, но не срабатывала из-за несовпадения регистра ключа.
Стало
- Batch-delete секций читает
type/ownerIdиз тела рядом сidsи прокидывает их в каждую команду удаления. Отсутствие любого — чистый400 MISSING_REQUIRED_PARAMSдо вызова Битрикс24. - Partial-update секций требует якорные
type/ownerId/name(у секций нет get-by-id для подстановки) — чистый400, без сырого422и без утечки имени метода. - Оба календарных read-пути убирают перечисленные внутренние/sync-поля из ответа.
Затронутые эндпоинты:
POST /v1/calendar-sections/batchPATCH /v1/calendar-sections/{id}GET /v1/calendar-sectionsGET /v1/calendar-events
FIX-0714-8: PATCH catalog-product-properties снова работает (был неустранимый catch-22)
Было
Обновить свойство товара было невозможно ни при каком теле: PATCH без iblockId → 422 («Required fields: iblockId» — B24 требует его на каждом update), а PATCH с iblockId → 400 READONLY_FIELD (поле только для создания). Итог — весь глагол UPDATE мёртв: ни одно поле нельзя было изменить после создания.
Стало
iblockId теперь подставляется автоматически из существующей записи (pre-fetch, как у catalog-sections), так что PATCH {name:"…"} доходит до B24 с нужным iblockId и возвращает 200. iblockId в теле по-прежнему не нужен (и по-прежнему отклоняется как read-only, если его прислать) — его сохраняет сам сервис.
Влияние на интеграторов
Если раньше ваш PATCH свойства товара всегда падал 422/400 — теперь шлите только изменяемые поля (PATCH {name:"…"}), iblockId передавать не надо.
FIX-0714-9: Валидация entityTypeId: мусорные формы → 400 вместо тихого усечения
Было
Пять поверхностей парсили entityTypeId (селектор типа smart-process / динамической сущности) снисходительно — неякорным parseInt или коэрсирующим Number(), — и мусорная форма молча превращалась в ДРУГОЙ (в рамках портала) тип сущности:
- Путевой
entityTypeId(/v1/items/:entityTypeId/...,/v1/categories/:entityTypeId/...— CRUD и/aggregate):GET /v1/items/1058abcусекался до1058,1e3→1,1.5→1. - Глобальный
POST /v1/batch:params.entityTypeIdчерезNumber()принимал дробные (1.5), hex ('0x10'→ 16), переполнение доInfinity, а также массив[1058]→ 1058. /v1/items/:entityTypeId/userfields/*: собственный парсер —2abcрезолвил пользовательские поля типа2.POST /v1/smart-processes/batch:ids: ['1030abc']усекался до1030— delete/update молча выполнялся против ЧУЖОГО реального типа; дробное число (1030.5) тоже проходило.POST /v1/triggers/fire(entityType="item"):entityTypeId: '1038abc'→ триггер автоматизации выстреливал по типу1038.
Стало
Все пять поверхностей требуют каноничную форму положительного целого (/^[1-9]\d*$/ для строк, Number.isInteger для чисел): любая иная форма → 400 с прежним кодом ошибки поверхности (INVALID_DYNAMIC_PARAM / INVALID_ENTITY_TYPE_ID / BATCH_ITEM_VALIDATION / MISSING_PARAMS) ДО вызова Битрикс24. Aggregate использует общий с CRUD-маршрутами валидатор вместо инлайн-копии.
Влияние на интеграторов
Формы, которые раньше коэрсились в корректное значение и обслуживались — 007 → 7, %20-пробелы, +2 → 2, массив [1058] в batch — теперь отклоняются с 400: значение должно быть каноничным целым без префиксов, хвостов и ведущих нулей. Boolean в batch отклонялся и раньше (коэрсился в reserved-тип); меняется только код ошибки — теперь INVALID_DYNAMIC_PARAM. Корректные вызовы не меняются.
FIX-0714-10: Переоткрытие фидбека очищает поля решения
Было
PATCH /v1/feedback/:id при возврате тикета в активный статус (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` больше не цитирует внутренний текст парсера
Было
Пять маршрутных групп (/v1/apps*, /v1/bots*, /v1/keys*, /v1/note*, /v1/infra/servers/* deploy/exec/upload) на битый JSON отвечали 400 с сообщением вида Invalid JSON: Unexpected token } in JSON at position 41 — сырой текст движка V8 (отпечаток рантайма, деталь реализации). Группа deploy/exec/upload при этом не ставила и код ошибки.
Стало
Все пять отвечают единым статическим сообщением Request body is not valid JSON. — как /v1/<entities> (тот же класс закрывается там отдельным исправлением). На V1-поверхностях код — INVALID_JSON_BODY (deploy/exec/upload теперь тоже его ставит); статус 400 не изменился.
Влияние на интеграторов
Если ваш код парсил текст сообщения (например, вытаскивал позицию ошибки) — опирайтесь на код INVALID_JSON_BODY; позиция ошибки больше не сообщается.
FIX-0714-12: Смета: amount, currency и даты в ответе GET снова заполнены (были null)
Было
Чтение сметы (GET/list/search /v1/quotes) отдавало null для amount, currency, beginDate, closeDate — значения «протекали» только под сырыми ключами Битрикс24 (opportunity, currencyId, begindate, closedate). Запись работала правильно, но READ-проекция теряла все поля-алиасы: сумма сметы и валюта были 100% невидимы через задокументированный API.
Стало
READ-ветка теперь реверс-мапит объявленные алиасы (зеркало write-маппинга): opportunity → amount, currencyId → currency, begindate → beginDate, closedate → closeDate — с приведением типов. Сырые ключи Битрикс24 в ответе больше не появляются.
Влияние на интеграторов
Если вы читали amount/currency смет и получали null — теперь они заполнены. Код, читавший обходным путём сырой opportunity/currencyId из ответа, их там больше не найдёт — переключитесь на документированные amount/currency.
FIX-0714-13: Валидация записи: фантомный чек-лист → 404, мусорные типы и `:id` → 400
Было
POST /v1/tasks/:taskId/checklistна несуществующую задачу возвращал201с правдоподобнымid, хотя пункт не создавался (его нельзя было получить GET-ом).POST /v1/warehousesпринимал нестроковыеtitle/address(число, объект) и пересылал их в Битрикс24 с непредсказуемым результатом;POST /v1/doc-templatesтак же пропускал нестроковыеname/regionи нечисловойnumeratorId.- Нечисловой
:idв путях сущностей с типизированным числовым id (GET/PATCH/DELETE /v1/quotes/abc,/v1/deals/1.5,/v1/leads/1e3) уходил в Битрикс24 как есть — в ответ прилетала непрозрачная ошибка B24 вместо внятного кода. У smart-processes12abcусекалсяparseInt-ом до12и попадал в ЧУЖОЙ тип.
Стало
- Чек-лист: родительская задача проверяется до создания пункта; несуществующая (или недоступная ключу) задача →
404 TASK_NOT_FOUND. - Склады и шаблоны документов: значение не того типа →
400 INVALID_PARAMSбез вызова Битрикс24 (числовая строка вnumeratorIdпо-прежнему принимается). - Сущности с явно типизированным числовым id: не-канонично-целый
:id→400 INVALID_PARAMSдо вызова Битрикс24 (id0— основная воронка сделокcategories— остаётся валидным). Smart-processes сохраняютINVALID_ENTITY_TYPE_IDи теперь отклоняют12abcна GET/PATCH/DELETE, а не усекают до12. Сущности, у которых тип id не задекларирован в схеме, сохраняют прежнее сквозное поведение.
Затронутые эндпоинты: POST /v1/tasks/:taskId/checklist, POST /v1/warehouses, POST /v1/doc-templates + GET/PATCH/DELETE по сущностям с типизированным числовым id.
Влияние на интеграторов
Если ваш код опирался на фантомный 201 от чек-листа или отправлял мусорные значения «на авось» — теперь придёт явный 4xx с кодом. Корректные вызовы не меняются.
FIX-0714-14: Пять тихих false-success/hint-дефектов: честный ответ вместо мнимого успеха
Было
PATCH /v1/userfields/{entity}/{id}clabel— тихий no-op на обновлении: ответ200, но подпись поля не менялась (Битрикс24crm.*.userfield.updateигнорируетLABEL).POST /v1/humanresources/nodes/{id}cparentId— ложный «перенос удался»:nameприменялся,parentIdмолча игнорировался, а в ответе эхом возвращался старый родитель.POST /v1/chats/messages/bulk— псевдонимdialogId: "me"не разрешался в bulk-цикле (одиночные роуты его разрешают) → сообщение уходило не туда.POST /v1/bots/{botId}/chats/{dialogId}/users— предохранительUSERS_NOT_ADDEDбыл мёртвым кодом (не совпадал с формой ответа метода v2) → неудачное добавление проходило как успех.- Ошибки
crm.item.listполучали нерелевантную подсказку «Maximum 50 records…» даже когда причина была иной (например «entity type does not exist»).
Стало
labelна обновлении разворачивается в реальные параметрыEDIT_FORM_LABEL/LIST_COLUMN_LABEL/LIST_FILTER_LABEL— подпись действительно меняется.parentIdиtypeтеперь только для создания: наPATCHони отклоняются с400(перенос — черезPOST /v1/humanresources/nodes/{id}/move), а не имитируют успех.- Bulk-чтение сообщений разрешает
dialogId: "me"для каждого элемента, как и одиночные роуты. - Предохранитель добавления в бот-чат снова срабатывает: непринятые пользователи возвращаются в
warning.USERS_NOT_ADDED. - Подсказки об известных ограничениях привязаны к релевантности сообщения ошибки, а не только к имени метода.
Затронутые эндпоинты:
PATCH /v1/userfields/{entity}/{id}PATCH /v1/humanresources/nodes/{id}POST /v1/chats/messages/bulkPOST /v1/bots/{botId}/chats/{dialogId}/users
FIX-0714-15: GET /v1/task-time теперь честно возвращает больше 50 записей при limit>50
Было
GET /v1/task-time с limit больше 50 возвращал только 50 записей, хотя meta.limit повторял запрошенное значение, а meta.hasMore мог вводить в заблуждение. Клиент, обходивший данные постранично с шагом больше 50, молча терял часть записей.
Стало
Запрос отдаёт до limit записей (максимум 500), собирая их постранично на стороне бэкенда; meta.total и meta.hasMore соответствуют фактически возвращённому окну. При limit больше 50 значение offset теперь указывает на правильную позицию, а не сдвигается на первые страницы.
NEW-0714-16: GET /v1/companies/fields и системные поля catalog-prices получили label и description
В ответе GET /v1/companies/fields теперь у всех полей компании есть человекочитаемые label и description. В GET /v1/catalog-prices/fields те же метки добавлены системным полям extraId, priceScale и timestampX. Метки приходят по-русски. Семантику поля можно получить программно из ответа, без сверки со статической документацией.
FIX-0714-17: /stop и /reboot различают отсутствующий сервер и неподходящий статус
Было
POST /v1/infra/servers/:id/stop и POST /v1/infra/servers/:id/reboot на сервере не в статусе running (например, спящем) отвечали плоским 404 NOT_FOUND с текстом «Running server not found» — по нему нельзя было понять, что сервер существует, и агент решал, что он удалён.
Стало
Оба маршрута ведут себя как /start и /wake: 404 SERVER_NOT_FOUND — только когда сервера с таким id действительно нет; 422 SERVER_WRONG_STATE — когда сервер есть, но не в статусе running. В error.currentState возвращается текущее состояние, в error.availableActions — доступные действия (wake/start/repair/delete).
FIX-0714-18: понятная ошибка на /fields у комментариев и учёта времени задач
Было
Запрос GET /v1/tasks/:taskId/comments/fields отвечал сбивающим с толку 400 INVALID_PARAMS с текстом «taskId and id must be positive integers» (спрашивали про поля — ответ про идентификатор), а GET /v1/tasks/:taskId/time/fields утекал сырой ошибкой Bitrix24 с внутренним PHP-методом и HTML-тегом.
Стало
Обе сущности распознают сегмент fields и возвращают понятный 400 WRONG_PATH: у них нет метода /fields (состав полей описан в справочнике /v1/guide), в тексте перечислены доступные маршруты. Нечисловой или дробный идентификатор в маршрутах по id теперь отклоняется как 400 INVALID_PARAMS до обращения к Bitrix24 — без утечки внутренней ошибки.
FIX-0714-19: Лимиты `/v1/ai/credentials*` считаются на портал; `CREDENTIAL_NOT_FOUND` подсказывает решение
Было
- Лимиты BYOK-маршрутов (
POST /v1/ai/credentials,/:id/test,/:id/fetch-models— 10/мин;/:id/modelsдобавление/удаление — 30/мин) фактически считались по IP-адресу: перебор чужих ключей обходился ротацией IP, а арендаторы за одним общим egress-IP делили один бакет. МаршрутPATCH /:id(при передачеcredentialsон проверяет ключ у апстрима — тот же оракул, что/:id/test) не имел лимита вовсе. 404 CREDENTIAL_NOT_FOUNDот/v1/searchи/v1/researchсодержал только слаг провайдера — без указания, как настроить ключ.
Стало
- Лимит считается на портал: все API-ключи одного портала делят единый бакет; ротация IP и число ключей на предел не влияют. При превышении —
429сRetry-After. ДополнительноPATCH /:id(проверяет ключ у апстрима, как/:id/test) раньше вовсе не имел лимита — теперь тоже 10/мин на портал. - В ответ
CREDENTIAL_NOT_FOUNDдобавлено полеhintс точным рецептом:POST /v1/search/credentials {provider, apiKey}, список провайдеров —GET /v1/search/providers.
FIX-0714-20: PATCH bizproc-шаблонов, роботов и активностей через /v1 применяет изменения
Было
PATCH /v1/bizproc-templates/:id, /v1/bizproc-robots/:code и /v1/bizproc-activities/:code с полями метаданных (name, description, autoExecute) возвращали 422 BITRIX_ERROR "No fields to update." — обновить сущность было нельзя (то же и в пакетных запросах). Поле autoExecute, переданное числом, дополнительно отвергалось как Incorrect field AUTO_EXECUTE!.
Стало
Поля корректно применяются (в том числе autoExecute, переданное числом); эндпоинт подтверждает успех и возвращает id обновлённой сущности. Работают и одиночный PATCH, и пакетные запросы. Создание (POST) не изменилось.
FIX-0714-21: Транскрипция: проверка кошелька до вызова распознавания
Было
POST /v1/audio/transcriptions (и /v1/ai/audio/transcriptions) не проверял состояние кошелька перед вызовом: PREPAY-счёт, ушедший за овердрафт (но ещё не замороженный фоновой проверкой), всё равно запускал распознавание и уводил баланс глубже в минус. Чат и эмбеддинги отклоняли такой вызов заранее; полностью замороженный счёт и раньше блокировался глобально (ACCOUNT_FROZEN).
Стало
Как у чата и эмбеддингов: денежная проверка выполняется до обращения к Whisper. Превышенный овердрафт → 402 insufficient_balance, распознавание не запускается. BYOK-ключи (USER-scope) бесплатны — для них проверки нет и поведение не меняется.
FIX-0714-22: удаление приложения больше не блокируется галактическим хостом на его ключе
Было
DELETE /v1/apps/:id возвращал 409 APP_HAS_ACTIVE_SERVERS, если на ключе приложения оказывался галактический хост (общая инфраструктура портала). Снять хост через удаление приложения было нельзя, а объяснения в ответе не было.
Стало
Галактический хост исключён из проверки блокирующих серверов: он управляется на уровне портала, а не ключа, поэтому не должен мешать удалению приложения. Отдельные серверы приложения (в том числе контейнеры приложений) по-прежнему блокируют удаление с 409 APP_HAS_ACTIVE_SERVERS — их сначала нужно перепривязать к другому ключу.
FIX-0714-23: OpenAPI: тело per-entity батч-эндпоинтов теперь описано верно (action + items/ids/calls)
Было
Спека (GET /v1/openapi.json) описывала тело каждого пер-сущностного батча как {create:[], update:[], delete:[]}. Рантайм же (общий обработчик батчей) требует {action, items|ids|calls} и отвечает 400 INVALID_BATCH_ACTION на документированную форму. Клиент, сгенерированный из спеки (codegen / AI-агент), получал 100% отказ batch-write на всех ~45 пер-сущностных батч-эндпоинтах. Сама фича работает — врала только спека (глобальный POST /v1/batch, /v1/tasks/{taskId}/comments/batch и /v1/guide уже описывали правильную форму).
Стало
Генератор спеки выдаёт правильную форму: один action (create/update/delete/list/get/fields); create/update шлют items, delete — ids, чтения — calls. Совпадает с рантаймом и с глобальным /v1/batch.
Влияние на интеграторов
Если вы генерировали клиент из openapi.json и batch-write падал INVALID_BATCH_ACTION — перегенерируйте: тело теперь {action:"create", items:[…]} вместо {create:[…]}. Handwritten-клиенты, уже славшие {action,…}, не затронуты.
FIX-0714-24: Валюты: fullName, формат и число знаков теперь сохраняются при плоской записи
Было
POST/PATCH /v1/currencies с плоскими fullName, formatString, decimals, decPoint, thousandsSep возвращал успех, но значения молча терялись — Битрикс24 хранит их в структуре локализации по языкам (LANG), а API слал их плоско. Задокументированный обходной путь «передавайте сырой LANG» тоже не работал: LANG — поле только для чтения, запрос отклонялся.
Стало
API упаковывает плоские локализуемые поля в локализацию вашего языка (языка API-ключа) перед вызовом Битрикс24, поэтому плоская запись сохраняется и читается обратно (POST {fullName:"…",decimals:3} → GET вернёт их). Работает на всех путях записи: одиночном, POST /v1/currencies/batch и глобальном POST /v1/batch. Сырой LANG в теле по-прежнему отклоняется как read-only. Разные значения сразу для нескольких языков через API пока не задаются. Язык записи определяется локалью API-ключа (ru или en) и может не совпадать с языком отображения валюты на портале — на порталах с иной локалью (de/pl/ua…) правка ляжет под en.
Влияние на интеграторов
Если вы обходили баг сырым LANG (и упирались в 400 READONLY_FIELD) — уберите его и шлите плоские поля. Уже работавшие плоские запросы теперь ещё и сохраняют значения.
FIX-0714-25: Aggregate уважает обязательные фильтры; infra-валидация не течёт сырым Zod
Было
POST /v1/<entity>/aggregateс сущностью, требующей фильтр (напр.catalog-productsтребуетiblockId), пропускал пустой запрос в Битрикс24 и возвращал сырой422, тогда как GET-list/search на том же условии отдают чистый400.POST /v1/infra/servers/:id/{deploy,exec,upload,logs}при ошибке валидации тела клал вerror.messageмногострочный JSON-сериализованный массив Zod-issues (сырой отпечаток валидатора).
Стало
- Aggregate проверяет обязательные фильтры/параметры до вызова Битрикс24: нет обязательного фильтра —
400 MISSING_REQUIRED_FILTER(напр.catalog-products→iblockId); нет обязательного list-параметра —400 MISSING_REQUIRED_PARAMS(напр.calendar-events→type,ownerId;humanresources-nodes→type). Как у list/search. - Infra-валидация форматирует ошибку компактно (
поле: сообщение; …). Код (VALIDATION_ERROR) и статус400не изменились.
Влияние на интеграторов
Если вы ловили сырой 422 от aggregate без фильтра — теперь придёт 400 MISSING_REQUIRED_FILTER. Если парсили infra-error.message как JSON — теперь это плоская строка поле: сообщение.
FIX-0714-26: Batch: работает per-entity batch для items и создание папок через batch
Было
POST /v1/items/{entityTypeId}/batchвозвращал404— per-entity batch-роут для сущностей с динамическим параметром (items,categories) монтировался по адресу без сегмента параметра (/v1/items/batch), поэтому документированный путь не находился, аentityTypeIdне доходил до команды Битрикс24. Обходной путь — глобальныйPOST /v1/batch— работал.POST /v1/folders/batchсaction: "create"падал на каждом элементе сERROR_ARGUMENT: batch-создание слалоfields[...], тогда какdisk.folder.addsubfolderожидает родительскую папку верхним параметромid, а остальные поля — подdata[...].
Стало
- Per-entity batch-роут для
items/categoriesмонтируется с сегментом:{entityTypeId}и прокидывает валидированныйentityTypeId(положительное целое; id выделенных API вродеdeals=2отклоняются с подсказкой — как в одиночных роутах) в каждую команду — для всех действий:create/update/deleteи read-действийlist/get/fields. - Batch-создание папок повторяет форму одиночного роута:
id=<родитель>&data[...]. ПропущенныйparentId— чистый per-item400до вызова Битрикс24.
Затронутые эндпоинты:
NEW-0714-27: восстановление зависшего exec-канала сервера
Новый эндпоинт POST /v1/infra/servers/:id/unstick принудительно освобождает залипший канал команд Black Hole-сервера, когда деплой или exec продолжает отвечать EXEC_BUSY («Another command is running») даже после DELETE /v1/infra/servers/:id/lock. Эндпоинт снимает блокировку на стороне платформы и разрывает туннель агента — при переподключении агент завершает зависшую команду и освобождает свой мьютекс. Перезагрузка сервера не выполняется.
Если в момент вызова на сервере ещё выполняется легитимная операция (деплой, exec, харден), эндпоинт по умолчанию отвечает 409 OPERATION_IN_PROGRESS и НЕ трогает её — расклинивать нужно только зависший канал. Повторите с ?force=true, если уверены, что канал команд действительно завис.
Ответ: { success: true, data: { backendLockReleased, agentBounced, reconnected } }. Коды ошибок: 404 SERVER_NOT_FOUND, 409 CONFLICT (восстановление уже идёт), 409 OPERATION_IN_PROGRESS (на сервере выполняется операция — повторите с ?force=true), 409 GALAXY_UNSTICK_UNSUPPORTED (для galaxy-хостов и galaxy-приложений не поддерживается), 502 GATEWAY_ERROR. Ошибка /exec (EXEC_BUSY) и провал /deploy (код DEPLOY_FAILED, сообщение «Another command is running») теперь несут подсказку hint с этим эндпоинтом.
NEW-0714-28: описание сервера в `PATCH /v1/infra/servers/:id`
Было
PATCH /v1/infra/servers/:id принимал только displayName. Поле description в контракте отсутствовало, а GET-ответы его не отдавали.
Стало
PATCH /v1/infra/servers/:id принимает опциональное поле description (строка, до 500 символов; пустая строка или null очищает описание; отсутствие поля оставляет текущее значение без изменений). Значение синхронизируется с карточкой приложения в каталоге. Поле description теперь возвращается в GET /v1/infra/servers, GET /v1/infra/servers/:id и в ответе PATCH. Старые запросы без description продолжают работать без изменений.
FIX-0714-29: деплой galaxy-приложения при обрыве связи во время сборки возвращает честную ошибку, а не ложный успех
Было
Если соединение с хостом обрывалось прямо во время сборки galaxy-приложения (частое явление под нагрузкой тяжёлой сборки), POST /v1/infra/servers/:id/deploy мог вернуть 200 со статусом running и пометкой [recovered] в buildLog, хотя новая версия так и не собралась и не поднялась — на хосте продолжал работать прежний контейнер. Повторный деплой упирался в тот же обрыв и снова рапортовал ложный успех.
Стало
Деплой считается восстановленным, только если он полностью завершился: под именем приложения запущен контейнер именно этой попытки, и деплой прошёл до конца. Если связь оборвалась во время сборки — или в любой момент до завершения деплоя — и новая версия не поднялась полностью, эндпоинт возвращает retryable-ошибку 502 с кодом GALAXY_HOST_UNREACHABLE и подсказкой «повторите тот же деплой, не удаляйте сервер» — вместо ложного 200. Только если деплой завершился полностью и потерялся лишь финальный ответ, восстановление в 200 работает как прежде.
BC-0714-30: переименование приложения в каталоге доходит до Битрикс24, publish потерял menuTitle
Поддержка старого формата до: 14.01.2027
Было
PATCH /v1/apps/:id с полем title у приложения, добавленного в каталог, возвращал 200, но не менял ничего из того, что видит пользователь: карточка каталога и привязки мест встраивания на портале (пункт левого меню, вкладки CRM) оставались со старым именем. Отказа не было никогда — вызов всегда успешен.
У POST /v1/apps/:id/publish тело не проверялось, а заголовок места встраивания задавался отдельным полем menuTitle.
Стало
Для приложения в каталоге title — это одна операция над отображаемым именем: имя синхронизируется в карточку каталога (вместе с title пишется catalogTitle) и перепривязывается в места встраивания на портале. Поэтому вызов, который раньше всегда отдавал 200, теперь может честно отказать:
400 NO_USER_TOKEN— приложение не авторизовано на портале, перепривязать места встраивания нечем;400 TITLE_TOO_LONG_FOR_CATALOG— имя приложения в каталоге ограничено 100 символами, тогда какtitleдопускает 255;502 BITRIX_PARTIAL_REBIND— Битрикс24 отклонил привязку. Имя в этом случае не записывается, повтор того же запроса чинит состояние.
Тело POST /v1/apps/:id/publish теперь валидируется, а параметр menuTitle удалён: заголовок места встраивания всегда равен отображаемому имени приложения. Пустое тело по-прежнему работает — публикация берёт значения из записи приложения.
Что делать интеграторам
- Убрать
menuTitleиз тела публикации: поле игнорируется, имя пункта меню задаётся черезtitleиcatalogTitle. - Держать имя приложения в каталоге в пределах 100 символов.
- Обработать отказы при переименовании: на
NO_USER_TOKENавторизовать приложение на портале, наBITRIX_PARTIAL_REBINDповторить запрос. - Учесть побочный эффект: переименование через
titleтеперь пишет иcatalogTitle— у приложения в каталоге эти поля держатся синхронными.
NEW-0714-31: GET /v1/tasks/:taskId/comments/fields — схема полей комментариев задачи
У комментариев к задаче появился метод /fields, как у остальных сущностей: GET /v1/tasks/:taskId/comments/fields возвращает статическую схему из 5 полей (id, taskId, authorId, message, createdAt) с типом, признаком «только для чтения», названием и описанием по-русски. Метод не обращается к Битрикс24. Раньше этот путь возвращал 400 WRONG_PATH — состав полей был доступен только из статических доков.
FIX-0714-32: пер-сущностный batch с action list теперь применяет filter
Было
POST /v1/{entity}/batch с action: "list" игнорировал filter: имена полей не приводились к именам Битрикс24 (например statusId у лидов не превращался в stageId), а операторы $gt / $contains / $in и другие не работали. Вызов возвращал 200 со всей таблицей — тихий отказ с неверными данными. Глобальный POST /v1/batch и POST /v1/{entity}/search при этом фильтровали корректно.
Стало
Пер-сущностный batch пропускает filter через тот же транслятор, что search и глобальный /v1/batch. Псевдонимы полей и операторы ($gt, $gte, $lt, $lte, $ne, $contains, $in, $nin, префиксные >=, <=, ! и т.д.) применяются. Некорректный filter (неизвестное поле у сущности с полной схемой, неподдерживаемый оператор, префиксы @ / !@, логические токены $or / $and) теперь возвращает 400 с указанием индекса вызова, а не молча всю выборку — так же, как на одиночных эндпоинтах.
FIX-0714-33: авто-пагинация больше не дублирует записи на границах страниц
Было
Для методов Битрикс24 без поддержки сортировки (например список объектов хранилища) запись на границе страниц могла сместиться между запросами соседних страниц и попасть в обе — при limit > 50 в выдаче появлялся дубль, занимавший слот, и клиент обрабатывал одну и ту же запись дважды.
Стало
После склейки всех страниц выдача дедуплицируется по id (сохраняется первое вхождение). Для методов со стабильной сортировкой ничего не меняется (дублей нет — операция вхолостую).
FIX-0714-34: список полей шаблона реквизитов больше не пустеет; создание не возвращает чужую запись
Было
GET /v1/requisite-presets/:presetId/fields мог вернуть 200 с пустым data: [], хотя в шаблоне есть поля: метод Битрикс24 отдаёт result то массивом [{…}], то объектом-картой {"0":{…},"1":{…}}, а обработчик принимал только массив. При создании поля (POST …/fields) ответ мог содержать ЧУЖУЮ существующую запись — эхо созданной строки читалось по id из ответа add, а на некоторых порталах чтение по этому id возвращало другое поле.
Стало
Список нормализует обе формы ответа Битрикс24 (массив и объект-карту) — поля больше не теряются. Эхо созданной записи возвращается только если её fieldName совпадает с тем, что создавали; при любом расхождении ответ содержит { id } (сам объект не подставляется), чтобы клиент не получил постороннюю строку.
Влияние на интеграторов
Идентификатор поля шаблона (id) — это позиционный идентификатор Битрикс24: он может меняться после операций записи в шаблоне и на части порталов не является устойчивым ключом. Не кэшируйте id между изменениями шаблона — перечитывайте список полей перед get/update/delete по конкретному полю.
FIX-0714-35: привязка плейсмента для ранее созданных приложений больше не отклоняется по обработчику
Было
POST /v1/placements/bind мог вернуть 400 с кодом PLATFORM_HANDLER_UNRESOLVABLE для приложения, созданного до перехода на единый платформенный обработчик (у такого приложения в качестве обработчика сохранился его собственный технический адрес). Привязка отклонялась даже тогда, когда платформенный обработчик /v1/bitrix-handler был доступен, — приложение нельзя было опубликовать заново через API.
Стало
Привязка проходит: обработчик плейсмента регистрируется на платформенный /v1/bitrix-handler, а в ответе появляются handlerRewritten: true и requestedHandler с исходным значением. Код PLATFORM_HANDLER_UNRESOLVABLE теперь возвращается только когда платформенный обработчик действительно недоступен. POST /v1/placements/unbind снимает такой плейсмент по тому же адресу.