# API changes: September 13, 2026

[← Changelog](/docs/changelog) · [September 2026](/docs/changelog/2026-09)

### FIX-0913-1: the API description now names the refusals shared by every operation

**Before**

The machine-readable description (`/v1/openapi.json`) did not name the refusals produced not by the endpoints themselves but by shared platform layers — and a client generated from the description met them as an unknown answer.

The balance refusal `402 ACCOUNT_FROZEN` (the account is frozen for debt) is possible on any key-authenticated operation except a few informational and service ones — and was described on 22 operations out of 842. The answers `429 RATE_LIMITED` (Bitrix24 limited the call rate), `429 ERROR_LOOP_DETECTED`, `429 QUEUE_OVERFLOW`, `429 QUEUE_TIMEOUT`, `429 OPERATION_TIME_LIMIT`, `429 TIMEOUT_QUARANTINE`, `502 BITRIX_UNAVAILABLE` (Bitrix24 is unavailable) and `503 BITRIX_TIMEOUT` (Bitrix24 did not answer in time) are possible on every operation that calls Bitrix24 — `502 BITRIX_UNAVAILABLE` was described on 11 operations, `503 BITRIX_TIMEOUT` on 4, the rest almost nowhere. The operations of workflow templates, robots and activities (`/v1/bizproc-templates`, `/v1/bizproc-robots`, `/v1/bizproc-activities`) answer a personal key with `403 OAUTH_REQUIRED` — the description was silent; [GET /v1/triggers](/docs/automation/triggers/list) described only success while Bitrix24 answers a personal key with `403 BITRIX_ACCESS_DENIED`. The `400 INVALID_PARAMS` answer to a non-numeric record id in the path was described on the record read of no entity with a numeric id (`GET /v1/tasks/{id}`, `GET /v1/workgroups/{id}` and the rest) nor on most of the task, scrum, timeline and timeline-log sub-resource endpoints.

**After**

Endpoint behaviour is unchanged — the description is corrected. The successful responses remain the same `200`, `201` and `204` they were: the description of the refusals changes, the response does not.

`402 ACCOUNT_FROZEN` is described on exactly the operations the shared freeze gate can refuse: every key-authenticated operation except `GET /v1/me`, `GET /v1/me/sources`, `GET /v1/guide`, the four feedback-conversation calls (`GET /v1/feedback`, `POST /v1/feedback`, `GET /v1/feedback/{id}`, `POST /v1/feedback/{id}/comments`) and `DELETE /v1/cowork/key`. The description states that the refusal arrives before any check of the operation itself and always in the common response envelope (on the OpenAI-compatible AI operations too), and that as the narrowing rolls out the refusal will remain only on wallet-paid operations — a successful answer on a frozen account does not mean the account is clear (the state is `infraState` in the [GET /v1/me](/docs/keys-auth/me) response).

`429`, `502` and `503` with the codes listed above are described on the operations that call Bitrix24: the generated entity operations (list, read, create, update, delete, search, batch, import, aggregate, relations, product rows, actions — except `GET /v1/{entity}/fields`, which answers a Bitrix24 failure with `200`, the static field set and a `fields_partial` warning), the batch call `POST /v1/batch` (with a set of its own: a Bitrix24 rate limit on the envelope itself arrives not as `429` but as `502 BITRIX_UNAVAILABLE` in its usual form or `422 BITRIX_ERROR`, with the Bitrix24 code in `error.bitrixError.error`; `429` there is the batch budget and the queue), and the hand-written families: the task sub-resources, scrum, timeline, timeline log, workday, calls, workflows (`/v1/workflows`), CRM custom fields (`/v1/userfields`), stage history, file upload, duplicate search, `GET /v1/addresses/fields`, `POST /v1/doc-templates`. Where an operation already had a `429` of its own (the read or batch budget, the endpoint's own limit), its own `502` or `503`, the previous text is kept and extended. Each such operation also describes `422 BITRIX_ERROR` — the common answer to a Bitrix24 error that falls under no narrower code.

All operations of the three workflow entities describe `403 OAUTH_REQUIRED`; on writes, next to `WRITE_BLOCKED_READONLY_KEY`. [GET /v1/triggers](/docs/automation/triggers/list) and [POST /v1/triggers/fire](/docs/automation/triggers/fire) describe `401`, `403` (`SCOPE_DENIED`, `BITRIX_ACCESS_DENIED`, and `WRITE_BLOCKED_READONLY_KEY` on firing), `422` and, on firing, `400` (`MISSING_PARAMS`, `INVALID_ENTITY_TYPE`, `INVALID_ENTITY_TYPE_ID`).

`400 INVALID_PARAMS` to a non-numeric record id is described on the record read, update and delete of every entity with a numeric id and on the task, scrum, timeline, timeline-log, call transcript (`GET /v1/activities/{activityId}/transcript`), workgroup member and lead conversion endpoints. The endpoint-specific codes of the same refusal are named where an endpoint answers with them: `INVALID_DYNAMIC_PARAM` (all smart-process operations by `{entityTypeId}`), `INVALID_ENTITY_TYPE_ID` (a smart-process record, smart-process custom fields, CRM card configuration), `INVALID_ROW_ID` (a product row), `INVALID_ANCHOR` (requisite links), `VALIDATION_ERROR` (configurable activities), `INVALID_ID` (an employee photo), `INVALID_BOT_ID` (bot ownership transfer), `UNKNOWN_ENTITY` (CRM custom fields by `{entity}`).

What the description STILL does not name — deliberately: the daily call-quota refusal (`429 QUOTA_EXCEEDED`) is shared by the whole API and is outside this change; on the batch reads that report a Bitrix24 refusal inside the successful answer, per sub-call (`POST /v1/mail/mailboxes/batch`, `POST /v1/tasks/{taskId}/comments/batch`), no response-level Bitrix24 refusal is described — there is none.

The shared answers live in the reusable description components `AccountFrozen`, `PortalRateLimited`, `PortalUnavailable`, `PortalTimeout` and `OauthAppKeyRequired`. The interactive reference (`/docs` → API reference) has been regenerated from the corrected description in both segments. Code details — on the [Errors](/docs/errors) page.

### NEW-0913-2: a Partner Connect application requests the vibe:ai scope itself

`vibe:ai` is now listed among the scopes of a Connect application at registration and on edit — like any Bitrix24 scope, with no request form and no review. From there the scope travels the usual path: the application passes it in the `scope` parameter of `/v1/connect/authorize`, the user sees an "AI models" line on the consent page and confirms the set as a whole, and the issued key carries the scope. AI Router calls made with such a key work, and consumption is covered by the account's monthly AI allowance.

The scope is never added automatically: an application that did not ask for it behaves exactly as before, and previously issued keys keep their scope sets. The other platform scopes — `vibe:search`, `vibe:infra`, `vibe:storage`, `vibe:feedback` — are granted by a platform administrator, and a self-registration request carrying them is rejected with `SCOPE_NOT_ALLOWED`. The `/.well-known/oauth-authorization-server` document lists `vibe:ai` in its `scopes_supported` field.

### FIX-0913-3: product rows of a missing record now answer "not found", like the sibling methods

**Before**

[GET /v1/deals/{id}/products](/docs/entities/deals/products-get) on a nonexistent `id` answered `403 BITRIX_ACCESS_DENIED` — that is how Bitrix24 answers a product-row request for an owner that does not exist, and VibeCode passed the answer through as is. The sibling methods on the same `id` — the record itself, its contacts, writing product rows — answered `404 ENTITY_NOT_FOUND`. A client that received "access denied" went to check permissions instead of checking the `id`. The same for leads, quotes, invoices and smart-process items.

**After**

When Bitrix24 refuses a product-row read, VibeCode checks the record itself once — with the same call `GET /v1/{entity}/{id}` makes. The record does not exist — the answer is `404 ENTITY_NOT_FOUND`, like the sibling methods. The record exists but the employee's permissions restrict it — still `403 BITRIX_ACCESS_DENIED`. Successful requests make no extra call. OpenAPI declares the `404` response for the method; the invoices page no longer describes `403` as the answer for a missing record.

**Affected endpoints:** [GET /v1/deals/{id}/products](/docs/entities/deals/products-get), [GET /v1/leads/{id}/products](/docs/entities/leads/products-get), [GET /v1/quotes/{id}/products](/docs/entities/quotes/products-get), [GET /v1/invoices/{id}/products](/docs/entities/invoices/products-get), [GET /v1/items/{entityTypeId}/{id}/products](/docs/entities/items/products-get).

### FIX-0913-4: the account freeze is checked for an application key even without a user session

**Before**

An application key (`vibe_app_*`) sent without an `Authorization: Bearer` header carrying a user session was not checked against the account freeze: the call went on to the operation's own checks. On a frozen account such a key passed through the doors paid for from the Vibe credits balance — [creating an application](/docs/apps/create) with its paired key, uploading and downloading ticket attachments, [updating a ticket](/docs/feedback/update), [search](/docs/search/run) and [deep research](/docs/search/research), the server list — whereas a personal key and the same application key with a session received `402 ACCOUNT_FROZEN`.

**After**

The freeze is a state of the account, not of the session: on a frozen account an application key without a session answers `402 ACCOUNT_FROZEN` exactly where a personal key does — the set of closed and open calls is described in the ["What the account freeze closes"](/docs/errors) section. The calls open under the freeze — the self-description, the reference, the spec, the support conversation — stay open for such a key too. On an account that is not frozen the responses do not change: the successful response is preserved.

**Impact on integrators**

Nothing changes for the client. If an integration used an application key without a session and suddenly receives `402 ACCOUNT_FROZEN` on a frozen account, the key is not broken — the account is in debt: top up the balance; the account state is visible in the `infraState` block of [`GET /v1/me`](/docs/keys-auth/me).

### FIX-0913-5: the batch call answers 429 ERROR_LOOP_DETECTED instead of 500 when the platform paused it after a series of refusals

**Before**

When the batch calls of one key kept being refused by Bitrix24 and the Vibecode platform temporarily paused them (see [ERROR_LOOP_DETECTED](/docs/errors/limits)), [POST /v1/batch](/docs/batch) itself answered `500 INTERNAL_ERROR` with no `Retry-After` header and no hint — while every other operation in the same state answers `429 ERROR_LOOP_DETECTED`. The client read an internal platform error where the platform's own protective mechanism was at work, and did not know how long to wait.

**After**

[POST /v1/batch](/docs/batch) answers `429 ERROR_LOOP_DETECTED` with a `Retry-After` header, and `error.message` and `error.hint` carry the number of refusals in the last hour and how often a probe request is let through to check that the series is over. The individual calls inside a batch already received this code in `data.errors.<id>` — only the answer to the whole batch changed.

**Impact on integrators**

Nothing to change. A handler that honours the `Retry-After` pause on `429 ERROR_LOOP_DETECTED` now covers the batch call too. If an alert was set on a `500` from the batch call, it stops firing on this state.
