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

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

### NEW-0811-1: тарифы серверов называют единицу цены

В ответе [GET /v1/infra/providers/{providerId}/plans](/docs/infra/providers/plans) у каждого тарифа появилось поле `currency` со значением `"Vibes"` — единица, в которой считаются `priceMonthly` и `sleepPriceMonthly`. Раньше цены приходили безымянными числами, и единицу приходилось брать из текста документации; теперь это та же машинная пометка, что у стоимости поиска в `GET /v1/me`.

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

### NEW-0811-2: каталог данных приложения объявляется в теле деплоя

Приложение на отдельной виртуальной машине запускается под непривилегированной учётной записью, и платформа передавала ей во владение только каталог распаковки. Каталог состояния вне него — тот самый `/opt/data`, который наша документация советует для данных, переживающих выкат, — создаётся администраторскими шагами `install` и `preStart`, поэтому оставался за администратором, и первая же запись из приложения падала с ошибкой доступа. Обойти это можно было только ручной раздачей прав в `preStart` на каждом деплое.

**Было**

Приложение писало только в свой каталог распаковки. Для каталога состояния декларативного способа не было.

**Стало**

В теле [POST /v1/infra/servers/{id}/deploy](/docs/infra/deploy/deploy) появились два необязательных поля. `dataDirs` — до восьми каталогов вне каталога распаковки, которые платформа создаёт и передаёт учётной записи приложения на каждом деплое, а при откате возвращает обратно. Путь — абсолютный, нормализованный, внутри `/opt`, `/srv` или `/var/lib`; сами корни отвергаются новым кодом `INVALID_DATA_DIRS` до того, как деплой займёт сервер. `dataDirsRecursive` дополнительно передаёт содержимое объявленных каталогов — нужно только для заранее развёрнутого дерева; для `/opt/data` не принимается, потому что там по нашему же рецепту восстановления базы лежит файл с паролем.

Передаётся сам каталог, а не его содержимое: файлы, положенные администратором раньше, владельца не меняют. Владелец каталога может удалять и заменять файлы внутри него, поэтому скрипты, запускаемые от администратора, и учётные данные держите в необъявленном каталоге.

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

### NEW-0811-3: выпуск ключа ровно с выбранными платформенными правами

Тело `POST /v1/keys` принимает необязательное поле `exactScopes`. С `exactScopes: true` ключ сохраняет ровно те права, что перечислены в `scopes`: четыре платформенных (`vibe:infra`, `vibe:ai`, `vibe:search`, `vibe:storage`) не дописываются при выпуске, и `vibe:ai` / `vibe:search` не добавляются к правам запроса на лету. Поэтому `GET /v1/me` возвращает ровно сохранённый набор, а ключ без `vibe:infra` отвечает `403 INFRA_SCOPE_REQUIRED` на `POST /v1/infra/servers`.

Умолчание не изменилось: без поля к запрошенным правам по-прежнему добавляются четыре платформенных, поэтому уже написанные скрипты работают как раньше.

Ключи, выпущенные в кабинете, теперь тоже точные — снятая галочка платформенного права означает, что права у ключа нет. Раньше набор Вайбкод дописывался безусловно, и сузить права можно было только правкой ключа после выпуска.

### NEW-0811-4: выгодные часы видны в подписке Cowork/Code и в отказе по исчерпанной квоте

В отдельные часы недели квота расходуется медленнее — тот же вызов забирает меньшую долю лимита. Раньше об этом можно было узнать только из расписания выгодных часов, теперь то же самое видно в ответах подписки Cowork/Code и в отказе по исчерпанной квоте, поэтому приложение может предложить перенести объёмную задачу на выгодный час, не собирая расписание само.

| Ответ | Что появилось |
|---|---|
| [GET /v1/off-peak](/docs/ai/consumption/off-peak) | `currentWindowEndsInHours` — через сколько часов расход перестанет быть таким же выгодным. `null`, если в пределах недели вперёд дороже не станет |
| [GET /v1/cowork/me](/docs/cowork/me) | Блок `offPeak` — действует ли скидка сейчас, какой множитель расхода и когда наступит ближайший выгодный час. Без сетки часов |
| [GET /v1/cowork/state](/docs/cowork/state) | Тот же блок `offPeak` вместе с сеткой часов на неделю и поле `touSavedPct` — какую долю расхода за текущий расчётный период сняли выгодные часы |
| Отказ 402 `cowork_quota_exhausted` на [POST /v1/chat/completions](/docs/ai/chat/completions) | `offPeakHint` — через сколько часов снимется блокировка (`inHours`) и какой множитель расхода будет действовать в этот момент (`multiplier`). Приходит, только когда этот момент попадает в час со скидкой |

Все перечисленные ключи необязательные и приходят, когда возможность включена для аккаунта. Включается она по очереди, и пока она не включена, ключей в теле ответа нет вовсе — со значением `null` они не приходят. Проверяйте наличие ключа, а не его значение.

Множитель — коэффициент расхода, а не размер скидки. Значение `0.5` означает, что вызов забирает половину той доли квоты, которую забрал бы без скидки.

Поля блока, примеры ответов и особенности — [Выгодные часы в подписке Cowork/Code](/docs/cowork/off-peak).

### FIX-0811-5: ближайший выгодный час отсчитывается от границы часа, а не от минуты запроса

**Было**

Поле `nextWindow.inHours` в ответе [GET /v1/off-peak](/docs/ai/consumption/off-peak) отсчитывалось от момента запроса. Расписание почасовое, поэтому ответ «через 2 часа», полученный в 10:55, указывал на 12:55 — на середину выгодного часа, начавшегося в 12:00. Клиент, прибавивший это число к моменту запроса, попадал в дешёвый час, когда большая его часть была уже позади.

**Стало**

Отсчёт идёт от границы текущего часа в часовом поясе расписания. То же «через 2 часа», полученное в любую минуту между 10:00 и 11:00, указывает на 12:00 — на начало выгодного часа. Поле стало тем, чем его описывает документация, — ближайшим часом, который дешевле текущего. Расхождение с прежним прочтением не превышает одного часа.

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

Менять ничего не нужно, форма ответа и тип поля прежние. Планировщик, который прибавляет `inHours` к моменту запроса, теперь стартует в начале выгодного часа, а не в его середине, и момент старта может сдвинуться не больше чем на час.

### BC-0811-6: машина, у которой не загружается операционная система, отвечает отдельным кодом

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

**Было**

Сервер, чья гостевая операционная система не стартует после прерванного обновления, выглядел как
обычный «временно не на связи». Ремонт по нему запускался снова и снова и каждый раз возвращал
сообщение про SSH-ключ, к которому поломка отношения не имеет, а `POST /v1/infra/servers/:id/start`
отвечал `200` и поднимал машину, которая всё равно не загрузится. Отличить безнадёжную машину от
временно недоступной по ответам API было нельзя.

**Стало**

Такая машина получает машинную метку `provisionErrorCode = "GUEST_NOT_BOOTING"` в ответах
[GET /v1/infra/servers/:id](/docs/infra/servers) и списка серверов. `POST /v1/infra/servers/:id/repair`
и `POST /v1/infra/servers/:id/start` отвечают `422` с кодом `GUEST_NOT_BOOTING` вместо заведомо
бесполезной работы, в `availableActions` остаётся только `delete`, а [GET /v1/me](/docs/quickstart) в блоке
`infra.unhealthyServers` советует пересоздание вместо ремонта. Начисление за такую машину закрыто в
момент вердикта.

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

Проверяйте `provisionErrorCode` перед запуском и ремонтом: значение `GUEST_NOT_BOOTING` терминально,
повтор не поможет, сервер нужно пересоздать. Клиент, который вызывает `start` по расписанию,
получит на такой машине `422` вместо прежнего `200` — обработайте этот код как «пересоздать», а не
как временную ошибку. Прежнее поведение не сохраняется: `200` означал запуск машины, которая всё
равно не загружается, и продолжал начисление за неё.

### NEW-0811-7: у ответа «приложение не отвечает» появился отдельный код для сервера без выкладки

**Было**

Когда туннель до сервера открыт, а приложение не отвечает, машинному вызывающему всегда приходил
один и тот же ответ `503` с кодом `BH_APP_STARTING` и заголовком `Retry-After`. Сервер, на который
выкладки никогда не было, был неотличим от сервера, где приложение упало или слушает не тот порт,
и повтор по таймеру выглядел осмысленным там, где ждать было нечего.

**Стало**

Если платформа не видела ни одной выкладки на этот сервер, ответ приходит с кодом
`BH_APP_NOT_DEPLOYED` и без заголовка `Retry-After` — повторять по таймеру бессмысленно, нужна
выкладка либо проверка адреса. Прежний `BH_APP_STARTING` с `Retry-After` остаётся для случая, когда
выкладка была: там приложение действительно может подняться само. Статус ответа в обоих случаях
`503`, поэтому обработка, не разбирающая код, работает как раньше.

### FIX-0811-8: создание шаблона документа объявляет требуемый скоуп в openapi.json

**Было**

У операции [POST /v1/doc-templates](/docs/entities/doc-templates/create) в машинной спецификации не было поля `x-required-scope`, хотя рантайм отвечает `403 SCOPE_DENIED` без скоупа `documentgenerator`. Клиент или агент, собиравший ключ по спецификации, видел «скоуп не нужен» и получал отказ на первом же вызове. Эта же операция попадала в любой срез `openapi.json?scope=`, включая срезы других модулей.

**Стало**

Операция объявляет `x-required-scope: documentgenerator`, как остальные операции модуля. В срезах она остаётся только при `openapi.json?scope=documentgenerator` и в полной спецификации.

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

Ничего менять не нужно: поведение самого эндпоинта не изменилось. Клиенты, которые выводят набор скоупов из спецификации, теперь запросят `documentgenerator` сразу и не упрутся в `403`.

### FIX-0811-9: Пользовательские поля реквизитов в ответах

**Было**

`crm.requisite.get` не возвращал пользовательские поля реквизита в ответах V1.

**Стало**

После успешного `crm.requisite.get` сервис точечно добирает пользовательские поля реквизита и сохраняет успешный ответ, если этот дополнительный вызов недоступен.

### NEW-0811-10: granted_scopes — фактические права выданного ключа в ответе обмена

[POST /v1/connect/token](/docs/partner-connect) отдаёт новое поле `granted_scopes` — набор прав, который реально несёт выданный API-ключ. Поле `scopes` не изменилось: там по-прежнему то, что приложение запросило при авторизации. Обычно наборы совпадают, но у приложений, которым права проставляет платформа, они расходятся — например при входе по коду устройства настольное приложение Cowork не просит ничего, поэтому `scopes` приходит пустым, а `granted_scopes` перечисляет весь набор ключа. Поле необязательное и может отсутствовать, если платформа не смогла прочитать права выданного ключа. Пустым оно не приходит, поэтому отсутствие означает «набор неизвестен», а не «прав нет». Прежние вызовы работают без изменений.

### FIX-0811-11: OpenAPI: обязательные скоупы для bespoke-операций

**Было**
Машинный OpenAPI-контракт не содержал x-required-scope для bespoke-операций, хотя их обработчики уже проверяли скоуп до вызова Bitrix24.

**Стало**
Контракт публикует проверяемый обработчиком скоуп для операций CRM, bots, calls, chats, lists, note, notifications, posts, scrum, tasks, timelines, userfields, warehouses, workday и workflows. Прокси скачивания с двумя допустимыми скоупами оставлен без одиночной аннотации, потому что она была бы неточной.

### FIX-0811-12: справочник /v1/guide отдаёт ссылку на документацию для сущности telephony-lines

**Было**

В ответе `GET /v1/guide` сущность `telephony-lines` приходила без поля `docs`. Страница с описанием линий существовала, но найти её по ответу справочника было нельзя.

**Стало**

Сущность `telephony-lines` содержит `docs` со ссылкой на раздел телефонии `/docs/telephony/lines`. Остальные поля ответа не изменились, действий на стороне клиента не требуется.

### FIX-0811-13: запись с Content-Type: text/plain теперь отклоняется, а не создаёт пустую запись

**Было**

Запрос на создание или изменение (POST/PATCH) с заголовком `Content-Type: text/plain` и телом, не являющимся JSON, принимался: тело молча отбрасывалось, а запись всё равно выполнялась — POST создавал пустую сущность с кодом 201, PATCH тихо ничего не менял. Любой другой не-JSON тип уже отвечал 415.

**Стало**

Такой запрос отвечает `415 Unsupported Media Type`, как и прочие не-JSON типы; пустая запись не создаётся. Отправляйте тело как `application/json`.

### BC-0811-14: деплой не рапортует успех, когда порт остался за прежним процессом

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

**Было**

Деплой на [POST /v1/infra/servers/{id}/deploy](/docs/infra/deploy/deploy) мог ответить `success: true` и `healthcheck: ok`, хотя порт приложения остался за процессом, который занимал его ещё до начала деплоя. Проверка живости получала `200` от него, а не от нового приложения, поэтому по публичному адресу продолжала отдаваться прежняя версия. Шаг `stop_existing` при этом честно предупреждал, что владелец порта не связан с новым приложением, но деплой шёл дальше и завершался успехом.

**Стало**

Если проверка живости получает `200`, а порт при этом занят процессом, который был на нём ещё до начала этого деплоя, шаг `healthcheck` завершается ошибкой. В тексте ошибки указан владелец порта. Ответ становится `success: false`, у шага — `status: "error"`, в потоковом режиме поток останавливается на том же шаге.

Деплои, где порт публикует процесс, запущенный самим этим деплоем, по-прежнему завершаются успехом — в том числе когда его поднимает шаг `install` или `preStart`, то есть до старта сервиса. Не меняется и случай, когда владельца порта определить не удалось: такой деплой, как и раньше, считается успешным.

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

Проверьте, не держит ли порт приложения процесс, который переживает деплои: обратный прокси, менеджер процессов, а также контейнер или systemd-юнит, поднятый один раз из шага `install` или `preStart` и не пересоздаваемый при следующих деплоях. Такая схема раньше отвечала `success: true`, а теперь будет отвечать `success: false`: платформа не может подтвердить, что по этому адресу отвечает именно новая версия. Отдайте порт приложения самому приложению, а свой процесс переведите на другой порт. Всем остальным менять ничего не нужно: форма ответа прежняя, а сценарий, который раньше получал `success: true` при неподнявшейся новой версии, теперь получит `success: false` с причиной.

### NEW-0811-15: предрасчёт стоимости смены тарифа Cowork/Code

`GET /v1/cowork/subscription/preview?tier=<FREE|PRO|MAX|ULTRA>` возвращает сумму, которая будет списана прямо сейчас при переходе на указанный тариф: полную цену операции, возврат за неиспользованный остаток оплаченного месяца и итог к списанию. Скоуп `vibe:cowork`, ответ не кэшируется, ограничение — 30 запросов в минуту на пару «аккаунт и пользователь».

Экран подтверждения стоит строить на этом ответе, а не на цене тарифа из `tiers[].feeVibes` в `GET /v1/cowork/state`: цена там — ценник, и сумма списания уже сегодня отличается от него в четырёх случаях. Повторный выбор тарифа, который уже стоит, списывает ноль; понижение, поставленное в очередь на конец оплаченного периода, сейчас не списывает ничего; повторный запрос про такое понижение — тоже ноль; повышение с зачётом остатка месяца списывает цену за вычетом возврата. Пара `netVibes: "0"` и `scheduled: true` прямо отвечает на вопрос «спишется ли сейчас», поэтому эти правила не нужно повторять в клиенте.

Вайбы приходят десятичной строкой в основных единицах — так же, как баланс и движения по счёту. Рядом идут `currency` (код валюты пополнения по ISO 4217 либо `null`) и `topUpAvailable`; это про пополнение кошелька, а не про цену тарифа — денежной цены тарифа и курса вайба к деньгам ответ не содержит.

### FIX-0811-16: сообщение об отказе установки приложения называет ответ портала

**Было**

Когда Битрикс24 отказывал в установке приложения по ключу разработчика и причина
не попадала ни в один распознаваемый код, ответ нёс `DEVKEY_MINT_FAILED` и общий
текст `Failed to install app via developer key`. По нему нельзя было отличить
отказ по правам от недоступности REST или сетевого сбоя.

**Стало**

К тому же тексту дописывается наблюдаемое: `Failed to install app via developer
key (Bitrix24 answered HTTP 403 BITRIX_REST_V3_EXCEPTION_ACCESSDENIEDEXCEPTION)`.
Код ошибки (`error.code`), статус ответа и поле `userMessage` не изменились, так
что разбор ответа по коду продолжает работать без правок. У отказов с
распознанной причиной — подписка, тариф, устаревший ключ — текст прежний.

Ответ портала дописывается только когда он был: на сетевом сбое, когда портал не
ответил вовсе, текст остаётся прежним. Код отказа дописывается в машинном виде и
не длиннее 64 символов.

### FIX-0811-17: чтение с ключом только для чтения не отклоняется как запись

**Было**

Ключ только для чтения мог получить `403 WRITE_BLOCKED_READONLY_KEY` при запросах, которые читают данные через методы Битрикс24 `timeman.status`, `timeman.settings`, `calendar.event.getbyid`, `bizproc.workflow.instances`, `lists.get.iblock.type.id`, `lists.element.get.file.url`, `crm.type.getByEntityTypeId` и `crm.activity.call.getTranscript`.

**Стало**

Ключ только для чтения пропускает эти операции чтения. Неизвестные методы по-прежнему считаются записывающими и блокируются.

### BC-0811-18: отметка «прочитано» требует ключа с правом записи

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

**Было**

Ключ в режиме [«только чтение»](/docs/keys-auth) мог отметить уведомления и сообщения прочитанными: [POST /v1/notifications/read](/docs/notifications/read), [POST /v1/chats/:dialogId/read](/docs/chats/messages/read) и [POST /v1/bots/:botId/chats/:dialogId/read](/docs/bots/messages/read) выполнялись и меняли состояние портала — счётчик непрочитанного, статусы уведомлений. Проходили они потому, что право на запись определяется по имени вызываемого метода Битрикс24, а эти имена оканчиваются словом «read» и читались как чтение.

**Стало**

Все три операции под ключом «только чтение» отвечают `403 WRITE_BLOCKED_READONLY_KEY`. Код добавлен в справочник ошибок каждой из трёх страниц. Ключ с правом записи работает как раньше.

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

Если сценарий отмечает прочитанным — выпишите или переключите ключ в режим с правом записи в разделе ключей кабинета. Чтение уведомлений и сообщений под ключом «только чтение» не изменилось. Подписка на события чатов (`POST /v1/chats/events/subscribe` и `POST /v1/chats/events/unsubscribe`) ключу «только чтение» по-прежнему доступна — это осознанное исключение, без которого агент в режиме чтения не смог бы следить за событиями.

### FIX-0811-19: ещё пять читающих операций перестали отклоняться под ключом только для чтения

**Было**

Запись FIX-0811-17 сняла отказ у восьми читающих методов, но того же класса осталось ещё пять операций: `GET /v1/tasks/:taskId/chat/messages`, `GET /v1/humanresources/nodes/:id/children`, `GET /v1/humanresources/employees/:id/subordinates`, `GET /v1/mail/messages/:id/thread` и `GET /v1/mail/mailboxes/:id/senders`. Ключ в режиме [«только чтение»](/docs/keys-auth) отвечал на них `403 WRITE_BLOCKED_READONLY_KEY`: право на запись определяется по имени вызываемого метода Битрикс24, а у этих операций оно оканчивается общим словом, и незнакомое имя трактовалось как запись. Ни одно обращение по ним не приходило — расхождение нашлось сверкой всего списка методов с их адресами.

**Стало**

Все пять отвечают ключу «только чтение» как обычное чтение. Отказ `403 WRITE_BLOCKED_READONLY_KEY` остался на операциях записи в тех же разделах: отправка письма, правка оргструктуры, отправка сообщения в чат задачи.

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

Менять ничего не нужно: запросы, которые раньше отклонялись, теперь выполняются. Если ради этих операций вы выписывали ключ с правом записи — верните ему режим «только чтение».

### NEW-0811-20: состояние Cowork/Code рассказывает, как аккаунту открыть работу с Битрикс24

В ответе [GET /v1/cowork/state](/docs/cowork/state) появился блок `activation`. Он говорит, по какой модели работает регион аккаунта (`model`: `subscription` или `tariff`), даёт ссылку на условия тарифов Битрикс24 (`tariffInfoUrl` — только при `model: tariff` и только для облачного аккаунта) и заранее отвечает, стоит ли предлагать включение пробного периода Маркета: `marketTrial.available` и `marketTrial.unavailableReason` (`trial_already_activated`, `subscription_active`, `demo_used`, `region_not_supported`, `not_cloud`, `portal_state`, `not_supported`), плюс журнал наших попыток — `status`, `endsAt`, `activatedAt`.

Признак считается до попытки, поэтому шаг включения можно не показывать там, где он заведомо не сработает. Значение `false` окончательно, значение `true` означает «предлагать можно», но не гарантирует успех: состояние аккаунта может измениться между запросом и нажатием, поэтому обработку отказа на самом включении оставьте. Список причин может пополняться — неизвестное значение читайте как «активацию не предлагать». Самого блока `activation` в ответе может не быть вовсе: так отвечает платформа, которая про него ещё не знает, и это штатное состояние — проверяйте наличие блока, а внутри него читайте значение поля.

### FIX-0811-21: у состояния Cowork/Code появился предел частоты опроса

**Было**

[GET /v1/cowork/state](/docs/cowork/state) принимал запросы без ограничения частоты, хотя документация рекомендует опрашивать его раз в 15–30 секунд.

**Стало**

Предел есть: он общий на аккаунт и владельца ключа, то есть все устройства одного человека делят его. Сверх предела эндпоинт отвечает `429 RATE_LIMITED`.

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

Клиент, соблюдающий рекомендованный интервал, предела не заметит: он расходует около двух запросов в минуту, а запас до потолка кратный. Если ваш опрос чаще, разредите его или обработайте `429`.

### FIX-0811-22: ссылка на повышение в ответе о возможностях больше не ведёт в никуда

**Было**

В [GET /v1/me](/docs/keys-auth/me) при отказе на создание сервера поле `capabilities.servers.create.alternatives[].url` (и тот же адрес в тексте `userMessage`) вело на страницу лицензии внутри самого аккаунта. Этот адрес различается между редакциями и версиями Битрикс24 и на части аккаунтов отдавал 404 — платформа отказалась от него в остальных ответах ещё в апреле, а здесь он остался.

**Стало**

Приходит устойчивый адрес: касса аккаунта там, где доступ открывается подпиской, и общая страница условий тарифов Битрикс24 там, где доступ тарифный. Заодно текст «оформите подписку» больше не показывается регионам, где подписки нет как продукта, — им остаётся общая формулировка и ссылка на условия тарифов.

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

Ничего менять не нужно: поле осталось на месте и по-прежнему может отсутствовать. Не сохраняйте значение у себя и не разбирайте его домен — это адрес платформы, а не вашего аккаунта.

### FIX-0811-23: пауза перед повтором в отказе ai_congested зависит от настроенной базы и ограничена сверху

**Было**

Когда платформа ограничивала поток AI-запросов, [POST /v1/chat/completions](/docs/ai/chat/completions),
[POST /v1/embeddings](/docs/ai/embeddings) и [POST /v1/audio/transcriptions](/docs/ai/audio/transcriptions)
отвечали `429` с кодом `ai_congested`, заголовком `Retry-After` и полем `retryAfter` в теле. Названная пауза
почти не разбрасывалась — соседние отказы приходили с близкими значениями, — и верхнего предела у неё не было.

**Стало**

Пауза разбрасывается шире и может прийти **длиннее** прежней; сверху она ограничена — не больше часа.
Насколько именно — зависит от того, как платформа ограничивает поток в этот момент, поэтому единственное
верное поведение прежнее: ждать ровно столько, сколько названо в ответе.

Заголовок `Retry-After` и поле `retryAfter` в теле по-прежнему несут одно и то же число.

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

Менять ничего не нужно: форма ответа, код `ai_congested` и набор полей не изменились. Если ваш клиент
предполагал, что пауза укладывается в базу плюс две секунды, — снимите это предположение и ждите столько,
сколько названо в `Retry-After`. Это относится и к вызовам через устаревшие адреса `/v1/ai/*`, которые
ведут к тем же обработчикам.

### NEW-0811-24: у прерванного деплоя в галактике появилась машинная причина и признак повтора

Ответ `502 GALAXY_DEPLOY_INTERRUPTED` теперь несёт два новых необязательных поля. Прежние клиенты продолжают работать без изменений: код ошибки, `retryable` и `error.hint` остались на месте.

`error.subcause` называет, что именно произошло, — раньше все прерывания выглядели одинаково и единственным советом было «повторите»:

- `http_window_exhausted` — окно запроса кончилось раньше, чем платформа успела проверить хоть раз. Сборка вполне могла завершиться на хосте, и повторный запрос подтвердит это за секунды.
- `exec_channel_busy` — канал команд хоста был занят до конца отведённого ожидания.
- `no_this_deploy_container` — хост ответил, но здорового контейнера именно этого деплоя не нашлось.
- `tail_unreached` — связь оборвалась, и чистого ответа до дедлайна не пришло.
- `source_fetch_interrupted` — добывание архива с исходниками прервалось до начала сборки, приложение при этом не тронуто.

`error.repeated` становится `true`, когда одно и то же приложение прерывалось несколько раз за короткое окно. В этом случае `error.hint` перестаёт советовать простой повтор и предлагает сначала проверить, стартует ли приложение, а если оно заведомо рабочее — обратиться в поддержку с идентификатором сервера.

Заодно текст ошибки перестал утверждать, что хост доступен, в том случае, когда платформа его ни разу не спросила.

Затронутые эндпоинты: [POST /v1/infra/servers/:id/deploy](/docs/infra/deploy/deploy)

### FIX-0811-25: создание galaxy-приложения сообщает, что присланные provider, plan и region не применены

Приложение в галактике живёт контейнером на общем хосте: своей машины у него нет, поэтому провайдера, тариф и регион оно наследует от хоста. Так было и раньше, но ответ об этом молчал — присланные значения подменялись на хостовые без единого признака, и опечатка в идентификаторе тарифа выглядела как принятый запрос.

**Было**

`POST /v1/infra/servers` на портале, который размещает приложения в галактиках, отвечал `201`, а в `data.provider`, `data.plan` и `data.region` возвращал значения хоста. Отличить «платформа взяла моё» от «платформа взяла чужое» по ответу было нельзя.

**Стало**

Тот же запрос по-прежнему отвечает `201` и по-прежнему наследует значения хоста — поведение не изменилось. Изменился ответ: рядом с `data` появляется запись в `warnings[]`, если присланное значение расходится с действующим. Она называет разошедшиеся поля, показывает оба значения, предупреждает, что повторная отправка ничего не изменит, и указывает на `placement` со значением `dedicated` — способ получить машину с выбранными вами характеристиками. Совпавшие поля не упоминаются, поэтому корректный вызов остаётся без предупреждения. Слишком длинное или не похожее на идентификатор каталога значение описывается словами, а не возвращается обратно.

### BC-0811-26: загрузка файла в приложение галактики отклоняется вместо записи на общий сервер

> Поддержка старого формата до: 01.02.2027

**Было**

[POST /v1/infra/servers/:id/upload](/docs/infra/deploy/upload) принимал `id` приложения галактики (`kind: GALAXY_APP`) и записывал файл — но не в контейнер приложения, а на файловую систему общего сервера, где живут остальные приложения аккаунта. Ответ был `200`, поэтому промах выглядел как успех.

**Стало**

Тот же вызов отвечает `400 GALAXY_APP_USE_GALAXY_ROUTE` — как это уже делают `exec`, `logs` и `deploy` для приложений галактики. Ответ называет причину: запись шла на общий сервер, а не в контейнер.

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

Файлы в приложение галактики доставляются пересборкой образа из исходников — `source` в теле [POST /v1/infra/servers/:id/deploy](/docs/infra/deploy/deploy). Загрузка в отдельный сервер (`kind: STANDALONE`) не изменилась.

### FIX-0811-27: приложение из каталога Битрикс24 открывается сотруднику без учётной записи Вайбкод

**Было**

Приложение, открытое из раздела «Битрикс24.Вайбкод», запускалось только у тех сотрудников, у кого уже была личная учётная запись Вайбкод. Остальные получали экран «войдите в VibeCode и подключите этот портал» — даже когда приложение было выдано всему порталу. Из-за этого администратор, у которого учётная запись есть, видел рабочее приложение, а рядовые сотрудники — нет.

**Стало**

Учётная запись больше не является условием доступа. Приложение с политикой «весь портал» (а также «любой авторизованный» и «публичный доступ») открывается всем сотрудникам портала: членство подтверждает подписанная порталом форма, а платформа дополнительно спрашивает у Битрикс24, что это активный сотрудник, а не уволенный и не внешний гость. Именные политики (только владелец, конкретные пользователи, отделы) этим путём по-прежнему не открываются — они решают по конкретному человеку, поэтому там остаётся прежний экран со входом.

Что учесть при разработке приложения: у сотрудника без учётной записи заголовок `X-Vibe-User-Role` всегда приходит `MEMBER`, даже если на портале он администратор, а `X-Vibe-User-Name` — `Unknown`. Не завязывайте на эти заголовки необратимые решения; права проверяйте вызовом к Битрикс24.

### BC-0811-28: у AI-запроса появился срок обслуживания: вместо зависания приходит отказ 429

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

**Было**

Запрос к [POST /v1/chat/completions](/docs/ai/chat/completions), [POST /v1/embeddings](/docs/ai/embeddings) и их псевдонимам `/v1/ai/*` не имел объявленного верхнего срока. Обычный ответ упирался во внутренний предел ожидания и приходил обрывом соединения без тела, а у потокового ответа общего ограничения по времени не было вовсе: если кластер переставал присылать фрагменты, соединение висело до таймаута самого клиента. Отличить «ещё работает» от «уже не ответит» было нечем.

**Стало**

У запроса есть срок обслуживания. Не уложились — приходит `429` с телом `{"error":{"code":"ai_deadline_exceeded","type":"rate_limit_exceeded","retryAfter":<секунды>}}` и заголовками `Retry-After` и `X-AI-Deadline-Ms` (фактический бюджет запроса в миллисекундах). В потоковом ответе, где статус уже отправлен, то же тело приходит отдельным событием потока, за которым следует `data: [DONE]`.

Клиент может попросить себе более короткий бюджет заголовком запроса `X-AI-Deadline-Ms` — значение в миллисекундах. Оно только сокращает срок: больше платформенного не выдаётся, и там, где срок отключён, заголовок его не включает. Нечисловое или неположительное значение считается неуказанным и вызов не отклоняет.

Срок применяется и к повторной попытке на резервной модели: отсчёт идёт от прихода запроса, а не от начала попытки.

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

Обрабатывайте `429` с кодом `ai_deadline_exceeded` как приглашение повторить: подождите указанное в `Retry-After` число секунд и отправьте запрос заново. Для потоковых вызовов добавьте разбор события с полем `error` — раньше поток такого события по этой причине не присылал. Если у вашего клиента свой предел ожидания, передавайте его в `X-AI-Deadline-Ms`: тогда отказ придёт от нас с объяснением, а не оборвётся у вас по таймауту.

### FIX-0811-29: каталог регионов сервера на международной версии больше не отдаёт зоны недоступных регионов

**Было**

`GET /v1/infra/providers/{providerId}/regions` на международной версии платформы возвращал в списке региональные зоны, которые в этом сегменте недоступны для размещения, — они попадали в ответ вместе с доступными.

**Стало**

Список фильтруется по сегменту: на международной версии в ответе остаются только зоны, доступные для размещения в этом сегменте. Клиенту менять ничего не нужно — ответ стал корректным. На основной версии платформы список не изменился.
