# API changes: July 3, 2026

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

### FIX-0703-1: binding a placement via a developer key no longer returns 500 after an unpublish/republish cycle

**Before**

`POST /v1/placements/bind` for a developer-key-managed app (type `local.*`) could consistently return `500` (`BITRIX_UNAVAILABLE`, `INTERNAL_SERVER_ERROR`) when binding a placement (e.g. `CRM_DEAL_DETAIL_TAB` or `CRM_CONTACT_DETAIL_TAB`) after the app was unpublished and published again. The previous placement registration remained on the Bitrix24 side, and registering a new one on top of it failed with an internal error. Retries returned the same error.

**After**

Before registering the placement, the request now clears its previous registration on the Bitrix24 side, so the bind succeeds even after an unpublish/republish cycle. No change to your call.

### FIX-0703-2: /v1/sites — filtering by knowledge-base (KNOWLEDGE) and group (GROUP) type is no longer ignored

**Before**

`POST /v1/sites/search` (as well as `GET /v1/sites` and `POST /v1/sites/aggregate`) with `filter[type]=KNOWLEDGE` or `filter[type]=GROUP` silently returned ordinary landing sites (`PAGE` / `STORE` / `VIBE`) instead of knowledge bases or group pages. The cause is on the Bitrix24 side: the `landing.site.getList` method binds the `TYPE` filter to an internal area (`scope`), and without the `scope` parameter the `KNOWLEDGE` / `GROUP` types are not part of the default area — so the type filter was silently dropped. The only workaround was to add `scope` manually (see [List sites](/docs/entities/sites/list)).

**After**

When the filter names one such type and `scope` is not passed explicitly, Vibecode supplies the matching area itself (`type=KNOWLEDGE` → `scope=KNOWLEDGE`, `type=GROUP` → `scope=GROUP`) — and the request returns exactly the knowledge bases / group pages. An explicitly passed `scope` always wins and is never overridden. If the type is given as a list or operator (for example `{"type":{"$in":["KNOWLEDGE","PAGE"]}}`), where a single area cannot be chosen, `meta.warnings` carries a hint with code `TYPE_REQUIRES_SCOPE`.

**Impact on integrators**

No action required. Requests with `filter[type]=PAGE` / `STORE` / `VIBE` and requests without a type filter work as before. `MAINPAGE` is an area, not a site type (its sites have type `VIBE`), so `MAINPAGE` is not derived from the type filter. Behavior for `/v1/pages` is unchanged.

### FIX-0703-3: page and site aggregation returns count again

**Before**

[POST /v1/pages/aggregate](/docs/entities/pages/aggregate) and [POST /v1/sites/aggregate](/docs/entities/sites/aggregate) returned `count: 0` and `meta.totalRecords: 0` even when pages and sites existed — in every form: without a filter, with a filter, with a `count` expression, and as the top-level `count` alongside `groupBy`. Per-group counts under `groupBy` were already correct.

**After**

`count` and `meta.totalRecords` reflect the actual number of records; the top-level `count` under `groupBy` equals the sum of the group counts.

**Integrator impact**

No action required — the response is now correct.

### NEW-0703-4: Quality and timestamp parameters in audio transcription

Audio transcription [POST /v1/audio/transcriptions](/docs/ai/audio/transcriptions) accepts five new optional fields. Recognition quality: `prompt` — a context hint (conversation topic, style, correct spelling of terms, up to 2000 characters), `hotwords` — a comma-separated list of special words (rare terms, brands, names, up to 500 characters), `vad_filter` — a silence filter applied before recognition (fewer hallucinations on recordings with pauses). Output control: `temperature` — decoder temperature from 0 to 1, `timestamp_granularities[]` — timestamp granularity `word`/`segment` (only with `response_format=verbose_json`; with `word`, each segment gains a `words` array with per-word timing and probability). The fields are passed in `multipart/form-data` alongside `file` and are compatible with the OpenAI contract. Invalid values are rejected with the `invalid_prompt`, `invalid_hotwords`, `invalid_temperature`, `invalid_vad_filter`, and `invalid_timestamp_granularities` codes.

### FIX-0703-5: Apps created via the API now open correctly as placements

**Before**

Some apps created via [POST /v1/apps](/docs/apps/create) failed to open when their placement was invoked in Bitrix24 — instead of the app interface the user saw an app-resolution error.

**After**

Created apps now resolve correctly and open as placement widgets in Bitrix24. The create response is unchanged — the app is immediately ready for publishing and binding placements.

**Integrator impact**

No action required. Recreate a previously non-opening app (delete and create it again) — the new app opens correctly.

### NEW-0703-6: key deletion is blocked while an agent or bot is linked

[DELETE /v1/keys/:id](/docs/keys-auth) now returns `409` with code `KEY_HAS_LINKED_AGENT` when the key is the control key of a live AI agent or managed bot.

**Before**

Deleting such a key orphaned the agent and cascade-deleted the bot together with its token — the bot's Bitrix24 identity was lost irrecoverably, with no warning.

**After**

Response body: `{ success: false, error: { code: "KEY_HAS_LINKED_AGENT", message, details: { linkedAgentCount, linkedBotCount, agents: [{ id, name, status }] } } }`. Rebind the resources to another key or delete the agent/bot first; to restore access for an orphaned agent, use the "Restore access" action in the dashboard. The check runs before Bitrix24 synchronization — on a `409` the Bitrix24-side credentials are left untouched. Sibling of the existing `KEY_HAS_ACTIVE_SERVERS`.

### NEW-0703-7: Task change history and kanban stages

Two read-only endpoints added (scope `task`). `GET /v1/tasks/:taskId/history` returns a task's full change history in a single call: kanban stage moves, sprint and backlog moves, statuses and other events. Filter by event type via `?field=STAGE` (comma-separate several, e.g. `?field=STAGE,MOVE_TO_SPRINT`); sort via `?order=asc` or `?order=desc` (created-date ascending by default). Each entry carries `id`, `createdDate`, `field`, a `value` object with the previous and new value, and a `user` with the author id. `GET /v1/tasks/stages/:entityId` returns the current kanban columns of a workgroup (`N`) or personal plan (`0`).

### FIX-0703-8: bizproc-activities: clear error instead of "Wrong handler URL" when handler is missing

**Before**

POST /v1/bizproc-activities without a `handler` field (or with the handler URL mistakenly sent under `handlerUrl`) returned the opaque core error `422 BITRIX_ERROR: Wrong handler URL`.

**After**

`code`, `name`, and `handler` are validated before the Bitrix24 call: a missing `handler` now returns `400 MISSING_REQUIRED_FIELDS` with the message `Body field "handler" is required to create bizprocActivity`, pointing at the correct field name. Successful calls that include a valid `handler` are unaffected.

### FIX-0703-9: POST /v1/batch — update and delete responses are now normalized like create and get

**Before**

In the global batch call [POST /v1/batch](/docs/batch) an update sub-call returned its response in a raw wrapper (a nested object instead of the flat record), and a delete sub-call returned an empty array with no success signal. This diverged from create and get in the same endpoint and from single PATCH /v1/{entity}/:id, which return a flat normalized record.

**After**

An update sub-call returns the flat normalized record (camelCase fields) — like create, get and single PATCH. A delete sub-call returns a success signal of the form { id, deleted: true }.

### FIX-0703-10: POST /v1/{entity}/batch — create, update and delete work again for CRM entities

**Before**

A per-entity batch call to [POST /v1/{entity}/batch](/docs/batch) with a create, update or delete action for deals, contacts, companies, leads, quotes and invoices returned a per-item error "Could not find value for parameter {entityTypeId}", and the record was not created, updated or deleted. Single calls (POST /v1/{entity}) and the global POST /v1/batch worked on the same entities.

**After**

Per-entity batch create, update and delete for these entities now succeed — the same way single calls and the global batch endpoint do.

### FIX-0703-11: Omitting the model field in chat again falls back to the default model

**Before**

[POST /v1/chat/completions](/docs/ai/chat/completions) without a `model` field returned `400 no_default_model` on accounts where no default model was explicitly set — even when a free model was available in the account. Meanwhile `GET /v1/me` could report a `defaultModel` that could not be called.

**After**

When the `model` field is omitted, the request automatically uses the account's first callable model — as the docs describe. `GET /v1/me` now always reports a callable `defaultModel`, the same one chat will use.

### FIX-0703-12: include of related entities again returns the entities themselves, not null

**Before**

`GET /v1/deals/:id?include=contact,company` returned `_included.company` = `null` and `_included.contacts` as relation metadata only (`sort`/`isPrimary`/`roleId`) without the entity's own fields, even though the deal referenced an existing company and contacts.

**After**

`_included.company` carries the full company object, and `_included.contacts` carries the full contact objects (`id`, `name`, …) alongside the relation metadata. The fix applies to `include` across CRM entities (`deals`, `leads`, `quotes`, and others).

### FIX-0703-13: deal search rejects an unknown filter field instead of silently returning every row

**Before**

[GET /v1/deals](/docs/entities/deals/list) and [POST /v1/deals/search](/docs/entities/deals/search) with an unknown filter field (a misspelled name, a field not in the schema) silently forwarded it to Bitrix24, which ignores unknown filter keys and returns the whole deal set with a `200`. A client that sent a filter with a typo in the field name got neither an empty result nor a `400`, but the full table — as if the filter had applied.

**After**

An unknown filter field is now rejected before the Bitrix24 call with a `400` and code `UNKNOWN_FILTER_FIELD`, listing the available fields in the message — the same behavior `contacts`, `companies`, `leads`, `quotes`, `invoices`, and `items` already have. Declared fields (including aliases such as `amount`), custom fields (`UF_CRM_*` and `ufCrm*`), and `id` work as before.

### FIX-0703-14: Catalog: listing and search return a clean 400 when iblockId is missing

**Before**

Listing and search [GET /v1/catalog-products](/docs/entities/catalog-products/list), [POST /v1/catalog-products/search](/docs/entities/catalog-products/search), [GET /v1/catalog-sections](/docs/entities/catalog-sections/list) and [POST /v1/catalog-sections/search](/docs/entities/catalog-sections/search) without `iblockId` in the filter reached Bitrix24 and returned a murky `422 BITRIX_ERROR` ("Field iblockId is not specified in the filter").

**After**

Catalog listing and search require `iblockId` in the filter — when it is missing they return `400 MISSING_REQUIRED_FILTER` with an example and never reach Bitrix24.

**Integrator impact**

No change needed — correct requests (with `filter[iblockId]`) work as before. Only the error code and its clarity changed for requests that already failed.

### FIX-0703-15: Contacts — real date fields createdTime/updatedTime instead of phantom createdAt/updatedAt

**Before**

[GET /v1/contacts/fields](/docs/entities/contacts/fields) advertised `createdAt` and `updatedAt`, but they never appeared in contact responses — Bitrix24 returns the dates under `createdTime`/`updatedTime`, and those are what the contact body carried. Meanwhile filter and `select` by `createdTime` (the name a client actually sees in the response) were rejected as an unknown field, while the phantom `createdAt` "worked" even though the field itself was never readable.

**After**

The schema declares the real keys `createdTime` and `updatedTime` (datetime, read-only): they appear in `/fields`, filter and `select` on them work, and the value is normalized to ISO-8601 in UTC. The phantom `createdAt`/`updatedAt` are no longer declared — filter or `select` on them returns `400 UNKNOWN_FILTER_FIELD`.

**Integrator impact**

Reads are unchanged — the `createdTime`/`updatedTime` keys were already in the response body, now also normalized. If you filtered or projected contacts by `createdAt`/`updatedAt`, rename them to `createdTime`/`updatedTime`.

### FIX-0703-16: Open Channels config list now honors the limit parameter

**Before**

[GET /v1/openline-configs](/docs/openlines/config/list) and its search counterpart ignored `limit`: the underlying Bitrix24 method returns the whole set of configurations and the wrapper forwarded every row. The `hasMore` flag was wrong too — `false` even when records remained beyond the requested `limit`.

**After**

The response is clamped to `limit` on the wrapper side. `hasMore: true` when Bitrix24 returned more records than the requested `limit` (a next page exists), otherwise `false`. `total` is the record count in the current window.

**Integrator impact**

A request with `limit` now returns no more than `limit` records. Clients that relied on the whole set coming back regardless of `limit` will see a truncated list — use `offset` for the next page.
