# API changes: October 5, 2026

[← Changelog](/docs/changelog) · [October 2026](/docs/changelog/2026-10)

### FIX-1005-1: the list of plans allowed on a trial can hold several plans

**Before**

On trial access, `POST /v1/infra/servers` answered `402 PLAN_NOT_ALLOWED_ON_TRIAL` for any plan but a single allowed one, and `GET /v1/me` named that one plan in `capabilities.servers.create.limits.allowedPlans` and in `note`.

**After**

The list can hold several plans: `limits.allowedPlans` in `GET /v1/me` and `error.details.allowedPlans` in a `402 PLAN_NOT_ALLOWED_ON_TRIAL` response list all of them, and `note` names each. Read the list from the response instead of hardcoding one plan.

### FIX-1005-2: repeating an event subscription no longer returns an error

**Before**

Repeating [POST /v1/infra/servers/:id/event-subscriptions](/docs/infra/event-subscriptions) for an event whose handler was already registered in Bitrix24 (a retry after a failure, an `appPath` change) returned `502 BIND_FAILED`, and the subscription was not saved. If another app installer subscribed again, Bitrix24 added a second registration and every event reached the app twice.

**After**

The repeated call returns HTTP 200 and updates the subscription: an already registered handler counts as success. Extra registrations of the same event under another user are removed, so the event arrives once.

**Impact on integrators**

No action required. Retrying a request after `502 BIND_FAILED` is now safe.

### FIX-1005-3: sleeping-server deploy operation is visible during wake

**Before**

[POST /v1/infra/servers/:id/deploy](/docs/infra/deploy/deploy) created an addressable dedicated-virtual-machine operation only after wake. If the connection dropped or wake was refused, the run could not be found through the operation list and the error response carried no operation id.

**After**

After successful authorization, the run is visible in the operation list as `running` during wake, including before the client receives an HTTP response. If wake or later preparation is refused, a response with an operation carries its id in `X-Vibe-Operation-Id` and `error.operationId`. The list reports the refusal code if the asynchronous outcome write succeeds. Operation tracking remains best-effort: just after the response it may remain `running`, and a failed write may leave it `unknown` without a refusal code after expiry. A missing id does not prove that the deploy never started.

**Impact on integrations**

When a response is lost, find the latest run through the server operation list and read its outcome before repeating the deploy.

### FIX-1005-4: Reading pages without explicit field selection

**Before**

`GET /v1/pages`, `GET /v1/pages/:id` and `POST /v1/pages/search` without an explicit `select` could return `422 BITRIX_ERROR` with the Bitrix24 code `SYSTEM_ERROR`.

**After**

The `domainId` field is no longer requested by default, so reading pages does not fail with this error. Existing ordinary requests need no changes. The field remains in `GET /v1/pages/fields`.

In the list (`GET /v1/pages`) and search (`POST /v1/pages/search`), an explicit selection of `domainId` is forwarded to Bitrix24 and can still return the stated error.

In the get-by-ID endpoint (`GET /v1/pages/:id`), `domainId` is not returned even with an explicit `select`: the endpoint requests the default fields from Bitrix24, and `select` only narrows the response. The `id` field is always returned.

### BC-1005-5: the task comment list reads the legacy store and paginates it

> Old format supported until: not provided

**Before**

[GET /v1/tasks/:taskId/comments](/docs/entities/task-comments/list) read only the chat on a task that has one. Comments written before the account moved to the new task card never migrate into the chat, so such a task answered `200` with an empty `data` while its comments were alive — and the client could not tell "there are no comments" from "we looked in the wrong place". The single [GET /v1/tasks/:taskId/comments/:id](/docs/entities/task-comments/get) found those same comments.

When the answer came from the legacy store, `limit` and `offset` were not applied to it: whatever Bitrix24 returned in one call was passed on whole, and `meta.hasMore` on such a response was always `false`.

**After**

When the chat is read to its end and yields no comment, the list reads the legacy store and answers from it — one extra Bitrix24 call, and only where the answer would otherwise be empty.

`meta` gained a `sources` field — the array of stores whose contents the response learned. A store is listed when it returned comments, or when it was read to the end and proved empty; a store the response learned nothing from is not listed. Being listed does not mean "read in full": completeness is carried by `meta.truncated`, and the chat is listed together with `truncated: true` when its walk broke off after collecting at least one comment. A conclusive "the task has no comments" comes from the set `["chat", "legacy"]` with no `meta.truncated`. The set `["legacy"]` means the chat was NOT READ — the method refused, the scope is missing, the response shape was unreadable, or the walk broke off. ⚠️ Such an answer MAY CARRY NO `truncated`: the flag speaks only for what WAS read being read in part, while an unread chat is reported by the set itself. So `if (!meta.truncated) → the answer is complete` is wrong: judge by the set AND the flag together. A task with no chat allotted is NOT that case: chat comments cannot exist there, so the set is complete. Only a fresh check proves it — within the five-minute cache the same task honestly answers `["legacy"]`, because a chat may have been allotted meanwhile. `["chat"]` means the legacy store did not answer, and `[]` means neither did.

`limit` and `offset` now apply to an answer from the legacy store as well: you get the page you asked for rather than everything Bitrix24 returned in one call. The response remains `200`.

⚠️ The Bitrix24 legacy store returns at most 50 comments per call and offers no navigation over them. The two `meta` fields therefore answer DIFFERENT questions. `meta.hasMore` — "the set this response was built from holds further rows", and you walk it with `offset`. `meta.truncated` — "the call came back full, and more comments may sit behind it with no way to ask for them". `hasMore: false` together with `truncated: true` means "you reached the end of the window, but not the end of the task's comments".

On that path `meta.total` is the Bitrix24 counter, and it does NOT always count the whole task: `filter` is passed to Bitrix24 as is, so with a filter the number counts the matches. Without a filter it does NOT become a task-wide counter either: `task.commentitem.getlist` is called once, with no `start`/NAV, and when Bitrix24 returns no counter of its own, `total` degenerates into the row count of that single page. It may exceed the length of `data`; it cannot reach past that one call. Read it together with `truncated`: "this many exist for this request, this many arrived, that is not all". ⚠️ Do not read `total` as "the task has this many comments" — neither with a `filter` nor without one. Judge completeness by `truncated` and `meta.sources`, not by comparing against `total`.

**What integrators should do**

If you passed `limit` or `offset` on a task whose comments live in the legacy store, they used to have no effect and you received everything Bitrix24 returned in one call. You now receive exactly the page you asked for — check those places.

On a long legacy history, do not read `meta.hasMore: false` as the end of the list: on this path the field describes only what arrived in one Bitrix24 call. This endpoint does not currently return the full set of comments for such a task.

### FIX-1005-6: OpenAPI describes object filter serialization

**Before**

OpenAPI omitted the serialization settings for some object query filters. A client generated from the specification could send fields outside `filter`.

**After**

Pure-object query parameters declare `style: deepObject` and `explode: true`: for example, `filter[amount]=1000`. This covers generic entity lists, `/v1/requisite-links`, `/v1/openline-configs` and `/v1/voximplant-sip`. Nested objects and arrays remain an extension whose support depends on the client; use `POST /search` for complex filters in entity lists.

### BC-1005-7: overall per-minute request limit per key and per key owner

> Old format supported until: not provided

**Before**

There was no overall limit per client: limits were counted separately on each method.

**After**

Requests to `/v1` are counted in a shared counter: 600 per minute per key and 1200 per minute across all keys of one owner. Bot event polling (`GET /v1/bots/{botId}/events`) is counted by separate counters and does not spend the shared counter of other requests: 120 per minute per bot, plus, with the same thresholds, 600 across all polls of one key and 1200 across all polls of the keys of one owner. On overflow the answer is `429` with the code `CALLER_RATE_LIMITED`. The `X-RateLimit-Scope` header names the counter that fired (`key`, `user`, `bot`) and `Retry-After` gives the seconds until the one-minute window ends. Retrying earlier does not extend the window. An app key is counted differently: 600 per minute for each app user in a Bitrix24 account, with no shared owner counter. AI methods are not part of this counter, except AI provider key management (`/v1/ai/credentials` and `/v1/ai/providers`), which is counted like any other request. The named numbers are platform-wide ceilings: they are divided across backend replicas, so a client on one connection can be refused earlier than the named number, and the effective number is not published in a header. Details — [Limits, queues, and pauses](/docs/errors/limits).

**What integrators should do**

On a `429` with the code `CALLER_RATE_LIMITED`, wait for the time from `Retry-After` and retry the request, retrying a write is safe. For exports, read records in pages through search and combine calls with `POST /v1/batch`. Keep the pace well below the ceilings (120 per minute per bot, 600 per key, 1200 per owner) and wait `Retry-After` on a `429`.

### NEW-1005-8: Read-only mode for Cowork chats: chat keys and the mode choice

The Cowork desktop app gets three methods for read-only chats. `GET /v1/cowork/portal-access` and `PATCH /v1/cowork/portal-access` read and change the mode for the next chats of the person in this Bitrix24 account: read-only or changes allowed. A change sends the version it read and gets `409 COWORK_PREFERENCE_CONFLICT` when that version is stale. `POST /v1/cowork/chat-key` issues a chat key of the chosen mode, and the desktop app hands it to the agent instead of its own key. A chat key is never wider than the current rights of the desktop key and stops working together with it (`401 COWORK_CHAT_KEY_INACTIVE`). A read-only chat key does not change Bitrix24 data: such a call gets `403 COWORK_READONLY` before anything reaches Bitrix24. A chat key does not manage keys or the mode choice (`403 COWORK_CHAT_KEY_FORBIDDEN`), and [GET /v1/me](/docs/keys-auth/me) called with it returns the `coworkPortalAccess` field with the effective mode of the chat. Only the Cowork desktop key may call the new methods, any other key gets `403 COWORK_DESKTOP_KEY_REQUIRED`. Until the feature is enabled for the account, all three methods answer `403 COWORK_CHAT_KEY_DISABLED`.

### NEW-1005-9: chats of one project as a separate list

The new endpoint [GET /v1/chats/projects/{projectId}/recent](/docs/chats/discovery/project-recent) returns the chats of one project (collab) from the recent list: the project's own chat and the chats nested in it. `projectId` is the chat id of the project, the same `chatId` as in its row of [GET /v1/chats/recent/collabs](/docs/chats/discovery/recent-collabs). It requires the `im` scope.

The response is built like that of [GET /v1/chats/recent](/docs/chats/discovery/recent) with `format=v2` — `recentItems`, `chats`, `users`, `messages`, `files`, `hasNextPage` — and on the first page it also carries the `data.sectionMeta` object with the project details. A non-empty `data.sectionMeta.fixedChatIds` shows that `projectId` points at a project chat: the ID of any other chat answers `200` with empty collections. In `data.chats` the project's own chat has `parentChatId` equal to `0`, and the nested chats have it equal to `projectId`. Paging works as in the collab list: `limit` from 50 to 200, the next page by `lastMessageDate`. The endpoint accepts no other query parameters and answers `400 INVALID_PARAMS`.

The first page is not a pure read: on the first read of a project Bitrix24 creates the CoPilot chat of that project for the key owner, once per project and for members only. A key whose access mode forbids writes therefore gets `403 WRITE_BLOCKED_READONLY_KEY` on the first page. The pages with `lastMessageDate` only read and stay available to such a key.

### BC-1005-10: device limit on Cowork/Code desktop key issuance and the COWORK_DEVICE_LIMIT_REACHED code

> Old format supported until: not provided

**Before**

Issuing a Cowork/Code desktop key through Connect (device-code sign-in, the consent page, the Atlas token exchange) did not limit the number of live desktop keys of a user and account pair: every sign-in added a new key.

**After**

A user and account pair may hold at most 10 live desktop keys. Revoked, blocked and expired keys take no slot, and reconnecting the same installation spends none.

Above the limit a sign-in from a client that does not report an installation id revokes the longest-unused keys of such clients without an installation id, as many as needed to get back to the limit (usually one). If the pair holds too few such keys, including when every slot is held by keys with an installation id, issuance is refused with `COWORK_DEVICE_LIMIT_REACHED` (HTTP 409, `details.used` and `details.limit`). On the return to `redirect_uri` the app receives `error=access_denied` with this code in `error_description`.

**What integrators should do**

Report the installation id on sign-in: reconnecting the same computer then spends no slot, and the client's keys are not evicted. On `COWORK_DEVICE_LIMIT_REACHED`, tell the person to sign out an unused device in the Cowork/Code section of the Vibecode cabinet, and repeat the sign-in.

### BC-1005-11: auto-sleep no longer stops an application with a fetch Bot

> Old format supported until: not provided

**Before**

An ordinary server or Galaxy application with an enabled fetch Bot could store a numeric idle threshold and read as `IDLE`. Requests to [PATCH /v1/infra/servers/:id/sleep](/docs/infra/lifecycle/sleep) and [PATCH /v1/infra/servers/:id/run-mode](/docs/infra/lifecycle/run-mode) accepted this mode even though outbound Bitrix24 polling did not count as inbound activity and the application went to sleep.

**After**

[GET /v1/infra/servers](/docs/infra/servers/list) and [GET /v1/infra/servers/:id](/docs/infra/servers/get) return the new `idleSleepProtected` field. It is `true` for an application with an enabled fetch Bot, and its mode without a schedule is `ALWAYS` even when a numeric threshold is stored. A numeric `sleepAfterMinutes` or the `IDLE` mode now returns `400 AGENT_IDLE_SLEEP_FORBIDDEN`. This also applies to [POST /v1/infra/servers](/docs/infra/servers/create) with explicit `runMode: IDLE` when the key is associated with an enabled fetch Bot: the refusal arrives before a server is created. `null` and the `SCHEDULE` mode remain available. When an assigned schedule is deleted with `reassign=true`, such an application moves to effective `ALWAYS` and retains its idle threshold; agent and bot servers move to `ALWAYS` with their threshold cleared.

**What integrators should do**

Check `idleSleepProtected` before changing auto-sleep. When it is `true`, use `null` for around-the-clock operation or assign a schedule. When creating a server with a key associated with an enabled fetch Bot, do not request `runMode: IDLE`. Handle `AGENT_IDLE_SLEEP_FORBIDDEN` on both create and update. Before deleting an assigned schedule with `reassign=true`, account for protected applications switching to around-the-clock operation.

### FIX-1005-12: request examples in the reference and the specification no longer show a body the route does not accept

**Before**

Generated examples on the Vibecode platform printed array and object fields empty where sending such a body makes no sense.

In the update examples: [PATCH /v1/bookings/{id}](/docs/entities/bookings/update) printed `"resourceIds":[]` and [PATCH /v1/bizproc-templates/{id}](/docs/entities/bizproc-templates/update) printed `"documentType":[]`. A reader who copied such an example to rename a record got one of two outcomes instead of a result: the booking answered `422` with the `BITRIX_ERROR` code and the `Empty resource collection` message, while on a business process template the `documentType` field is not applied by an update at all — the request returned `200` and left the document type unchanged.

In the create examples, required fields were printed empty: [POST /v1/bookings](/docs/entities/bookings/create) sent `"resourceIds":[]` and `"datePeriod":{}`, [POST /v1/bizproc-templates](/docs/entities/bizproc-templates/create) sent `"templateData":[]` and `"documentType":[]`. Those requests were refused as well: `templateData` with the `MISSING_REQUIRED_FIELDS` code, the others on the Bitrix24 side.

**After**

The update example does not show fields that are replaced as a whole — neither arrays nor objects with a described nested structure. The create example shows working values for required fields: the list of resource identifiers, the booking period built from `from` and `to`, and the document type as module, object and type. The template file carries an explicit placeholder, `<base64-encoded .bpt file contents>` — the caller supplies the contents of their own file, and the sample is written so that it cannot be mistaken for a ready-to-send value. The same values now appear in the `GET /v1/openapi.json` specification as the `example` of those fields and in the response examples. The one exception is `documentType` on a business process template: the write schema carries no example for it, because an update does not apply the field and a ready-to-send value there would read as an invitation to send a write that answers `200` and changes nothing. The read schema keeps the example, and the field description states the behaviour in words on both schemas. An empty array stays where it is meaningful — on optional fields it still means "the list is empty".

**Impact on integrators**

Route behaviour is unchanged for every operation listed: these bodies were refused or ignored before as well, and the error codes and messages are the same. Nothing has to change in working code. A client generated from the specification will see a filled-in `example` on the fields listed above.

### FIX-1005-13: OpenAPI describes every address route

**Before**

OpenAPI and the API reference described only [GET /v1/addresses/fields](/docs/entities/addresses/fields) for addresses. The list, search, create and composite-key operations worked, but client generators and AI agents reading the specification could not see them.

**After**

The full specification, the `crm` slice and both reference languages describe [GET /v1/addresses](/docs/entities/addresses/list), [POST /v1/addresses](/docs/entities/addresses/create), [POST /v1/addresses/search](/docs/entities/addresses/search), and [GET](/docs/entities/addresses/get), [PATCH](/docs/entities/addresses/update) and [DELETE](/docs/entities/addresses/delete) `/v1/addresses/{typeId}/{entityTypeId}/{entityId}`. Route behaviour is unchanged. Regenerate your client from the current specification to expose the operations.

### BC-1005-14: deleting a call log entry searches the 5000 newest entries

> Old format supported until: not provided

**Before**

[DELETE /v1/calls/log/{callId}](/docs/calls/log/delete) deleted an entry at any depth of the personal call log: before deleting, the wrapper read the journal page by page until it found the entry or reached the end, with no limit on the number of pages. The response was `200`.

**After**

Before deleting, the 5000 newest journal entries are searched (at most 50 pages). An older entry returns `422 CALL_LOG_PREREAD_LIMIT_EXCEEDED`, and nothing is deleted: such an entry cannot be deleted via the API. For entries among the 5000 newest the response remains `200`; a missing entry still returns `404 ENTITY_NOT_FOUND`.

**What integrators should do**

Treat `422 CALL_LOG_PREREAD_LIMIT_EXCEEDED` as a final refusal and do not retry.

### NEW-1005-15: Calendar resources, availability and meeting RSVP

Added [calendar resources](/docs/entities/calendar-resources): list, create, rename and delete; [resource bookings](/docs/entities/calendar-resources/bookings) and [attendee availability](/docs/calendar/accessibility). Timed intervals return ISO-8601 dates with timezone offsets; all-day intervals return civil dates without a timezone. [Meeting RSVP](/docs/entities/calendar-events/meeting-status) reads or changes the current credential owner response: accepted, declined or invited. Requires calendar scope; READONLY keys cannot write. A missing resource or event returns 404 ENTITY_NOT_FOUND; an unconfirmed status write returns 422 BITRIX_ERROR.

### FIX-1005-16: OpenAPI describes employee deactivation

**Before**

The working `DELETE /v1/users/{id}` was missing from OpenAPI and the VibeCode API reference, so client generators could not discover the operation.

**After**

The operation is included in the full specification, the `user` slice and both reference languages. The response remains HTTP 200 with `success: true` and `data: { id, active: false, deactivated: true }`; the `user` field is optional. No request body is required. This is [employee deactivation](/docs/entities/users/delete) through `ACTIVE=N`, reversible through `PATCH /v1/users/{id}` with `active: true`. Regenerate your client from the current specification to expose the operation.
