# Изменения API: 22 августа 2026

[← Журнал изменений](/docs/changelog) · [Август 2026](/docs/changelog/2026-08)

### NEW-0822-1: промокод на занятом месте: предпросмотр отдаёт варианты, погашение их исполняет

**Было**

`POST /v1/cowork/coupon/preview` отвечал только тем, что даёт код, а на занятом месте погашение
отказывало: «у вас уже оплачен тариф» — и всё, сделать было нечего.

**Стало**

Предпросмотр возвращает поле `decision` — что произойдёт с этим местом и что можно предложить
человеку: `apply` (применить), `extend` (продлить срок того же тарифа), `choose` (выбор из
вариантов, у «применить сейчас» указано `losesDays` — сколько суток оплаченного срока сгорит) или
`refuse` с кодом отказа. Выбранная ветка передаётся в `POST /v1/cowork/coupon/redeem` новым
необязательным полем `action` (`apply` | `extend` | `force` | `resume-and-apply`); без поля
поведение прежнее.

Действие проверяется заново в момент погашения: если место успело измениться, ответ —
`409 COUPON_ACTION_NOT_AVAILABLE`, промокод при этом НЕ тратится. Правильная реакция — перечитать
предпросмотр и предложить варианты, которые вернулись сейчас. Незнакомое значение `action` отвечает
`400 INVALID_ACTION` — раньше такое тело давало `400 INVALID_CODE`, то есть указывало на поле кода,
которое в порядке.

### BC-0822-2: ответ на выпуск и перевыпуск ключа приведён к документированной форме

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

**Было**

`POST /v1/keys` и `POST /v1/keys/:id/rotate` на части порталов возвращали строку ключа
целиком, вычитая только секретные поля. Вместе с документированной формой в ответ
попадали внутренние поля платформы, которых нет ни в описании формы ключа, ни в
ответах `GET /v1/keys` и `GET /v1/keys/:id`: `preMigrationScopes`, `ownerActive`,
`deletedAt`, `isOAuthApp`, `appId`, `purpose`, `scopesAuthoritative`, `linkedServerId`,
`userAgentAutoModel`, `webhookScopesRepairedAt`, `tokenExpiresAt`.

**Стало**

Оба метода возвращают ровно документированную форму ключа — тот же набор полей, что и
`GET /v1/keys/:id`, плюс одноразовый `rawKey`. Перечисленные внутренние поля из ответа
убраны; ни одно из них не было описано в документации и не является частью контракта.

Если ваша интеграция читала любое из них, замените источник: состояние ключа — в
`status`, режим доступа — в `accessMode`, канал выписки — в `issuedVia`. Документированные
поля, сроки и коды ошибок не менялись.

### BC-0822-3: ротация ключа подчиняется подписке маркетплейса наравне с созданием

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

**Было**

На облачном портале без действующей подписки маркетплейса `POST /v1/keys/:id/rotate`
выписывал новый ключ, тогда как `POST /v1/keys` на том же портале отвечал отказом с
требованием подписки. Ротация оставалась обходным путём вокруг этого требования.

**Стало**

Оба метода отвечают одинаково. Без действующей подписки ротация возвращает `403` с
тем же кодом, что и создание: `B24_MARKET_SUBSCRIPTION_REQUIRED`, либо
`INT_TARIFF_REQUIRED` там, где доступ определяется платным тарифом, а не подпиской.

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

### NEW-0822-4: ключ сообщает, каким каналом он выписан, а отказ — точную причину

**Было**

`POST /v1/keys` и `POST /v1/keys/:id/rotate` не сообщали, каким каналом ключ выписан
на портале. На порталах, где входящий вебхук выдаёт только модуль платформы, оба
метода отвечали ошибкой выписки, даже когда сама выписка на портале работала.

**Стало**

Форма ключа несёт необязательное поле `issuedVia` — канал выписки; оно приходит в
ответах `POST /v1/keys`, `POST /v1/keys/:id/rotate`, `GET /v1/keys`,
`GET /v1/keys/:id` и `PATCH /v1/keys/:id`. Тело отказа получило необязательное поле
`error.reason` с точной причиной, а `error.code` у прежних отказов остался прежним.
Порталы, где ключи выдаёт модуль платформы, теперь обслуживаются обоими методами.

Ответ на создание и ротацию теперь одинаков независимо от канала выписки — раньше
набор полей у части каналов был уже. Поле `message` остаётся человекочитаемым и
может меняться: разбирайте отказ по `error.code`, а не по тексту.

### FIX-0822-5: отказ перевыпуска по правам приходит одинаково на любом аккаунте

**Было**

`POST /v1/keys/:id/rotate` у ключа, на котором из прав остались только права контекста
приложения — `placement`, `entity`, `userfieldtype`, — отвечал по-разному в зависимости от
того, каким каналом аккаунт выписывает ключи. Там, где ключ выдаёт модуль платформы,
приходил `400 PERSONAL_KEY_WEBHOOK_SCOPES_INVALID`, а на остальных аккаунтах — общий
`502 BITRIX_UNAVAILABLE` после неудачной попытки выписки.

**Стало**

Набор прав проверяется до обращения к аккаунту, поэтому отказ один и тот же везде —
`400 PERSONAL_KEY_WEBHOOK_SCOPES_INVALID`, тот же код, что у создания и обновления ключа.
Менять на клиенте нечего: отказ стал точнее и приходит раньше. Чтобы перевыпуск прошёл,
добавьте прежнему ключу право на данные через `PATCH /v1/keys/:id`.

### FIX-0822-7: Надёжная загрузка исходников при деплое на отдельную VM

**До:** `source.url` на отдельной виртуальной машине скачивала только сама клиентская VM. Кратковременные сетевые сбои обрывали скачивание, а клиент получал дословный текст сетевой ошибки агента.

**После:** для серверов, включённых в поэтапный rollout, платформа скачивает архив и передаёт его на VM потоком через туннель, выполняя до четырёх попыток с backoff в общем бюджете пяти минут. Совместимый VM-download fallback и публичный [`/upload`](/docs/infra/deploy/upload) в том же rollout получают ограниченные повторы, безопасную сетевую политику и стабильные сообщения; до включения сохраняется прежний контракт. Код `DEPLOY_FAILED` сохранён; ошибка шага `download` в новом режиме дополнена полями `causeCode`, `retryable` и `attempts`. Подробнее — в документации [деплоя](/docs/infra/deploy/deploy).

**Влияние:** менять запросы не нужно. Автоматизация может ветвиться по новым полям вместо разбора текста ошибки; архивы до 500 МБ передаются потоково. У публичного `/upload` в новом URL-режиме итог долгой фазы приходит под HTTP 200 с keepalive, поэтому успех или ошибку нужно определять по `success` и `error.code` в JSON; проверки до запуска и режим до включения сохраняют прежние HTTP-статусы.

### BC-0822-8: перевыпуск и возврат в работу ключей, выданных платформой, закрыты

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

**Было**

[POST /v1/keys/:id/rotate](/docs/management-keys) перевыпускал любой ключ владельца, а
[PATCH /v1/keys/:id](/docs/management-keys) возвращал любой ключ в `ACTIVE` и продлевал ему срок —
в том числе ключи, которые платформа выдаёт на своих отдельных эндпоинтах: ключ настольного
приложения Cowork/Code, ключ агента и проектный ключ для деплоя. В выдаче `GET /v1/keys` такой ключ
от личного почти не отличается, поэтому скрипт «перевыпустить или включить обратно все свои ключи»
доходил до него наравне с остальными: перевыпуск отвечал `201` со свежим `rawKey`, включение —
`200`, причём значение ключа при включении не менялось и снова начинало работать прежнее.

**Стало**

Обе операции над таким ключом отвечают `403`: перевыпуск — с кодом `SYSTEM_KEY_ROTATE_FORBIDDEN`,
возврат в `ACTIVE` и продление срока — с кодом `SYSTEM_KEY_REACTIVATE_FORBIDDEN`. В тексте ответа
сказано, где именно ключ этого класса выдаётся заново. Ключ в обоих случаях остаётся в прежнем
состоянии. Обе операции обходили проверки, которые стоят на выдаче каждого такого ключа: у ключей
с правом `vibe:cowork` — доступ к Cowork/Code, политику аккаунта «сторонние клиенты» и состояние
подписки, у проектного ключа для деплоя — три платформенных выключателя, требование ключа
Cowork/Code и активную подписку. Перевыпуск вдобавок выдавал бессрочное значение: у проектного
ключа для деплоя это снимало семидневный срок жизни, единственное, что ограничивает утёкший ключ.
Отзыв ключа и сокращение срока по-прежнему разрешены: это способ остановить ключ, а не выдать его.
Прежнее поведение снято сразу, без переходного срока.

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

Исключите из массового перевыпуска и массового включения ключи, которые выдаёт платформа: ключ
настольного приложения Cowork/Code, ключ агента и проектный ключ для деплоя. Первые два узнаются в
ответе `GET /v1/keys` по праву `vibe:cowork`; у проектного ключа для деплоя отдельного признака в
выдаче нет, поэтому `403` с этими кодами считайте окончательным ответом по ключу и не повторяйте
запрос. Ключ настольного приложения выдаётся заново подключением приложения Cowork/Code, ключ
агента — перевыпуском ключа агента в кабинете, проектный ключ для деплоя — повторным вызовом
`POST /v1/cowork/deploy-key`. Остальные ключи перевыпускаются и включаются как раньше.

### NEW-0822-9: POST /v1/cowork/deploy-key закрыт для ключа внешнего агента

**Было**

Ручка проверяла только право `vibe:cowork`.

**Стало**

У [POST /v1/cowork/deploy-key](/docs/cowork/deploy-key) ключ, выписанный для стороннего агента,
получает `403 COWORK_HARNESS_KEY_FORBIDDEN`. Такой ключ
живёт в чужом приложении, а выдача project-deploy ключа отзывает прежний и переносит на новый
серверы и приложения владельца. Десктопные и агентские ключи не затронуты.

### NEW-0822-10: Реактивация ключа подписки проверяет гейты выдачи

**Было**

Ключ, оплачиваемый подпиской, снова становился активным обычным изменением поля `status`,
а срок действия снимался значением `null`. Проверялась только необратимая блокировка, поэтому
отзыв ключа администратором Битрикс24 отменялся одним запросом, причём сам секрет не менялся.

**Стало**

[PATCH /v1/keys/:id](/docs/management-keys) отвечает `403` с кодом соответствующего гейта, если
запрос переводит ключ подписки в `ACTIVE` либо отодвигает или снимает `expiresAt`, а доступ к
Коворку, политика Битрикс24 «сторонние клиенты» или состояние подписки закрыты. Сужение срока,
отзыв и правка остальных полей работают как раньше. Обычные ключи не затронуты.

### NEW-0822-11: Ротация ключа подписки проверяет гейты выдачи

**Было**

Ротация переносила права и назначение ключа дословно, не спрашивая ничего.

**Стало**

[POST /v1/keys/:id/rotate](/docs/management-keys) для ключа, оплачиваемого подпиской, отвечает
`403` кодом соответствующего гейта, если закрыт доступ к Коворку, выключена политика портала
«сторонние клиенты» или подписка неактивна. Обычные ключи ротируются как раньше.

### NEW-0822-12: POST /v1/keys отклоняет тарификацию по подписке

**Было**

Поля `billing` в теле не существовало, неизвестный ключ отбрасывался схемой молча.

**Стало**

У [POST /v1/keys](/docs/management-keys) значение `billing: "subscription"` отклоняется кодом
`BILLING_MODE_NOT_SUPPORTED`. Ключи, оплачиваемые
подпиской Cowork/Code, выдаются только из кабинета платформы Вайбкод: там стоят проверки доступа,
политики портала и состояния подписки, которых у публичного маршрута нет. `billing: "wallet"`
и запросы без этого поля работают как раньше.

### FIX-0822-13: Отказ по неактивной подписке Cowork/Code ведёт на страницу подписки

**Было**

Отказ `402` с кодом `cowork_subscription_inactive` советовал активировать подписку вызовом
маршрута кабинета. Этот маршрут работает только в сессии кабинета, и клиенту,
который ходит с API-ключом, он недоступен. Сторонний агентный клиент показывает поле `message`
дословно, поэтому совет выглядел выполнимым, а выполнить его было нечем.

**Стало**

Тот же отказ на [POST /v1/chat/completions](/docs/ai/chat) называет состояние и место, где оно
меняется: подписку возобновляют в разделе Cowork/Code в кабинете платформы Вайбкод, ключ при
этом остаётся прежним. Код, статус и остальные поля тела не изменились.
