Для AI-агентов: markdown этой страницы — /docs-content/changelog/2026-09-16.md индекс документации — /llms.txt

Изменения API: 16 сентября 2026

← Журнал изменений · Сентябрь 2026

FIX-0916-1: партнёрским аккаунтам подняли бесплатные лимиты Коворка

Было

Бесплатное место Коворка считалось по общим окнам независимо от того, чей это аккаунт. GET /v1/cowork/me и GET /v1/cowork/state отдавали доли, посчитанные от общей сетки, а на исчерпании окна запрос к AI получал 402.

Стало

У партнёрского (NFR) аккаунта бесплатное место считается по своим окнам — они задаются на платформе и накладываются только вверх. Форма ответа прежняя: quotaPct и windows те же поля, но доли теперь считаются от партнёрского потолка, и 402 по исчерпанию приходит позже. Платное место партнёра не затронуто — оно считается по своему тарифу, и цена места не меняется.

Признак партнёрства платформа читает из лицензии аккаунта. У коробки он появляется после перерегистрации модуля Вайбкод на портале.

Влияние на интеграторов

Делать ничего не нужно: поля и статусы прежние, успешный ответ остаётся успешным. Если клиент держит у себя копию потолка, взятую из доли и расхода, её стоит пересчитывать по свежему ответу, а не кэшировать между сессиями.

FIX-0916-3: одновременная замена ключа приложения выдаёт ровно один ключ

Было

Ограничение «одна замена на приложение» держалось не всё время работы запроса, а лишь до того, как соседняя замена финишировала. Два одновременных вызова POST /v1/cowork/applications/{id}/key с РАЗНЫМИ значениями Idempotency-Key могли попасть в это окно и выдать по ключу: документированный отказ APPLICATION_KEY_REPLACE_IN_PROGRESS второму не приходил, слот карточки доставался тому, кто финишировал вторым, а ключ, выданный первому, молча переводился на суточный срок как заменённый — его владелец об этом не узнавал.

Стало

Ограничение действует всё время работы запроса. Тот, чья карточка успела уйти под чужую замену, получает 409 APPLICATION_KEY_REPLACE_IN_PROGRESS и ничего не чеканит — ровно тот отказ, который у метода уже описан. Ключ выдаётся один, и он же лежит в слоте.

Влияние на интеграторов

Менять ничего не нужно: код APPLICATION_KEY_REPLACE_IN_PROGRESS и совет по нему у метода прежние, новых кодов не добавилось. Изменилась частота: отказ приходит теперь и в той гонке, где раньше проскакивала вторая выдача. Обрабатывайте его как штатный исход параллельного вызова — прочитайте карточку приложения и посмотрите, нужен ли ещё один ключ. Если вы шлёте замену параллельно сами себе, надёжнее сериализовать вызовы у себя.

NEW-0916-4: детальная карточка сервера показывает порт, найденный агентом туннеля

GET /v1/infra/servers/:id в полной карточке сервера дополнительно возвращает detectedPort и detectedPortObservedAt: последний достоверно наблюдавшийся целевой порт туннеля и время наблюдения. До первого наблюдения оба поля равны null; неуспешное наблюдение не стирает прежнее. Сам GET не опрашивает машину, поэтому актуальность значения нужно оценивать вместе со временем и статусом подключения.

Влияние на интеграторов

Действий не требуется: новые поля аддитивны. Для диагностики фактической цели туннеля используйте detectedPort, а localPort продолжайте трактовать как настройку или fallback.

FIX-0916-5: код COWORK_HARNESS_DISABLED больше не приходит на ключах подписки Коворка

Было

PATCH /v1/keys/:id и POST /v1/keys/:id/rotate отвечали 403 с кодом COWORK_HARNESS_DISABLED, пока выдача ключей подписки для сторонних агентов была закрыта на платформе.

Стало

Этот код не приходит. Остальные проверки выдачи прежние: доступ владельца ключа к Коворку, разрешение администратора портала на сторонние клиенты и состояние подписки — каждая по-прежнему отвечает 403 со своим кодом, успешный ответ 200 остаётся успешным. Менять в интеграции ничего не нужно, обработку кода COWORK_HARNESS_DISABLED можно снять.

FIX-0916-6: Чтение объектов ограничивается отдельно для каждого портала

Было

GET /v1/storage/objects/:key и HEAD /v1/storage/objects/:key не ограничивали поток повторных запросов.

Стало

Каждый метод получил собственный бюджет 600 запросов в минуту на портал, общий для всех API-ключей этого портала. Обычные успешные ответы не изменились. После исчерпания бюджета API отвечает 429 RATE_LIMITED: действующее значение лимита приходит в x-ratelimit-limit, задержка перед повтором — в Retry-After.

Влияние на интеграторов

Менять обычные запросы не требуется. При 429 RATE_LIMITED выдерживайте Retry-After перед повтором.

FIX-0916-7: API показывает фактическое состояние туннеля

Было

После потери туннеля сервер мог оставаться CONNECTED в ответах GET и /refresh, поэтому клиент не видел действие repair, а /exec, /upload и /logs доходили до ошибки отсутствующего туннеля.

Стало

Список, карточка и refresh показывают фактическое состояние туннеля, включая DISCONNECTED и действие repair. Перед exec, upload, logs и deploy API проверяет доступность сервера. Отсутствие туннеля считается подтверждённым только по непустому актуальному снимку соединений; пустой или недоступный снимок сохраняет записанный статус. Exec, upload и deploy могут восстановить подтверждённо отсутствующий туннель, а GET logs возвращает 409 SERVER_NOT_READY с указанием явного POST /repair, не запуская ремонт. Успешные ответы и их HTTP 200 статус сохраняются.

Затронутые эндпоинты: GET /v1/infra/servers, GET /v1/infra/servers/:id, POST /v1/infra/servers/:id/refresh, POST /v1/infra/servers/:id/exec, POST /v1/infra/servers/:id/upload, POST /v1/infra/servers/:id/deploy, GET /v1/infra/servers/:id/logs.

Влияние на интеграторов

Считайте DISCONNECTED и repair актуальным состоянием. После 409 SERVER_NOT_READY повторяйте операцию с обычным backoff и учитывайте Retry-After, когда заголовок присутствует. Для GET logs сначала вызовите указанный в hint POST /repair: сам GET ремонт не запускает. Форматы ответов и успешный HTTP 200 не меняются.

NEW-0916-8: ключи выгодных часов и компенсаций Коворка приходят всегда

В GET /v1/cowork/me и GET /v1/cowork/state ключи приходят в каждом успешном ответе: offPeak (оба ответа), touSavedPct (state), relief (оба ответа), boostPct и boostExpiresAt (me). Проверку наличия этих ключей можно снять — ответ 200 по-прежнему успешен, значения внутри блоков прежние, и null внутри блока сохраняет свой отдельный смысл. currentWindowEndsInHours в GET /v1/off-peak приходит в каждом успешном ответе, как и прежде: от включения по аккаунтам он не зависел никогда.

offPeakHint в отказе 402 cowork_quota_exhausted на POST /v1/chat/completions больше не зависит от включения возможности для аккаунта, но по-прежнему приходит, только когда момент разблокировки попадает в час со скидкой. Проверка наличия этого ключа остаётся.

FIX-0916-9: хранилище: файл, загруженный повторно под тем же ключом, больше не числится несколькими объектами

Было

До 04.09.2026 повторная загрузка файла под тем же ключом через личный ключ разработчика или от имени сервера создавала ещё один объект вместо замены. В ответе GET /v1/storage/objects один файл повторялся несколько раз под разными идентификаторами, а GET /v1/storage/objects/{key} и DELETE /v1/storage/objects/{key} работали с одной из копий, поэтому после удаления файл продолжал числиться в списке.

Стало

Копии приватных файлов свёрнуты: у ключа остаётся один объект с идентификатором самой ранней неудалённой копии и метаданными последней загрузки (sizeBytes, sha256, contentType, contentUpdatedAt). Удаление по ключу срабатывает с первого раза. Байты файлов не менялись. Копии публичных файлов пока остаются на месте, чтобы выданные на них ссылки GET /v1/public-storage/{portalId}/{objectId} продолжали работать. Объекты, загруженные ключом приложения, не затронуты.

Влияние на интеграторов

Менять ничего не нужно. Приватный файл читается и удаляется по ключу, а ключ не изменился. Идентификаторы свёрнутых копий пропадают из листинга.

FIX-0916-10: будильник на сервере с режимом работы больше не отбивается как конфликт с круглосуточным

Было

POST /v1/infra/servers/{id}/wake-schedules отвечал 400 ALWAYS_ON_CONFLICT любому серверу, у которого не задан порог засыпания по простою, — в том числе машине, которой владелец уже назначил режим работы по расписанию. Добавить такой машине дополнительное пробуждение было невозможно.

Стало

Сервер в режиме работы «по расписанию» (SCHEDULE) круглосуточным не считается: сон у него объявлен владельцем явно, окнами расписания. Дополнительный будильник на такой машине создаётся обычным порядком, ответ 201 не изменился. Для машины без расписания — в том числе в режиме ALWAYS — поведение прежнее: 400 ALWAYS_ON_CONFLICT, как и раньше.

BC-0916-11: Однозначный тип сущности при получении CRM-документов

Поддержка старого формата до: не предусмотрена

Было

GET /v1/crm-documents принимал повторённый entityTypeId с ответом HTTP 200 и мог вернуть документы другой сущности. Одновременная передача entityTypeId и entityTypeID также принималась.

Стало

Повтор параметра, оба имени вместе и скобочные формы возвращают HTTP 400 INVALID_ENTITY_TYPE, даже если значения совпадают. Для одиночной скобочной формы код ошибки изменён с MISSING_PARAMS на INVALID_ENTITY_TYPE. Одиночное положительное целое под именем entityTypeId или entityTypeID работает как прежде.

Что делать интеграторам

Передавайте ровно один параметр entityTypeId или entityTypeID с одним положительным целым значением. Не используйте массивы или объекты для типа сущности. Кодируйте пользовательский ввод при построении строки запроса.

NEW-0916-12: у событий календаря появился include: owner, host, attendee

Событие календаря теперь объявляет три связи на сотрудников, и их можно затребовать параметром include у GET /v1/calendar-events и GET /v1/calendar-events/{id}: owner — владелец календаря (по полю ownerId), host — организатор встречи (по meetingHost), attendee — приглашённые (по attendeeList). Раньше связей у сущности не было объявлено ни одной, поэтому любое имя отвергалось с 400 INVALID_INCLUDE, а список допустимых имён в тексте отказа был пустым.

Запрос вида GET /v1/calendar-events/{id}?include=owner,host отвечает 200 и кладёт карточки сотрудников в _included. Запрос без include работает как прежде. Связь читает карточку сотрудника, поэтому ключ обязан нести право user — иначе запрос отвечает 403 SCOPE_DENIED.

Имя вне этого списка отвергается по-прежнему, но отказ теперь называет доступные связи: Unknown include 'section'. Available: owner, host, attendee. Секции календаря связью не объявлены намеренно: в Битрикс24 нет чтения секции по идентификатору, она доступна только списком.

NEW-0916-13: пользовательские поля сотрудников через /v1/userfields/users

Пользовательские поля сотрудника (сущность users, префикс имени UF_USR_) теперь заводятся и правятся через API Вайбкод. Раньше платформа умела только читать их: поле сотрудника уже приезжало в GET /v1/users/fields со своим типом и списком значений, а на попытку создать такое же поле POST /v1/userfields/users отвечал 400 UNKNOWN_ENTITY со списком из шести сущностей CRM.

Появились шесть маршрутов: GET /v1/userfields/users (список), GET /v1/userfields/users/{id} (одно поле), POST /v1/userfields/users (создание), PATCH /v1/userfields/users/{id} (правка), DELETE /v1/userfields/users/{id} (удаление). Нужен скоуп user.userfield — голого user для этой сущности недостаточно. Тело создания то же, что у полей CRM: fieldName, userTypeId, label и остальные свойства поля. Имя поля сотрудника несёт префикс UF_USR_ и передаётся целиком — платформа его не дописывает.

GET /v1/userfields/users/types отвечает 400 UNSUPPORTED_ACTION: у Битрикс24 нет метода, перечисляющего типы полей сотрудника, поэтому userTypeId при создании указывается явно. Прежнее поведение шести сущностей CRM на /v1/userfields/{entity} не изменилось ни в одном ответе.

Та же сущность появилась в MCP: тула manage_userfield принимает entity: "users".

FIX-0916-14: отказ агрегации объясняет ограничение чтения без пагинации

Было

В POST /v1/{entity}/aggregate сообщение AGGREGATION_LIMIT_EXCEEDED предлагало постраничную выгрузку даже для сущностей, которые этот путь агрегации читает без пагинации.

Стало

Для таких сущностей сообщение объясняет защиту от неограниченного чтения при числовой агрегации и группировке. Оно предлагает count без groupBy, не обещая отсутствие чтения данных при подсчёте. Статус HTTP 422, код ошибки и лимит 5000 записей сохранены.

Влияние на интеграторов

Изменился только текст error.message для этого случая. Обрабатывайте отказ по коду AGGREGATION_LIMIT_EXCEEDED.

BC-0916-15: previousKey в ответе замены ключа приложения

Поддержка старого формата до: не предусмотрена

Было

POST /v1/cowork/applications/{id}/key на ветке rotated возвращал previousKey с обязательной датой graceUntil — моментом, до которого прежний ключ ещё аутентифицируется.

Стало

previousKey несёт { id, graceUntil, status }. Поле graceUntil теперь может быть null, и ровно тогда status равен BLOCKED: прежний ключ был заблокирован, не работал ещё до замены, и отсрочки ему не выдавалось. У живого прежнего ключа всё как раньше — дата и status ACTIVE. Ответ остаётся HTTP 201.

Что делать интеграторам

Допустите null в graceUntil и печатайте текст по status: при BLOCKED прежний ключ уже не работает, ждать нечего, и приложению нужен новый секрет прямо сейчас. Код, читающий graceUntil как всегда присутствующую дату, на таком ответе сломается.

FIX-0916-16: приложение с заблокированным ключом чинится заменой

Было

POST /v1/cowork/applications/{id}/key отвечал 409 KEY_ROTATE_NOT_ACTIVE, если личный ключ приложения был заблокирован. Освободить место под ключ было нечем: удаление отбивалось живым сервером, а включить ключ обратно нельзя — блокировка терминальна. Приложение чинилось только пересозданием.

Стало

Замена проходит. Блокировка при этом не отменяется: прежняя строка остаётся заблокированной, отсрочки на сутки ей не выдаётся, а выписанные ею живые токены доступа гасятся, а не переезжают на новый ключ. Отказ KEY_ROTATE_NOT_ACTIVE теперь означает только отозванный или просроченный ключ. Тексты отказов замены переписаны: три из четырёх называют конкретный раздел кабинета и действие, а отказ по системному ключу раздела не называет — его в кабинете нет, — и вместо этого говорит, что делать: обратиться в поддержку. Если живые токены заблокированного ключа погасить не удалось, замена не выдаётся за успех: ответ — 500 KEY_ROTATE_REVOKE_FAILED с details.orphanKeyId (id выписанного, но никому не отданного ключа), секрет не выдан. Дальнейшее решает одно поле. details.strandedSlots нет — слоты вернулись на прежний ключ целиком: отзовите ключ из orphanKeyId и повторите с НОВЫМ Idempotency-Key. Поле есть — откат удался не целиком, перечисленные категории всё ещё указывают на осиротевший ключ: повторять НЕЛЬЗЯ, отзывать его тоже нельзя — нужна поддержка. Одна запись там не категория слота: previousKeyGrace означает, что прежний ключ, заблокированный уже посреди замены, сохранил выданный ротацией суточный срок и перестанет работать на этом дедлайне. Повтор здесь хуже, чем бесполезен: он заменит тот ключ, на который карточка указывает СЕЙЧАС, и может ответить 201 с рабочим секретом, а живые токены утёкшего ключа так и не будут погашены — инцидент останется открытым за экраном, который говорит «готово».

Влияние на интеграторов

Ответ на успешную замену не изменился, кроме previousKey (отдельная запись). Клиент, ветвившийся на 409 KEY_ROTATE_NOT_ACTIVE как на «ключ заблокирован», должен убрать эту ветку: заблокированный ключ теперь заменяется, а этот код означает только отозванный или просроченный.

FIX-0916-17: POST /v1/keys/{id}/rotate принимает заблокированный ключ

Было

Ротация заблокированного ключа отвечала 403 KEY_BLOCKED. Ключ, который удерживал живой сервер, не чинился ничем: удалить его не давал сервер, перевыпустить — эта проверка.

Стало

Ротация проходит на тех же условиях, что и замена ключа приложения: блокировка остаётся, прежней строке не выдаётся отсрочка, её живые токены доступа гасятся. Если погасить их не удалось, ротация не выдаётся за успех: эндпоинт отвечает 500 KEY_ROTATE_REVOKE_FAILED с details.orphanKeyId (id уже выписанного, но не отданного ключа), а сырой секрет в таком ответе не возвращается. Если откат ротации сам прошёл не до конца, к ответу добавляется details.strandedSlots — что осталось указывать на осиротевший ключ; при чистом откате этого поля в ответе нет. Одна запись там не слот: previousKeyGrace означает, что прежний ключ сохранил суточный срок, выданный ротацией (так бывает, когда ключ заблокировали уже посреди неё и вернуть прежний срок не удалось), — на этом дедлайне он перестанет работать. Разблокировка по-прежнему невозможна — PATCH заблокированного ключа отвечает 403 KEY_BLOCKED, как и раньше.

Влияние на интеграторов

Клиент, который раньше получал на заблокированном ключе только 403, теперь получает 201 либо 500 KEY_ROTATE_REVOKE_FAILED. Ветвитесь по details.strandedSlots: поля нет — откат прошёл целиком, отзовите осиротевший ключ из details.orphanKeyId и повторите вызов — Idempotency-Key эндпоинт не поддерживает, так что повтор это обычный новый запрос, и он попробует погасить токены заново. Поле есть — повторять НЕЛЬЗЯ: часть хозяйства осталась на осиротевшем ключе, и повтор заменит уже его, а живые токены утёкшего ключа так и не будут погашены — ответ может оказаться даже успешным, но компрометация останется. Отзывать осиротевший ключ тоже нельзя, пока на него что-то указывает, — нужна поддержка. Успешные ответы не изменились.

NEW-0916-19: каталог моделей: фильтр по возможности для ключей Коворка/Кода

GET /v1/models принимает необязательный параметр capability — явная выборка моделей по одной объявленной возможности вместо перебора полного списка на стороне клиента.

Параметр читается только у ключа со скоупом vibe:cowork. У остальных ключей он игнорируется: ответ остаётся прежним при любом значении, включая неизвестное. Набор допустимых значений закрыт и расширяется вместе с возможностями, которые платформа обслуживает для Коворка/Кода.

Ответ без параметра не изменился ни для одного ключа.

BC-0916-20: название приложения из одних пробелов отклоняется на всех путях записи

Поддержка старого формата до: не предусмотрена

Было

Название, состоящее только из пробелов, табуляций или переводов строки (например " "), проходило проверку «не короче одного символа». Приложение создавалось или переименовывалось, а его пункт меню в Битрикс24 получал пустое название.

Стало

Такое значение отклоняется с 400 VALIDATION_ERROR до обращения к Битрикс24 — во всех запросах, где задаётся отображаемое имя приложения или подписи встройки:

Название, в котором есть хотя бы один видимый символ, принимается как раньше и сохраняется без изменений, включая пробелы по краям. На уже сохранённые значения правило не распространяется: публикация приложения со старым пустым названием проходит как прежде.

Что делать интеграторам

Передавайте в title и catalogTitle название хотя бы с одним видимым символом. Если название формируется автоматически и может оказаться пустым, подставьте осмысленное значение по умолчанию до отправки запроса. Особое внимание — на сценарии переименования и повторной публикации: раньше они пустое название принимали.

FIX-0916-21: вызов на прежний адрес переехавшего портала отбивается названным отказом

Было

Портал коробки переехал на новый адрес, а вебхук ключа остался выписан на прежний. Вызов отбивался защитой (секрет не уходит туда, где портала больше нет), но клиент получал 500 с телом {"success":false,"error":{"code":"INTERNAL_ERROR","message":"Internal server error"}} — ни причины, ни подсказки, что делать.

Стало

Тот же вызов отвечает 409:

JSON
{"success":false,"error":{"code":"PORTAL_ADDRESS_CHANGED","message":"Portal address changed: this credential is issued for the portal's previous address. Re-issue the key webhook to continue"}}

Действие то же, что и раньше: перевыпустить вебхук ключа («Ключи» → «Перевыпустить»). Ключ, его строка и права не меняются.