# Изменения API: 27 июля 2026

[← Журнал изменений](/docs/changelog) · [Июль 2026](/docs/changelog/2026-07)

### NEW-0727-1: фавикон приложения одной строкой — /_gw/icon

Фавикон во вкладке браузера теперь ставится одной статической строкой без id сервера:

```html
<link rel="icon" href="/_gw/icon">
```

`/_gw/icon` — платформенный путь на домене приложения; он всегда отдаёт текущую загруженную иконку. Одна загрузка [POST /v1/infra/servers/:id/icon](/docs/infra/app-icon) управляет и карточкой в каталоге Bitrix24, и фавиконом: перезалили иконку — фавикон обновится сам (~5 минут), пересобирать приложение не нужно. Свой статический файл иконки в приложение класть больше не нужно. Строка совместима с созданием приложения одним запросом (id не требуется).

Дополнительно ответ [POST /v1/infra/servers/:id/deploy](/docs/infra/deploy) и создания приложения одним запросом [POST /v1/infra/servers](/docs/infra/servers) теперь возвращает запись в `warnings[]`, если иконка ещё не загружена — с точным эндпоинтом для загрузки (иконка грузится отдельным запросом, id известен только после создания). Тот же `warnings[]` по-прежнему подсказывает, если не заданы `displayName`/`description`. Успешный деплой без иконки или названия больше не выглядит завершённым молча.

### NEW-0727-2: source-at-create: ошибка SOURCE_AT_CREATE_GALAXY_ONLY теперь несёт подсказку с путём к галактике

Отказ [POST /v1/infra/servers](/docs/infra/servers/create) с `source`, который не удалось разместить в галактике (на портале в режиме «обе стратегии» без открытого galaxy-хоста, либо на портале только со standalone), теперь дополнительно несёт `error.hint` — с понятным путём: как получить galaxy-хост и/или как задеплоить в два шага на выделенный сервер. Код и текст ошибки не изменились.

### FIX-0727-3: galaxy-приложения: PATCH /sleep и PATCH /port теперь отвечают 400 — управляйте ими со страницы «Галактики»

**Было**

Для приложения, размещённого в галактике (`GALAXY_APP`), [PATCH /v1/infra/servers/:id/sleep](/docs/infra/lifecycle/sleep) возвращал `200` и записывал `sleepAfterMinutes`, а [PATCH /v1/infra/servers/:id/port](/docs/infra/deploy/port) отвечал 404/409 — расходясь с задокументированным контрактом `/v1/me` (`deployment.galaxyApp`), где ни одно V1-действие жизненного цикла к galaxy-приложению не применяется.

**Стало**

Оба вызова для galaxy-приложения отвечают `400` с `error.code = "GALAXY_APP_USE_GALAXY_ROUTE"` и ничего не меняют. Настраивайте авто-сон приложения через маршрут галактики; порт у galaxy-приложения закреплён за хостом и не задаётся. Для обычных (standalone) серверов поведение `/sleep` и `/port` не изменилось.

### FIX-0727-4: galaxy-приложение восстанавливается после обрыва туннеля хоста

**Было**

Если у galaxy-хоста обрывался защищённый туннель (сервер в статусе `RUNNING`, но связь потеряна), развёртывание и выполнение команд galaxy-приложения возвращали `502 GALAXY_HOST_UNREACHABLE`, а запрос логов — пустой ответ с подсказкой. Хост оставался недостижим до ручного ремонта: повтор того же запроса упирался в ту же ошибку сколь угодно долго.

**Стало**

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

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

Код ошибки и форма ответа не изменились — `502 GALAXY_HOST_UNREACHABLE` (для развёртывания и выполнения команд) по-прежнему помечен как повторяемый, а логи по-прежнему отдают пустой список с подсказкой. Изменилось только то, что теперь повтор приводит к успеху, а не к вечной ошибке. Продолжайте повторять запрос по своей обычной политике на этот код и на пустой ответ логов.

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

### NEW-0727-5: предупреждение, когда changelog деплоя некуда опубликовать

Ответ [POST /v1/infra/servers/:id/deploy](/docs/infra/deploy) теперь возвращает запись в `warnings[]`, если в запросе передан `changelog`, а деплой не создал новую версию исходников. Заметка о релизе привязана к версии, поэтому без неё текст никуда не сохраняется и в ленту канала приложения в мессенджере Bitrix24 не уходит.

Причина видна в поле `source` того же ответа: хранилище исходников выключено (`feature-disabled-platform` или `feature-disabled-portal`), сохранение не удалось (`save-failed`) либо загруженные байты совпали с предыдущей версией. Раньше такой деплой отвечал обычным успехом, и узнать, что заметка потерялась, было нечем. Предупреждение приходит и в JSON-режиме, и в событии `done` при `?stream=true`.

### FIX-0727-6: Список bizproc-templates без явного select возвращает все поля, включая id

**Было**

`GET /v1/bizproc-templates` и `POST /v1/bizproc-templates/search` без явного `select` возвращали только поле `documentType`. Без `id` клиент не мог выполнить последующие `update`/`delete` — список был бесполезен без второго запроса с явным `select`.

**Стало**

Оба вызова без `select` возвращают полный набор объявленных полей записи (`id`, `moduleId`, `entity`, `documentType`, `autoExecute`, `name`, `description`, `modified`, `isModified`, `userId`). Явный `select` работает как прежде. Форма запроса не изменилась.

### NEW-0727-7: загрузка файла в документ Базы знаний возвращает assetMarkdown сразу

Загрузка файла через [POST /v1/note/documents/{documentId}/files](/docs/note/files/upload) раньше отдавала только `{ id }`, поэтому за готовым блоком для вставки в документ приходилось идти вторым запросом в `GET /v1/note/documents/{documentId}/files/{id}` либо собирать разметку `[[image fileId=N]]` руками.

Теперь ответ несёт весь объект файла — `id`, `documentId`, `name`, `size`, `mimeType`, `assetType`, `assetMarkdown` — то есть ту же форму, что отдаёт GET. Загрузили картинку, взяли `assetMarkdown` из ответа, вставили в текст документа и вызвали PATCH — второй запрос больше не нужен.

Изменение аддитивное: поле `id` осталось на месте и с тем же значением, поэтому клиент, который читает только его, продолжает работать без правок. Если конкретный портал вернёт объект без `assetMarkdown`, лишних полей платформа не придумывает — в ответе будет то, что пришло от Битрикс24.

### FIX-0727-8: границы `ttlSeconds` у токенов доступа в машинной схеме совпали с поведением

**Было**

Схема OpenAPI для [POST /v1/infra/servers/{id}/access-tokens](/docs/infra/access-tokens/create) обещала `ttlSeconds` в диапазоне от 60 секунд до 30 суток. Платформа же с самого начала принимала от 300 секунд до 315 360 000 (десять лет) и отклоняла всё за этими пределами кодом `400 INVALID_TTL`. Поэтому клиент или генератор клиентских библиотек, взявший минимум прямо из схемы, получал жёсткий отказ на значении, которое схема сама и предлагала, а вариант «Бессрочно» из интерфейса выглядел недоступным через API. Текстовая документация всё это время была верна — расходилась только машинная схема.

**Стало**

Схема берёт границы и значение по умолчанию из тех же констант, которыми проверяется запрос, поэтому разойтись им больше нечем: минимум 300, максимум 315 360 000, по умолчанию 86 400.

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

Поведение эндпоинта не менялось — менялось только то, что о нём написано в машинной схеме. Если вы генерировали клиента по OpenAPI и он валидировал `ttlSeconds` на своей стороне, перегенерируйте его: прежний клиент отклонял бы корректные значения больше 30 суток и разрешал бы заведомо отказные меньше 300 секунд.

### NEW-0727-9: Приложение в плейсменте может авто-ресайзить свой iframe

Приложение, встроенное в плейсмент, теперь может сообщать платформе высоту своего контента, и платформа растит iframe под неё — раньше высоту фиксировал Битрикс24 и высокий контент обрезался. Приложение постит сообщение родительскому окну: `window.parent.postMessage({ type: 'vibe:resize', height: <пиксели> }, '*')`. Принимается тип `vibe:resize` или `vibe:setHeight` с числовым полем `height`; `targetOrigin` должен быть `'*'` — браузер сверяет его с непосредственным родителем окна. Пересчитывайте высоту при изменении контента, например через `ResizeObserver`. Полное описание и рекомендации — раздел «Авто-высота iframe» руководства по рантайму приложения.

Функция активируется на стороне платформы по аккаунтам Битрикс24; если ресайз пока не срабатывает, для вашего аккаунта она ещё не активна.

### FIX-0727-10: offset в списках считается по записям, а не по страницам

**Было**

Битрикс24 отдаёт списки страницами по 50 и трактует смещение как номер страницы, а не как число записей. Мы передавали `offset` как есть, поэтому он молча округлялся вниз до кратного 50: `offset=0`, `offset=7` и `offset=49` возвращали одну и ту же первую страницу — без ошибки и без предупреждения. Обход выборки шагом меньше 50 записей зацикливался на первой странице, а шаг ровно в 50 работал и создавал впечатление, что параметр исправен.

**Стало**

`offset` считается по записям на generic-списках: `GET /v1/{entity}`, `POST /v1/{entity}/search`, `POST /v1/batch` (действие `list`) и `GET /v1/{entity}/{id}/activities`. `offset=7` начинает выборку с 8-й записи. Смещение и `limit` независимы: `?limit=2&offset=51` вернёт ровно две записи, начиная с 52-й. Битрикс24 по-прежнему отдаёт страницами по 50 — Вайбкод запрашивает страницу, покрывающую нужную позицию, и отбрасывает лишнее начало; цена — не более одной дополнительной страницы у Битрикс24 на запрос.

Заодно исправлены два следствия. `meta.hasMore` учитывает смещение: раньше у сущностей, чей список Битрикс24 отдаёт под именованным ключом — сделки, контакты, компании, лиды, задачи, заказы, товары, счета и другие, всего два десятка, — он сравнивал только длину страницы с общим количеством и оставался `true` на последней странице при ненулевом `offset`. `GET /v1/{entity}/{id}/activities` теперь уважает `limit`: метод Битрикс24 не принимает ограничение выборки, поэтому раньше приходила вся страница целиком независимо от запрошенного значения.

Если выборка на запрошенной позиции оказалась пустой из-за фильтрации на стороне Битрикс24, ответ несёт `meta.warnings` с кодом `OFFSET_BEYOND_FETCHED_PAGE` — раньше это выглядело как пустой список без объяснения.

Для глубокой навигации по большим выборкам курсор по ключу (`filter[>id]` с `order[id]=asc`) по-прежнему надёжнее смещения: он не зависит от глубины и устойчив к параллельным изменениям.

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

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

Два момента стоит проверить. Первый — `GET /v1/{entity}/{id}/activities` с явным `limit`: раньше приходила вся страница (до 50 записей) независимо от значения, теперь придёт ровно запрошенное количество. Если код полагался на то, что за один вызов вернётся больше запрошенного, увеличьте `limit` или пройдите выборку постранично. Второй — обход выборки шагом меньше 50: раньше он зацикливался на первой странице, теперь идёт вперёд, поэтому цикл, который «выручал» лишний выход по счётчику, начнёт возвращать новые записи.

Остаточные исключения, где смещение НЕ построчное. Во-первых, четыре сущности generic-слоя, у которых метод Битрикс24 округляет смещение до границы страницы, а расширить выборку сверх запрошенного размера нельзя, не рискуя потерять записи: `calendar-events`, `calendar-sections`, `telephony-lines`, `workgroups`. Смещение там осталось прежним. Во-вторых, отдельные эндпоинты с собственными обработчиками, которые в это изменение не входили: `/v1/warehouses`, `/v1/bookings`, `/v1/posts`, `/v1/requisite-links`, `/v1/lists`, `/v1/timeline-logs` и история стадий в `/v1/crm-extras`. Их поведение не изменилось.

У четырёх сущностей generic-слоя выше смещение осталось прежним, но `meta.hasMore` у них стало точнее: раньше на последней странице при ненулевом смещении он мог сказать «больше нет», хотя записи оставались.

### FIX-0727-11: статус закрытого тикета обратной связи больше не показывается как «в работе»

**Было**

Тикет, попавший под детекцию probe-кампании, показывался автору со статусом `NEW` независимо от того, что с ним реально происходило. Фильтр списка работает по настоящему статусу, поэтому закрытый тикет одновременно попадал во вкладку «Решено» и рисовался бейджем «в работе» — один и тот же тикет противоречил сам себе. Затронуты [GET /v1/feedback](/docs/feedback) и `GET /v1/feedback/{id}`.

**Стало**

Маскируется только то состояние, для которого маска и заводилась: авто-архив. Закрытый тикет отдаёт `RESOLVED`, тикет в работе — свой реальный статус, а авто-архив по-прежнему приходит как `NEW`. Ключи с доступом к обратной связи (management, скоуп `vibe:feedback`) как и раньше видят настоящий статус.

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

Клиент, который читал `status` и ожидал `NEW` у такого тикета, теперь получит его фактический статус — это и есть исправление. Дополнительно: у тикета, отмеченного детекцией и уже закрытого, отзыв (`PATCH {"status":"WITHDRAWN"}`) и ответ автора теперь отклоняются с `409 FEEDBACK_CLOSED`, как у любого закрытого тикета; раньше они проходили, потому что гейт сверялся с замаскированным статусом.

### FIX-0727-12: Connect-ключи: сохранённые права авторитетны — vibe:ai / vibe:search больше не добавляются автоматически

**Было**

Ключ, выданный через VibeCode Connect, при каждом запросе автоматически получал платформенные права `vibe:ai` и `vibe:search`, даже если они не запрашивались и не были согласованы. Такой ключ мог обращаться к AI-эндпоинтам (`/v1/chat/completions`, `/v1/ai/*`) и поиску (`/v1/search`), и расход шёл со счёта аккаунта, к которому привязан портал.

**Стало**

Сохранённые на ключе права теперь авторитетны — платформа не расширяет их автоматически. Ключ, выданный через Connect, обращается к AI- и поиск-эндпоинтам только если соответствующее право реально присутствует в ключе; иначе ответ `403`. Ротация ключа сохраняет этот признак. Обычные ключи, созданные в кабинете, поведения не меняют.

### FIX-0727-13: Приложения: производный ключ и синхронизация прав не выдают больше, чем есть у вызывающего ключа

**Было**

Вызов `POST /v1/apps` авторитетным ключом (выданным через VibeCode Connect или производным от него) минтил парный ключ приложения с полным набором платформенных прав по умолчанию (`vibe:infra`, `vibe:ai`, `vibe:search`, `vibe:storage`) — даже если у самого вызывающего ключа этих прав не было. Так же `PATCH /v1/apps/:id` мог записать в объявление приложения `vibe:*`-право, которого у ключа нет. В обоих случаях производный ключ получал возможность обращаться к AI и поиску за счёт аккаунта, к которому привязан портал.

**Стало**

Производный ключ получает ровно те платформенные права, что реально есть у вызывающего ключа. Если у авторитетного ключа права нет, запрос с ним в теле возвращает `403` `SCOPE_GRANT_REQUIRES_CONSENT`, а список несогласованных прав приходит в `error.details.unconsented`. Права, которые у ключа есть (например согласованный `vibe:storage`), проходят как раньше. Срок жизни производного ключа наследуется от вызывающего. Синхронизация прав приложения на парные ключи больше никогда не добавляет `vibe:*` авторитетному ключу — сужение прав при этом по-прежнему применяется. Обычные ключи, созданные в кабинете, поведения не меняют: платформенные права им по-прежнему выдаются по умолчанию.

### FIX-0727-14: таймаут вызова Битрикс24 стал управляемым, внутренний повтор после таймаута отменён

**Было**

Платформа всегда обрывала HTTP-вызов к Битрикс24 на 15-й секунде, а для методов чтения после обрыва делала одну внутреннюю повторную попытку — итого до ~30 секунд до ответа `503 BITRIX_TIMEOUT`. Повтор при этом запускал второе параллельное выполнение того же вызова на портале: обрыв соединения не останавливает работу Битрикс24 над запросом.

**Стало**

- Лимит времени одного вызова к Битрикс24 настраивается платформой (по умолчанию прежние 15 секунд). На порталах, где Битрикс24 отвечает медленно, платформа может поднять лимит — запросы, которые раньше стабильно завершались `503 BITRIX_TIMEOUT`, доживают до реального ответа и возвращают данные.
- Внутренняя повторная попытка после таймаута отменена для всех методов. `503 BITRIX_TIMEOUT` на чтениях приходит примерно вдвое быстрее (~15 секунд вместо ~30), и запрос больше не выполняется на портале дважды. Повторы по `429` (rate limit) не тронуты.
- Текст ошибки `Bitrix24 did not respond within 15s` подставляет фактический лимит (например, `within 60s`) — не опирайтесь на константу в тексте.
