For AI agents: markdown of this page — /docs-content-en/changelog/2026-09-11.md documentation index — /llms.txt
API changes: September 11, 2026
FIX-0911-1: the OpenAPI spec's scope slice no longer pulls in unrelated hand-written sections
Before
The ?scope=<scope> parameter on GET /v1/openapi.json sliced only the generated entities. Hand-written sections — bots, feedback, infrastructure, AI, platform files, search, placements — stayed in EVERY slice in full, regardless of the requested scope. The ?scope=tasks slice weighed 725,100 bytes and contained 284 paths, including the full /v1/apps, /v1/placements, /v1/bots, /v1/feedback sections.
After
The slice keeps the entities of the requested scope, the hand-written routes of the same section, and the service endpoints /v1/me, /v1/guide, /v1/batch — everything else is cut. ?scope=tasks now returns 34 paths instead of 284. Hand-written sections have their own ?scope= values: imbot — bots, vibe:feedback — feedback, vibe:infra — infrastructure, vibe:ai — AI, vibe:storage — platform files, vibe:search — search, placement — placements. Anyone who used to read a foreign section from a foreign slice — bots from ?scope=crm, for example — now gets it with a separate call under that section's own scope: several sections at once need one GET per scope, a comma-separated list still does not union them. The full specification with no ?scope=, and the response to garbage, an empty, or a value that does not collapse to one scope, are unchanged. The apps, applications, cowork, oauth, partner-connect, coupons, portals, app-blueprints, agents families got no addressable ?scope= value of their own and stay in the full specification only.
FIX-0911-2: POST /v1/doc-templates refusals point to the file field, not to Drive
Before
The MISSING_FILE_OR_FILE_ID and UNSUPPORTED_MEDIA_TYPE refusals offered a second route: upload the file to Drive and reference it by fileId. A template created that way could not be rendered — POST /v1/documents answered 422 BITRIX_ERROR with the FILE_NOT_PROCESSABLE marker.
After
Both messages name the working route: the .docx content as a base64 string in the file field. Refusal codes, statuses and trigger conditions are unchanged, only the message text differs. The fileId field is still accepted — the GET /v1/guide description now states that a template built from it cannot be rendered.
NEW-0911-3: the Marketplace trial unavailability reason gained an eighth value
The unavailableReason field inside the activation.marketTrial block of GET /v1/cowork/state accepts a new value, not_required_for_data, in addition to the seven previous ones.
It means the platform no longer treats platform access as a precondition for reading company data with this key, so there is nothing to offer. The Bitrix24 account itself can still refuse in rare cases — keep handling the refusal.
The value is returned only on cloud accounts whose platform access is opened by a paid plan of that kind, and only once the matching platform setting is enabled. The other values keep their meaning, the field is still always present, and the rule "a false value is final, read an unknown value as do-not-offer" is unchanged — thanks to it a client built before this change behaves correctly without an update.
Access is still required for bot calls, deploying and waking applications, creating an application and replacing its key, binding placements and issuing an agent key.
FIX-0911-4: Batch calls preserve selected fields
Before
POST /v1/batch with list and search actions could omit fields from select that a single request returned: task custom fields such as ufCrmTask and declared CRM aliases such as statusId, amount and currency. Selecting tasks by UF_CRM_TASK also dropped the field from single-request responses.
After
The same named select preserves the same fields in single and batch reads. Task custom fields are also preserved when selected by UF_CRM_TASK, including null values.
Impact on integrators
Use named select instead of the select: ["*"] workaround. Custom field spellings for other entities remain unchanged.
FIX-0911-5: audio transcription tells an input error apart from a service failure
Before
POST /v1/audio/transcriptions answered a file whose contents are clearly not audio — text, a document, an image or an archive — with 502 ai_provider_unavailable, as it would a recognition-service failure. Clients retried such a request, and every retry failed again. An unknown ID in the model field was sent to recognition and came back as 400 ai_provider_rejected.
After
Content that is clearly not audio is rejected with 400 invalid_audio before recognition, and nothing is charged. An unknown model ID is rejected with 404 ai_model_not_found before recognition — the same code as on POST /v1/embeddings. The HTTP 200 response for audio files and known models is unchanged.
Impact on integrators
There is no point in retrying a 400 invalid_audio request without replacing the file: show the error to the user instead. A client that does not retry 4xx responses keeps working unchanged. If your handler matches specific codes, add invalid_audio and ai_model_not_found to it.
FIX-0911-6: a write response now hints which fields were not recognised
Before
A typo in a field name in the body of POST /v1/{entity} or PATCH /v1/{entity}/{id} passed silently: the response was successful, and the field — an amount with a misspelled name, say — appeared nowhere.
After
The single-record write response stays successful with the same status, and meta.warnings carries one UNRECOGNIZED_WRITE_FIELD entry per body key that is neither in the entity description nor in your account's field list; field names the key. It is a hint, not a refusal: the record is created or updated as before, meta appears only when there is something to say, and there are at most ten hints per response. When the account's field list is unavailable there is no hint — the Vibecode platform never warns by guesswork.
NEW-0911-7: The flow_ref field in the device-code response
POST /v1/connect/device/authorize returns a new optional flow_ref field — the identifier of THIS sign-in attempt, named the same way by the client and by the Vibecode platform.
It exists for analytics only. One device signs in many times — reconnect, account switch, retry after a refusal — and without a shared name for the attempt, the events of one sign-in cannot be told apart from the next one's. The client stamps flow_ref into its own events and gets an exact match instead of guessing by timestamps.
The field is not a secret, carries no access rights, and user_code cannot be recovered from it. A client that ignores it works exactly as before.
NEW-0911-8: balances export: client identifier, single-account lookup and a delta
Four fields were added to the GET /v1/platform/revenue/balances row. clientType and clientId carry the client identifier as Bitrix24 billing knows it — the same value the Vibecode platform receives in the payment webhook metadata; the pair arrives whole or stays entirely empty. portalNetworkId is the Bitrix24.Network identifier of the account: unlike the domain, it survives a move. updatedAt is when the billing account last changed.
Self-hosted accounts always carry the client identifier. For cloud accounts it is known only from their own payments, so an account that never paid comes back with the pair empty — such rows can be matched by portalNetworkId or by domain.
New query parameters: changedSince (only accounts changed at or after the given moment, UTC ISO-8601 with Z), portalId, portalDomain, and clientId together with clientType (b24 or box). A daily sweep shrinks roughly fivefold, and a single-account card no longer pulls the whole population.
The cursor is now bound to the filter set: continuing a walk with a different filter is refused with 400 INVALID_CURSOR — start the walk again. A cursor issued before this change keeps working for an unfiltered walk. Existing row fields and the walk order are unchanged.
BC-0911-9: the tax amount on deals and quotes is now read-only
Old format supported until: not provided
Before
The taxValue field on deals and quotes was declared writable and accepted with a 200/201 response, but Bitrix24 did not store it: the value stayed 0 on create — including manual-amount mode — on update and on import. There was no way to tell that apart from a successful write.
After
The field is declared server-assigned: Bitrix24 computes it from the product rows. A write is refused before Bitrix24 is called, on every door: create and update return 400 READONLY_FIELD, import returns 400 IMPORT_ITEM_VALIDATION, entity batch returns 400 BATCH_ITEM_VALIDATION, and in the global batch the refusal arrives under the affected call in data.errors with the code READONLY_FIELD while the other calls of the envelope still run. The field description says where the tax is set: through the product rows. Measured 2026-09-11 on live Bitrix24 accounts of both platforms: create and update in both amount modes, import in both modes.
What integrators should do
Remove taxValue from the body of create, update and import requests for deals and quotes — including flows that read a record in full and send it back. The value was never stored; the tax is set through the product rows, and the field stays in read responses.
Affected endpoints: POST /v1/deals, PATCH /v1/deals/{id}, POST /v1/quotes, PATCH /v1/quotes/{id}, POST /v1/{entity}/import, POST /v1/{entity}/batch, POST /v1/batch.
FIX-0911-10: the messenger API description now matches what the endpoints answer
Before
The machine-readable description (/v1/openapi.json) of the chat, notification, knowledge-base and activity-feed endpoints diverged from their behaviour, and a client generated from it broke on the very first call.
GET /v1/chats/find was described with entityTypeId and entityId (integers) — the endpoint reads entityType and entityId (strings, e.g. CRM and DEAL|123) and always answered 400 MISSING_PARAMS to the described form. GET /v1/chats/search was described with a query parameter — the endpoint reads search. POST /v1/posts was described with a message body property — the endpoint requires text.
Thirty-four operations of these families described no refusal at all — only 200, 201 or 204 — while the endpoints answer 400, 401, 403, 404 and 422. A client generator built neither types nor handlers for them. Four operations (POST /v1/chats/{chatId}/files, POST /v1/posts, POST /v1/posts/{id}/comments, DELETE /v1/posts/{id}/comments/{commentId}) described success as 200 while the endpoints answer 201 and 204.
After
As far as parameters and success codes go, endpoint behaviour is unchanged — the description is corrected (the behaviour changes are named separately below). GET /v1/chats/find takes entityType and entityId, required strings; GET /v1/chats/search takes search; the POST /v1/posts body is text (required), title, recipients, files. The success of the four operations is described with the status the endpoint actually answers, 201 and 204. The successful response remains 200, 201 or 204 exactly where it was: the description changes, the response does not.
Every operation of the chat, notification, knowledge-base and activity-feed families, plus GET /v1/bots, POST /v1/bots and GET /v1/bots/revision, now describes the refusals it actually answers, drawn from: 401 (MISSING_API_KEY, INVALID_API_KEY, TOKEN_MISSING), 403 (SCOPE_DENIED, WRITE_BLOCKED_READONLY_KEY on writes, BITRIX_ACCESS_DENIED), 404 (ENTITY_NOT_FOUND plus endpoint-specific codes), 422 (BITRIX_ERROR), and — where the endpoint validates its own input — 400 with its codes (MISSING_PARAMS, MESSAGE_REQUIRED, INVALID_PARAMS, INVALID_POST_ID and others).
Behaviour was straightened along the way as well — both changes are purely in the client's favour, no previously successful call is refused: POST /v1/chats/messages/bulk — a pure bulk read of message history — answered a read-only key with 403 WRITE_BLOCKED_READONLY_KEY, because the generic batch container counted as a write; such a key now reads in bulk exactly as it reads one dialog at a time. Some chat and activity-feed write endpoints called with no request body at all (and no Content-Type header) answered 500; such a call now takes the same path as an empty {} body: where a field is required the endpoint refuses on its own (DELETE /v1/chats/{chatId}/users — 400 MISSING_PARAMS about userId, POST /v1/posts — about text), while chat creation, where every field is optional, forwards the request to Bitrix24 as is. Message editing (PATCH /v1/chats/{dialogId}/messages/{messageId}) is not part of this fix and is straightened in a separate change.
The bot family is described in full: every operation on a specific bot (/v1/bots/{botId}…, except deletion, re-authorization, re-subscription and ownership transfer, which carry their own sets) declares 400 INVALID_BOT_ID, 404 BOT_NOT_FOUND, 410 BOT_DISABLED and 422 — the refusals of the shared bot lookup step and of the Bitrix24 call — as well as 401.
The interactive reference (/docs → API reference) has been regenerated from the corrected description in both segments: the GET /v1/chats/find, GET /v1/chats/search cards and the POST /v1/posts example show the working parameters.
What the description of these operations STILL does not name — deliberately, because the decision belongs to the whole API rather than to the messenger: the balance refusal (402 ACCOUNT_FROZEN) and the 429 RATE_LIMITED, 502 BITRIX_UNAVAILABLE, 503 answers when Bitrix24 is unavailable or overloaded — they are possible on any operation that reaches Bitrix24 and will be declared by one rule across the whole description in a separate change. A client's error handler should be ready for them already.