# API changes: July 8, 2026

[← Changelog](/docs/changelog) · [July 2026](/docs/changelog/2026-07)

### FIX-0708-1: GET /v1/doc-templates and POST /search now honour order and offset

**Before**

The `order` and `offset` parameters on `GET /v1/doc-templates` and `POST /v1/doc-templates/search` were silently ignored: the list always came back sorted ascending by `id`, and `offset` did not move the window. The underlying Bitrix24 method returns templates as an id-keyed object, and the requested order was lost while unwrapping the response.

**After**

Sorting (`order[field]=asc|desc`, including multi-field) and windowing (`offset`/`limit`) are applied on the Vibecode side: the full template set is fetched, sorted, and sliced to the requested window. `total` and `hasMore` are computed from the actually collected set.

**Impact on integrators**

If you relied on the implicit ascending-by-`id` order at `offset=0` with no sort, nothing changes — that stays the default. `POST /v1/doc-templates/batch` (batch list) is unaffected. String order (`name`, `region`) is byte-wise, not locale-aware.

### NEW-0708-2: GET /v1/apps/:id/sources — new linkedServerSources field

[GET /v1/apps/:id/sources](/docs/source-storage) now additionally returns a `linkedServerSources` field — source versions stored under a server (via `POST /v1/infra/servers/:id/sources` or auto-save on deploy), grouped by server, each with its own `serverContext`. Such versions previously did not appear in this response when saved under a personal key — they are now visible.

The field is additive: `versions`, `totalVersions`, `currentVersionId` and `totalSizeBytes` are unchanged and still list only app-scoped versions. Alongside it come `linkedServerSourcesTruncated` (a truncation flag for very large histories) and `linkedServerHint`, which points to `GET /v1/infra/servers/:serverId/sources` — the authoritative full list and download for those versions. The section is populated for the app author (personal key) and the Bitrix24 account administrator; it is empty when called with an OAuth application key, and is not computed on a `?sha256=` probe.

### NEW-0708-3: preemptible field on the server plans list response

The [GET /v1/infra/providers/:id/plans](/docs/infra/providers/plans) response now formally documents the `preemptible` field on each plan. A preemptible plan is cheaper, but the cloud force-restarts such a machine roughly once a day — it is not suitable for continuous 24/7 workloads. For a server, agent or bot that must run without interruption, pick a non-preemptible plan (`preemptible` is `false` or absent).

The field was already returned by the runtime — this entry formalizes it in OpenAPI and the docs; no change to existing integrations is required.

### NEW-0708-4: GET /v1/models/:id now reports a retired model's successor price and a replaced_by field

For a model that has been retired, the by-id detail request now returns a `replaced_by` field with the identifier of the successor model that actually serves the calls, and the `pricing` field shows that successor's price — the price the request is billed at. Previously `pricing` showed the retired row's own zero price, making the model look free while a paid successor served the calls. Ordinary models and existing calls are unchanged.

### FIX-0708-5: list auto-pagination preserves row order for limits above 550

**Before**

Auto-paginated list reads — `GET /v1/{entity}?limit=…` and `POST /v1/{entity}/search` — returned rows out of order when `limit` exceeded ~550: internal result pages were merged in the wrong sequence relative to what Bitrix24 returned, so the `order` parameter was not honored across the final array. When the collection held more rows than `limit`, the window trim could drop rows from the middle of the sorted set while keeping later ones.

**After**

Pages are merged strictly in Bitrix24 return order: rows arrive in the requested sort for any `limit`, and the `limit` trim no longer drops rows from the middle of the set because of wrong merge order.

**Impact on integrators**

No changes required. If you re-sorted large result sets on your side as a workaround, that is no longer needed.

### FIX-0708-6: auto-pagination no longer silently drops a page when a batch sub-request fails

**Before**

With `limit > 50` the list is assembled from batch sub-requests of 50 records each. If Bitrix24 rejected one sub-request (most often on its request limit — `QUERY_LIMIT_EXCEEDED`), that page silently fell out of the middle of the set: the response stayed `200` with an undetectable 50-record hole in the data (e.g. records 1–200 and 251–600 without 201–250), while `meta.total` and `meta.hasMore` looked self-consistent.

**After**

For lists, plain search, batch sub-calls and aggregations the result is always a contiguous prefix of the set: records past the failed page are dropped, `meta.hasMore` stays `true`, and the response carries `meta.pageErrorSample { code, message }` with the failure reason — mirroring windowed search's `meta.windowErrorSample`. The field is added to list responses (e.g. [GET /v1/deals](/docs/entities/deals/list)), to `POST /v1/{entity}/search` (e.g. [deals](/docs/entities/deals/search)), to per-sub-call `meta` of [POST /v1/batch](/docs/batch) and to `data.meta` of `POST /v1/{entity}/aggregate` (where it explains why `recordsProcessed` is below `totalRecords`). In windowed search (a wide date range) the set is assembled from windows, so a page lost inside one window can leave that window's tail missing — there the incompleteness signal is `meta.pageErrorSample` itself, not `meta.hasMore`. In all cases the field appears only when the returned page is actually shorter than `limit`: a full page is never flagged with a false alarm. Incomplete responses are never cached: a retry goes straight to Bitrix24.

**Impact on integrators**

No client changes required: result sets that could previously contain an invisible hole are now correct, and the shortfall reason is visible in `meta.pageErrorSample`. Fetch the remainder by retrying with `offset` equal to your original `offset` plus the number of records received — except for windowed searches over a wide date range (`offset` is not supported there: narrow the range or retry later).

### BC-0708-7: structured output: a truncated or empty result now returns 422 instead of an empty 200

> Old format supported until: 08.07.2026

**Before**

[POST /v1/chat/completions](/docs/ai/chat/completions) with `response_format` (`json_object` or `json_schema`) could return `200` with `content: null` (or a truncated, unparseable JSON string) plus a warning that clients ignored, when generation was cut off. This happened most often on reasoning models: the reasoning phase consumed the whole `max_tokens` budget before the model produced the JSON. The response looked successful but could not be parsed.

**After**

Such a request now returns `422` with `code: "structured_output_truncated"`, plus `finishReason`, `suggestedMaxTokens` (a larger `max_tokens` to retry with), and `param: "max_tokens"`. In streaming mode a `{"error":{"code":"structured_output_truncated"}}` frame is emitted before `data: [DONE]` — read the stream through to `[DONE]`. Additionally, for free reasoning models given a too-small `max_tokens` the platform raises the budget to a safe minimum and tags the successful response with a `MAX_TOKENS_RAISED` warning. The truncated attempt still consumes and bills tokens.

**What integrators should do**

Handle `422 structured_output_truncated` in your error branch and retry with a larger `max_tokens` (you can use the `suggestedMaxTokens` value). For strictly deterministic JSON, set a generous `max_tokens` or use a non-reasoning model.
