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

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

### FIX-0708-1: GET /v1/doc-templates и POST /search честят order и offset

**Было**

Параметры `order` и `offset` на `GET /v1/doc-templates` и `POST /v1/doc-templates/search` молча игнорировались: список всегда возвращался в порядке возрастания по `id`, а `offset` не смещал окно выборки. Причина — метод Bitrix24 отдаёт шаблоны объектом с ключами-`id`, и заданный порядок терялся при разворачивании ответа.

**Стало**

Сортировка (`order[поле]=asc|desc`, в том числе по нескольким полям) и постраничная выборка (`offset`/`limit`) применяются на стороне Vibecode: набор шаблонов вытягивается полностью, сортируется и режется по запрошенному окну. `total` и `hasMore` считаются от фактически собранного набора.

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

Тем, кто полагался на неявный порядок «по возрастанию `id`» при `offset=0` без сортировки, менять ничего не нужно — это остаётся поведением по умолчанию. `POST /v1/doc-templates/batch` (batch-list) не затронут. Строковый порядок (`name`, `region`) — побайтовый, без учёта локали.

### NEW-0708-2: GET /v1/apps/:id/sources — новое поле linkedServerSources

[GET /v1/apps/:id/sources](/docs/source-storage) теперь дополнительно возвращает поле `linkedServerSources` — версии исходников, сохранённые под сервером (через `POST /v1/infra/servers/:id/sources` или авто-сохранение при деплое), сгруппированные по серверу, каждая со своим `serverContext`. Такие версии раньше не попадали в этот ответ, если сохранялись под личным ключом, — теперь они видны.

Поле аддитивное: `versions`, `totalVersions`, `currentVersionId` и `totalSizeBytes` не изменились и по-прежнему перечисляют только версии, привязанные к приложению. Рядом приходят `linkedServerSourcesTruncated` (признак усечения при очень большом числе версий) и `linkedServerHint` с указателем на `GET /v1/infra/servers/:serverId/sources` — авторитетный полный список и скачивание этих версий. Секция заполняется для автора приложения (личный ключ) и администратора портала; при вызове ключом OAuth-приложения она пуста, а при `?sha256=`-пробе не вычисляется.

### NEW-0708-3: поле preemptible в ответе списка тарифов серверов

Ответ [GET /v1/infra/providers/:id/plans](/docs/infra/providers/plans) теперь формально описывает поле `preemptible` у каждого тарифа. Вытесняемый тариф дешевле, но облако принудительно перезапускает такую машину примерно раз в сутки — он не подходит для непрерывных 24/7-нагрузок. Для сервера, агента или бота, которые должны работать без перерывов, выбирайте невытесняемый тариф (`preemptible` равно `false` или отсутствует).

Поле уже отдавалось в ответе рантаймом — эта запись фиксирует его в OpenAPI и документации; менять существующие интеграции не требуется.

### NEW-0708-4: GET /v1/models/:id теперь показывает цену преемника у снятых моделей и поле replaced_by

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

### FIX-0708-5: автопагинация списков сохраняет порядок строк при limit выше 550

**Было**

Списочные запросы с автопагинацией — `GET /v1/{entity}?limit=…` и `POST /v1/{entity}/search` — при `limit` выше ~550 возвращали строки с нарушенным порядком: внутренние страницы выдачи склеивались не в том порядке, в котором их отдал Битрикс24, поэтому параметр `order` на итоговом массиве не соблюдался. Если записей было больше, чем `limit`, обрезка окна могла выбросить строки из середины отсортированной выборки, оставив более поздние.

**Стало**

Страницы склеиваются строго в порядке выдачи Битрикс24: строки приходят в заказанной сортировке при любом `limit`, а обрезка по `limit` больше не выбрасывает строки из середины выборки из-за неверного порядка склейки.

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

Менять ничего не нужно. Если вы пересортировывали большие выборки на своей стороне как обходной путь — это больше не требуется.

### FIX-0708-6: автопагинация больше не теряет молча страницу при сбое пакетного подзапроса

**Было**

При `limit > 50` список собирается пакетными подзапросами по 50 записей. Если Битрикс24 отклонял один подзапрос (чаще всего по лимиту запросов — `QUERY_LIMIT_EXCEEDED`), его страница молча выпадала из середины выборки: ответ оставался `200`, в данных образовывалась необнаружимая дыра в 50 записей (например, записи 1–200 и 251–600 без 201–250), а `meta.total` и `meta.hasMore` выглядели непротиворечиво.

**Стало**

Для списков, обычного поиска, пакетных подвызовов и агрегаций результат — всегда непрерывный префикс выборки: записи после сбойной страницы отбрасываются, `meta.hasMore` остаётся `true`, и в ответе появляется `meta.pageErrorSample { code, message }` с причиной сбоя — по образцу `meta.windowErrorSample` оконного поиска. Поле добавлено в ответы списков (например [GET /v1/deals](/docs/entities/deals/list)), в `POST /v1/{entity}/search` (например [сделки](/docs/entities/deals/search)), в `meta` подвызовов [POST /v1/batch](/docs/batch) и в `data.meta` агрегаций `POST /v1/{entity}/aggregate` (там оно объясняет, почему `recordsProcessed` меньше `totalRecords`). В оконном поиске (широкий диапазон дат) набор собирается из окон, поэтому при потере страницы внутри одного окна ответ может недосчитаться хвоста этого окна — признак неполноты там именно `meta.pageErrorSample`, а не `meta.hasMore`. Во всех случаях поле появляется, только если итоговая страница действительно короче `limit`: полный ответ ложным сигналом не помечается. Неполный ответ не кэшируется: повторный запрос сразу идёт в Битрикс24.

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

Менять клиентский код не нужно: выборки, которые раньше могли содержать незаметную дыру, теперь корректны, а причина недобора видна в `meta.pageErrorSample`. Дочитать остаток можно повторным запросом с `offset`, равным сумме исходного `offset` и числа полученных записей, — кроме оконного поиска по широкому диапазону дат (там `offset` не поддерживается: сузьте диапазон или повторите запрос позже).

### BC-0708-7: структурированный вывод: обрезанный или пустой ответ теперь возвращает 422, а не пустой 200

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

**Было**

[POST /v1/chat/completions](/docs/ai/chat/completions) со `response_format` (`json_object` или `json_schema`) при обрыве генерации мог вернуть `200` с `content: null` (или обрезанной, непарсимой строкой JSON) и предупреждением, которое клиенты не замечали. Чаще всего это случалось на моделях рассуждения: фаза рассуждения расходовала весь бюджет `max_tokens` до того, как модель писала JSON. Ответ выглядел успешным, но разобрать его было нельзя.

**Стало**

Такой запрос возвращает `422` с `code: "structured_output_truncated"`, полями `finishReason`, `suggestedMaxTokens` (рекомендованный увеличенный `max_tokens` для повтора) и `param: "max_tokens"`. В потоковом режиме перед `data: [DONE]` приходит служебный кадр `{"error":{"code":"structured_output_truncated"}}` — читайте поток до `[DONE]`. Дополнительно: для бесплатных моделей рассуждения при слишком маленьком `max_tokens` платформа поднимает бюджет до безопасного минимума и помечает успешный ответ предупреждением `MAX_TOKENS_RAISED`. Обрезанная попытка по-прежнему расходует и тарифицирует токены.

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

Обрабатывайте `422 structured_output_truncated` в ветке ошибок и повторяйте запрос с бóльшим `max_tokens` (можно взять значение из `suggestedMaxTokens`). Для строго-детерминированного JSON задавайте `max_tokens` с запасом или используйте обычную (не «thinking») модель.
