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

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

### FIX-0825-1: карточка приложения получает сервер и на порталах с галактиками

**Было**

Приложение, созданное без сервера, а затем опубликованное на портале с галактиками, оставалось в разделе «Приложения» без сервера: `GET /v1/applications/{id}` отдавал `server: null` неограниченно долго. Привязка контейнера к уже существующей карточке зависела от настройки, выключенной по умолчанию, поэтому карточка и контейнер жили порознь.

**Стало**

Контейнер привязывается к уже существующей карточке независимо от этой настройки, и `GET /v1/applications/{id}` отдаёт сервер сразу после публикации. Настройка по-прежнему управляет только появлением НОВОЙ карточки у приложения, созданного на общем хосте, — её поведение не изменилось.

### BC-0825-2: ключ «только чтение» больше не пишет на платформенных ручках

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

**Было**

Режим ключа «только чтение» резал вызовы Битрикс24, но не трогал платформенные ручки V1.
Ключом «только чтение» проходили управление серверами (`DELETE /v1/infra/servers/{id}`,
`POST /v1/infra/servers/{id}/stop|start|reboot|wake`), деплой и запуск команд на сервере,
запись в хранилище (`POST /v1/storage/objects`), обращения (`POST /v1/feedback`), ключи
поиска и ИИ, а также ручки, списывающие вайбы.

**Стало**

Любая запись на платформенной ручке V1 под ключом «только чтение» отвечает
`403 WRITE_BLOCKED_READONLY_KEY` до выполнения операции. Ручки, которые проксируют вызов
в Битрикс24, работают как прежде: там решение принимает классификатор по имени метода,
поэтому чтения с телом запроса (`POST /v1/deals/search`, `POST /v1/batch` из одних чтений
и подобные) не затронуты.

Исключений два. `POST /v1/apps` — ключом «только чтение» по-прежнему можно завести
приложение и парный ключ В РЕЖИМЕ «только чтение», но нельзя выписать ключ «чтение и
запись». `DELETE /v1/infra/servers/{id}/lock` — снятие залипшего лока не меняет
состояния сервера и остаётся доступным: на него ссылается поле `recoveryAction`
в ответе `EXEC_BUSY`.

Если приложению нужны эти операции, переключите ключ в режим «чтение и запись» на
странице `/keys`. Проверить режим и доступность создания сервера можно через
[GET /v1/me](/docs/keys-auth/me): у ключа «только чтение» в ответе появилось поле
`writeRestriction` — код отказа, область его действия и адрес страницы про режимы. Оно
нужно потому, что тело ответа называет пишущие ручки в трёх десятках мест (подсказки
хранилища, обращений, автосохранения исходников, коворка); это адреса, а не разрешения,
и поле говорит это прямо. Доступность конкретной операции по-прежнему в `capabilities`.

### FIX-0825-3: /v1/me перестал объявлять создание приложения недоступным ключу «только чтение»

В ответе [GET /v1/me](/docs/keys-auth/me) слот `capabilities.apps.create` под ключом в режиме «только чтение» приходил с `available: false` и причиной `WRITE_BLOCKED_READONLY_KEY`. Это было неверно: [POST /v1/apps](/docs/apps/create) отказывает такому ключу только при попытке завести приложение и парный ключ в режиме «чтение и запись», а приложение В РЕЖИМЕ «только чтение» тот же ключ заводит и получает `201`.

Слот теперь приходит доступным, а ограничение описано в его поле `note`. Клиент, который ветвится по `capabilities` — а `/v1/me` для этого и нужен, — больше не пропускает работающую операцию.

### BC-0825-4: ключ десктопа Коворка больше не меняет регистрацию OAuth-приложений

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

**Было**

Ключ с системным правом `vibe:cowork` мог создать OAuth-приложение на аккаунте Битрикс24 (`POST /v1/apps`), изменить его (`PATCH /v1/apps/{id}`), снести (`DELETE /v1/apps/{id}`) и перепривязать к другим учётным данным (`POST /v1/apps/{id}/relink-oauth`). Тот же ключ при этом уже был закрыт от инфраструктуры, депо исходников и публикации в каталог, то есть запрет был неполным.

**Стало**

Все четыре операции отвечают `403 INFRA_FORBIDDEN_FOR_COWORK_KEY`; в `details.deployableKeys` перечислены пригодные ключи владельца. На `POST /v1/apps` отказ приходит до проверки тела, поэтому невалидное тело тоже получает этот код, а не `VALIDATION_ERROR`. Чтения семейства (`GET /v1/apps`, `GET /v1/apps/{id}`) не изменились.

**Что делать**

Выполнять эти операции обычным ключом приложения: право `vibe:cowork` выдаётся платформой и предназначено только для данных. Ключи-кандидаты приходят в `details.deployableKeys` того же ответа — имя, префикс и последние символы, без секрета. Окна поддержки старого поведения нет: право `vibe:cowork` не выдаётся пользователям и не появляется в диалогах ключей, а единственный его носитель этих операций не вызывает.

### BC-0825-5: повторная загрузка файла заменяет содержимое вместо ошибки 500

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

**Было**

Загрузка на уже занятый логический `key` через `POST /v1/storage/objects/upload` отвечала `500` с кодом
`P2002`. При этом байты в хранилище уже были перезаписаны, а `sizeBytes` и `sha256` объекта оставались
от прежней версии, поэтому размер в списке и в счёте не соответствовал реальному файлу.

**Стало**

Повторная загрузка на тот же `key` заменяет содержимое: `200`, тот же `object.id` (выданные ранее
ссылки продолжают работать), а `sizeBytes`, `sha256`, `contentType` и новое поле `contentUpdatedAt`
соответствуют новым байтам.

Это верно для **ключа приложения**. У личного ключа разработчика без привязки к приложению повторная
загрузка по-прежнему создаёт новый объект со своим `id` — поведение не изменилось, оно чинится
отдельно.

Когда заменить объект на месте нельзя, вместо `500` приходит `409` с внятным кодом:
`STORAGE_KEY_DELETED` (объект удалён и держит имя до уборки),
`STORAGE_MULTIPART_IN_PROGRESS` (идёт составная загрузка),
`STORAGE_UPLOAD_PENDING` (по ключу висит незавершённая бронь предподписанной ссылки),
`STORAGE_KEY_OWNED_ELSEWHERE` (имя занято другим объектом приложения),
`STORAGE_KEY_CONFLICT` (объект изменился во время загрузки — повторите запрос).

**Что нужно сделать**

1. Поле `visibility` при замене не может менять видимость объекта: не передавайте его либо передавайте
   текущее значение. Иное значение отвечает `400 STORAGE_VISIBILITY_MISMATCH`, и текущее значение
   указано в сообщении. Раньше такой запрос отвечал `500`, но байты при этом всё же заменялись — то
   есть клиент, игнорировавший ошибку, публиковал обновления и после фикса перестанет.
2. Замена через предподписанную ссылку (`POST /v1/storage/objects`) больше не выдаётся на существующий
   объект: `409 STORAGE_KEY_EXISTS`. Content-Type в такой ссылке не подписывается, поэтому заменять
   содержимое можно только прямой загрузкой. Незавершённая бронь, выданная вам же, при этом
   переиспользуется — приходит `200` с тем же `object.id` и свежей ссылкой; видимость брони такая
   ссылка не меняет, поэтому расхождение отвечает `400 STORAGE_VISIBILITY_MISMATCH`.
3. Составная загрузка (`POST /v1/storage/objects/multipart/create`) на занятый ключ тоже отвечает
   `409` вместо `500`: `STORAGE_KEY_EXISTS`, `STORAGE_KEY_DELETED`, `STORAGE_MULTIPART_IN_PROGRESS`,
   `STORAGE_UPLOAD_PENDING` или `STORAGE_KEY_CONFLICT` (объект создал параллельный запрос). Замена объекта через составную загрузку не поддерживается —
   используйте другой ключ.
4. В ответах появилось поле `object.contentUpdatedAt` — момент, когда содержимое последний раз стало
   актуальным. У объектов, записанных до этого обновления, оно `null`.

### BC-0825-6: поля привязки и автора у комментария таймлайна больше не выглядят изменяемыми

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

**Было**

`GET /v1/timelines/fields` отдавал `entityType`, `entityId` и `authorId` как обычные записываемые поля. `PATCH /v1/timelines/{id}` с любым из них отвечал `200` и `success: true`, но значение не менялось — повторное чтение показывало прежнее. При этом `id` и `createdAt` в том же ответе честно отклонялись с `400 READONLY_FIELD`, то есть справочник полей был правдив избирательно. Интегратор и AI-агент считали, что сменили автора или перенесли комментарий на другую запись, тогда как не менялось ничего.

**Стало**

Три поля помечены в справочнике неизменяемыми, и попытка их записать отклоняется с `400 READONLY_FIELD` вместо тихого успеха. Причины у полей разные, и справочник теперь их различает:

- `entityType` и `entityId` — доступны только при создании: комментарий привязывается к записи в момент добавления, перенести его на другую запись нельзя;
- `authorId` — только для чтения: автора Битрикс24 берёт из учётных данных, которыми сделан вызов, поэтому задать его нельзя ни при обновлении, ни при создании.

Изменяемым остаётся `comment` — это единственное поле, которое принимает и обновление на стороне Битрикс24.

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

Уберите `entityType`, `entityId` и `authorId` из тела `PATCH /v1/timelines/{id}` — они и раньше не применялись, а теперь запрос с ними получит `400 READONLY_FIELD`. Отдельно проверьте **создание**: `POST /v1/timelines` с полем `authorId` тоже переходит с «`201`, значение проигнорировано» на `400 READONLY_FIELD`, потому что автора нельзя задать и при создании. Поля `entityType` и `entityId` на создании по-прежнему обязательны и принимаются. Если код полагался на «успешный» ответ как на признак смены автора или переноса комментария, это ожидание не выполнялось и до правки: значение оставалось прежним. Комментарий по-прежнему обновляется обычным `PATCH` с полем `comment`. Привязку задавайте при создании через `POST /v1/timelines`, автора сменить нельзя — вызовите метод от имени нужного пользователя.

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

### BC-0825-7: базовая версия для выкладки требуется и когда верхнюю версию сохранил другой человек

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

**Было**

[POST /v1/infra/servers/{id}/deploy](/docs/infra/deploy/deploy) требовал `baseVersionId` только на сервере, у которого есть живая команда разработки. Если владелец добавлял сотрудника, тот выкладывал свою версию, а затем владелец снимал его с команды, требование снималось вместе с последним участником — и следующая выкладка молча затирала сохранённую работу.

**Стало**

Метка `baseVersionId` обязательна и тогда, когда верхняя версия в хранилище исходников сервера сохранена не тем, кто выкладывает сейчас, — независимо от того, есть ли у сервера команда прямо сейчас. Отказ прежний — `409 BASE_VERSION_REQUIRED` с номером актуальной версии.

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

Отправлять `baseVersionId` при каждой выкладке. Клиент, который уже это делает, не меняет ничего. Клиент, который полагался на «команда пуста → метка не нужна», получит `409 BASE_VERSION_REQUIRED` на сервере с чужой сохранённой версией — в том числе после передачи владения приложением, когда верхнюю версию сохранил прежний владелец. Обработка та же, что уже написана для сервера с командой: прочитать номер актуальной версии из тела отказа и повторить выкладку, указав его как базу. Старое поведение не сохраняется ни на какой срок: именно оно и приводило к потере чужой работы.

### NEW-0825-8: участник команды разработки сервера теперь видит его приложение в витрине

Участник команды разработки сервера теперь видит приложение, привязанное к этому серверу, в [GET /v1/applications](/docs/applications/list) (со значением `viewerState: "shared"`) и может прочитать его карточку через [GET /v1/applications/{id}](/docs/applications/get) — раньше карточка отвечала `403 FORBIDDEN`, потому что доступ проверялся только по владению и по политике доступа сервера, без учёта членства в команде.

### FIX-0825-9: include публикуется только для сущностей со связями

**Было**

В OpenAPI и MCP-справке параметр `include` объявлялся для list, get и search у всех сущностей. Для сущностей без доступных связей запрос с этим параметром завершался `400 INVALID_INCLUDE` с пустым списком доступных связей.

**Стало**

OpenAPI объявляет `include` только у сущностей с доступными связями, а MCP-справка предлагает сначала проверить capability через `discover` или `get_fields`. Проверка во время запроса и ответ `400 INVALID_INCLUDE` для неподдерживаемого `include` не изменились.

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

Генераторы клиентов больше не получают неподдерживаемый `include` из OpenAPI, а MCP-агенты получают явное указание сначала проверить capability. Общий optional-ключ MCP сохраняется. Существующие корректные запросы продолжают работать без изменений, а прежние неподдерживаемые запросы получают тот же ответ `400 INVALID_INCLUDE`.

### BC-0825-10: Потолок inline-архива в теле деплоя сужен до 96 МБ

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

**Было**

[POST /v1/infra/servers/:id/deploy](/docs/infra/deploy/deploy) с кодом во встроенном `source.content`, [POST /v1/infra/servers/:id/upload](/docs/infra/deploy/upload) со встроенным `content` и [POST /v1/infra/servers](/docs/infra/servers/create) с полем `source` при создании принимали тело до 500 МБ. Отказ сверх потолка приходил кодом `PAYLOAD_TOO_LARGE`.

**Стало**

Тело этих трёх запросов ограничено 96 МБ. Единица — само HTTP-тело, а не архив: `source.content` / `content` это base64, который тяжелее исходных байт примерно на треть, поэтому 96 МБ тела соответствуют архиву примерно в 72 МБ. Тело сверх потолка отклоняется кодом `413 INLINE_SOURCE_TOO_LARGE` — новым, вместо прежнего `PAYLOAD_TOO_LARGE` на этих трёх входах. Отказ приходит по заголовку `Content-Length` до чтения тела, поэтому он детерминированный — то же тело повторной отправкой не пройдёт. Трафик это не экономит: тело загружается целиком, и отказ приходит после того, как загрузка закончилась. Конверт ошибки несёт `error.hint` с полями `reason`, `recovery`, `recoveryAction` и `note` — готовым рецептом перехода.

Многосоставная форма (`multipart/form-data`) [POST /v1/infra/servers/:id/deploy](/docs/infra/deploy/deploy) под тот же потолок попадает условно. Файловая часть уходит в хранилище потоком, и тогда её предел остаётся прежним — 500 МБ архива, — только когда одновременно верны три условия: вызывающий обращается к серверу как его прямой владелец, а не через карточку чужого приложения или через менеджмент-ключ; сервер либо не galaxy-приложение, либо для вызывающего включены обе настройки ссылочного деплоя galaxy-приложений; хранилище исходников включено — и на уровне платформы, и для этого портала. Если хотя бы одно условие не выполнено, поток на потоковую запись не переключается, и форма принимает архив тем же буферным способом, что и base64-поля выше, — под тем же потолком в 72 МБ архива и с тем же кодом `413 INLINE_SOURCE_TOO_LARGE` и `error.hint`.

Прежний код `PAYLOAD_TOO_LARGE` никуда не делся: он по-прежнему приходит на входе `/v1/` (потолок 500 МБ) и на остальных маршрутах платформы со своими лимитами — переименований нет.

Без изменений:

- `source.url` — по-прежнему принимается до 500 МБ, платформа скачивает архив по ссылке сама;
- `source.versionId` — деплой уже сохранённой версии, до 500 МБ архива;
- потолок сохранения версии исходников на [POST /v1/infra/servers/:id/sources](/docs/source-storage/servers) и `POST /v1/apps/:id/sources` — 500 МБ, не тронут.

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

Архив крупнее 72 МБ отправляйте версионным путём в два вызова — он работает одинаково для личного ключа (`vibe_api_*`) и для ключа OAuth-приложения, а прямому владельцу сервера этих двух вызовов достаточно:

```
POST /v1/infra/servers/:id/sources   # сырые байты архива, Content-Type: application/gzip, --data-binary
POST /v1/infra/servers/:id/deploy    # {"source":{"versionId":"vN"}}
```

Если же сервер доступен через карточку приложения или менеджмент-ключ, а не напрямую владельцем, к тем же двум вызовам добавляется третий: `{versionId}` может ответить `SOURCE_VERSION_REQUIRES_APP` или `SOURCE_VERSION_NOT_FOUND` — тогда получите подписанную ссылку [GET /v1/infra/servers/:id/sources/vN/download](/docs/source-storage/servers) и разверните архив по ней: `{"source":{"url":"<ссылка>"}}`. Тот же путь и есть страховка от условного потолка многосоставной формы — версионный деплой ни при каких условиях не буферизует архив целиком, поэтому его 500-мегабайтный предел безусловен.

Сужено на уровне наблюдаемого поведения: тело в сотни мегабайт в base64-форме (и многосоставный архив, для которого не выполнилось хотя бы одно из условий выше) приходилось держать в памяти целиком на всё время запроса, и такой запрос мог оборваться без ответа, задев параллельные запросы на этом же процессе.

### NEW-0825-11: `GET /v1/me` называет архивный эквивалент inline-потолка отдельным полем

**Было**

Блок `deployment.limits` нёс потолок встроенного тела одним полем `uploadInlineMax`. Единица там — HTTP-тело, а `source.content` и `content` едут в base64, поэтому размер архива, который в это тело поместится, клиент считал сам.

**Стало**

Рядом появилось поле `uploadInlineMaxArchive` — тот же потолок, выраженный в размере АРХИВА: три четверти от `uploadInlineMax`, потому что base64 тяжелее исходных байт примерно на треть. Поле добавлено к ответу и ничего в нём не убирает: `uploadInlineMax` остаётся на прежнем месте с прежним смыслом, и клиент, который его не читает, работает без правок. Оба значения приходят строками с единицей измерения — например `96MB` и `72MB`.

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

Ничего обязательного. Если вы сами пересчитывали потолок тела в размер архива, возьмите готовое значение из `uploadInlineMaxArchive` — оно приходит из той же константы, что и отказ `413 INLINE_SOURCE_TOO_LARGE`, поэтому разойтись с фактическим поведением не может.

### BC-0825-12: POST /v1/apps соблюдает политику портала «кто может создавать приложения»

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

**Было**

Публичный маршрут сверялся только с устаревшим режимом «по списку». Портал, где администратор
Битрикс24 разрешил создавать приложения одним администраторам или запретил создание совсем,
всё равно позволял создать приложение любым личным API-ключом — при том что то же действие в
кабинете отвечало `403`.

**Стало**

[POST /v1/apps](/docs/apps/create) отвечает `403` с кодом `APP_CREATION_RESTRICTED`, когда
политика портала не разрешает вызывающему создавать приложения. Режим «по списку» работает
как прежде: участник из списка приложение создаёт, остальные получают `403`. На порталах, где
создание открыто всем, не меняется ничего.

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

Ключ, выписанный на портале с ограничением, начнёт получать `403` `APP_CREATION_RESTRICTED`
там, где раньше приходил `201`. Попросите администратора Битрикс24 выдать право на создание
приложений — или ведите создание ключом того, у кого это право есть.

### FIX-0825-13: Установка среды Node.js 20 не повторяет уже выполненные шаги

**Было**

При повторном развёртывании среды Node.js 20 через [POST /v1/infra/servers/:id/deploy](/docs/infra/deploy/deploy) платформа заново устанавливала Node.js и `pm2`. Недоступность реестра npm могла занять всё время шага и завершиться без точной причины.

**Стало**

Платформа пропускает установку уже доступных Node.js 20 и `pm2`. Если `pm2` отсутствует, его установка ограничена по времени и числу повторов, а ошибка или превышение лимита времени останавливает развёртывание с явной диагностикой.

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

Повторные развёртывания завершаются быстрее и не требуют изменений со стороны интеграторов. Ошибка установки `pm2` теперь сразу видна как причина неуспешного развёртывания.

### FIX-0825-14: привязка плейсмента отвечает 400 на слишком длинный iconName вместо 502 без причины

**Было**

`POST /v1/placements/bind` принимал `options.iconName` длиной до 255 символов. Битрикс24 отклоняет значение длиннее 50, поэтому запрос уходил в аккаунт и возвращался оттуда как `502 BITRIX_UNAVAILABLE` с текстом «Failed to register placement on Bitrix24 via dev key». Какое именно поле не подошло, из ответа понять было нельзя.

**Стало**

Граница длины в схеме совпадает с границей Битрикс24: значение длиннее 50 символов отклоняется сразу, а `400` называет поле `options.iconName`. Значения, которые привязывались раньше, продолжают привязываться — длиннее 50 аккаунт не принимал никогда, а подставляемый по умолчанию значок `fa-cube` в границу укладывается.
