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

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

### FIX-0713-1: Платформенные scope ключа OAuth-приложения синхронизируются при создании и правке

**Было**

Ключ, выписанный вместе с OAuth-приложением через [POST /v1/apps](/docs/apps), не получал платформенные scope (`vibe:infra`, `vibe:ai`, `vibe:search`, `vibe:storage`) — в отличие от ключа, созданного в кабинете. Из-за этого [POST /v1/infra/servers](/docs/infra/servers) под таким ключом отвечал 403 `INFRA_SCOPE_REQUIRED`. Добавление `vibe:infra` в scope приложения через [PATCH /v1/apps/:id](/docs/apps) меняло только приложение, но не парный ключ — эффекта на доступ не было.

**Стало**

Парный ключ при создании через `POST /v1/apps` получает те же платформенные scope по умолчанию, что и ключ из кабинета. Изменение `vibe:*`-scope через `PATCH /v1/apps/:id` (добавление И удаление) теперь синхронизируется в активные ключи приложения. Ключ в режиме «только чтение» (READONLY) при этом отклоняется с 403 `WRITE_BLOCKED_READONLY_KEY` на трёх write-операциях: создание сервера (`POST /v1/infra/servers`), изменение scope приложения (`PATCH /v1/apps/:id`) и выписка READWRITE-ключа (`POST /v1/apps` с `mode: "READWRITE"`).

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

Приложения, созданные через API, теперь могут управлять инфраструктурой без пересоздания. Ключ, у которого приложение уже объявляет `vibe:infra`, но сам ключ его лишён (старое расхождение), чинится одной правкой: снять `vibe:infra` из scope приложения и вернуть обратно через `PATCH /v1/apps/:id` — вторая правка синхронизирует ключ; либо пересоздать приложение. READONLY-ключ по-прежнему может создавать READONLY-приложение (`mode: "READONLY"`).

### FIX-0713-2: product-sections: неподдерживаемые фильтры возвращают 400, а не всю таблицу

**Было**

Операторы (`>`, `<`, `!`, `%`, `$ne`, `$contains`, `$nin`) и поля вне точного равенства (`sort`, неизвестные) в фильтре [GET /v1/product-sections](/docs/entities/product-sections/list) и [POST /v1/product-sections/search](/docs/entities/product-sections/search) молча игнорировались — возвращался код 200 и весь список без фильтра.

**Стало**

Такие фильтры отклоняются с `400 UNSUPPORTED_FILTER`. Фильтруйте точным равенством или `$in` по `id`, `name`, `xmlId`, `code`, `catalogId`, `sectionId`. Сортировка (`order`/`sort`) не изменилась.

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

Вызовы с операторами или `filter[sort]`, ранее возвращавшие 200 с неотфильтрованными данными, теперь возвращают `400` — переключитесь на точное равенство или `$in`.

### FIX-0713-3: причина неудачного первого провижининга сервера теперь видна

**Было**

Если сервер не поднимался при первом создании (вытесняемый тариф вытеснен, нет свободной ёмкости), он молча оказывался в статусе `sleeping` без объяснения причины. Клиент, опрашивающий [GET /v1/infra/servers/:id](/docs/infra/servers/get), видел `sleeping` и не понимал, почему деплой заблокирован.

**Стало**

Такой сервер теперь переходит в `status: "error"` с заполненным `provisionError` (человекочитаемая причина) и новым полем `provisionErrorCode` — машиночитаемой категорией сбоя (`PREEMPTIBLE_EVICTION` / `PROVISION_TIMEOUT` / `NO_CAPACITY` / `GENERIC`). Поле `provisionErrorCode` добавлено в ответы [GET /v1/infra/servers/:id](/docs/infra/servers/get) и [GET /v1/infra/servers](/docs/infra/servers/list) рядом с `provisionError` (аддитивно, `null`, если ошибок не было).

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

Ничего менять не нужно: `error` — уже существующий статус. Восстановление такого сервера — через `POST /v1/infra/servers/:id/start` или `/repair` (не `/wake`: на статусе `error` он вернёт `422`; поле `availableActions` в ответе подсказывает доступное действие).

### NEW-0713-4: POST и PATCH /v1/tasks/:taskId/time принимают createdDate

Необязательное поле `createdDate` теперь передаётся в [POST /v1/tasks/:taskId/time](/docs/entities/tasks/time/create) и [PATCH /v1/tasks/:taskId/time/:itemId](/docs/entities/tasks/time/update) — запись учёта времени ложится на указанную дату, а не на текущий момент (сценарий понедельничного добивания трека за прошлую неделю). Принимаются ISO 8601 со смещением, ISO без смещения и `YYYY-MM-DD` — значение уходит в `CREATED_DATE` без изменений. Если поле не передано, поведение прежнее — дата равна моменту создания.

### FIX-0713-5: meta.hasMore перестаёт зависать в true при filter + offset

**Было**

При пагинации списка с фильтром (например [GET /v1/deals](/docs/entities/deals/list) с `filter` и `offset`) `meta.hasMore` оставался `true` на любом offset — даже далеко за пределами `meta.total`. Цикл `while (meta.hasMore) { offset += limit }` уходил в бесконечность.

**Стало**

Когда Bitrix24 игнорирует смещение за пределами отфильтрованного набора и отдаёт его целиком, `meta.hasMore` считается по окну запроса (`offset + limit < meta.total`), а не выставляется в `true` безусловно. При `offset=0` ответ прежний; после того как окно перекрыло `total`, `hasMore` становится `false`. Затрагивает список и `POST /search` для всех сущностей.

### FIX-0713-6: GET /v1/lists и /v1/lists/:iblockId/elements учитывают offset

**Было**

[GET /v1/lists/:iblockId/elements](/docs/lists/elements) и [GET /v1/lists](/docs/lists/lists) молча игнорировали `offset` — при `?limit=50&offset=50` возвращалась та же первая страница, и клиент, листающий через `offset`, никогда не доходил до строк 51 и дальше.

**Стало**

`offset` мапится в нативный для этих методов параметр `start`, так что пагинация через `offset` работает. Явный `start` сохраняет приоритет, если переданы оба.

### NEW-0713-7: Флаг preserveEnv в деплое сохраняет .env при cleanDeploy

Тело `POST /v1/infra/servers/:id/deploy` приняло необязательный булев `preserveEnv` (по умолчанию `false`). Когда `cleanDeploy: true` (очищает каталог приложения вместе с файлом `.env`), а `preserveEnv: true` — существующий `.env` считывается до очистки и восстанавливается после неё, если в этом же запросе не передан `env` (переданный `env` имеет приоритет). Без флага повторный деплой с `cleanDeploy` мог поднять приложение без переменных окружения — на порту по умолчанию.

### NEW-0713-8: у неудачной сборки galaxy-приложения появилась подсказка `buildHint`

**Было**

Провал сборки galaxy-приложения возвращал только `GALAXY_APP_BUILD_FAILED`
(и `GALAXY_APP_START_FAILED`) c «хвостом» лога в `buildLog` — разобрать причину
можно было лишь вручную. `GET /v1/infra/servers/:id` по ERROR-приложению отдавал
короткий `provisionError`, но без готовой рекомендации.

**Стало**

Ответ теперь несёт разобранную причину. В теле ошибки `POST /v1/infra/servers/:id/deploy`
(502) при классифицируемом провале дополнительно приходят `error.category`
(машинная категория, например `MODULE_NOT_FOUND`, `INSTALL_AUTH`, `RESOURCE`) и
`error.buildHint` — локализованная строка-рекомендация с конкретным следующим шагом.
`GET /v1/infra/servers/:id` по ERROR-приложению добавляет то же поле `data.buildHint`.
Поля аддитивные: если причина не распознана, они отсутствуют (`buildHint` — `null`),
а `provisionError`/`buildLog` продолжают приходить как раньше — старые запросы
не меняются.

### FIX-0713-9: одновременные одинаковые сохранения исходников больше не создают дубль-версию

**Было**

Два одновременных сохранения одинаковых байтов на один сервер (`POST /v1/infra/servers/:id/sources`, а также автосохранение при деплое) при редком стечении таймингов создавали **две** байт-идентичные версии вместо одной — дедупликация по содержимому была best-effort.

**Стало**

Дедупликация детерминированная: одинаковые байты, отправленные одновременно, всегда сходятся на одну версию. На `POST /v1/infra/servers/:id/sources` оба ответа возвращают один и тот же `versionId`, проигравший запрос получает `deduplicated: true`; автосохранение при деплое сходится на ту же единственную версию (формат ответа деплоя не менялся).
