For AI agents: markdown of this page — /docs-content-en/changelog/2026-08-10.md documentation index — /llms.txt
API changes: August 10, 2026
NEW-0810-1: issuing a share link is recorded in the account access journal
The POST /v1/infra/servers/:id/access-tokens response is unchanged — what changes is what happens on the account side.
Before
Issuing a mode=share-url link left no trace in the account oversight layer: an administrator could not see who opened an application with a link, or when.
After
After a link is issued, the platform records the exposure change in the account journal, and — when the link does not require a Bitrix24 sign-in (identityBound=false) and the application was not already open to outsiders — sends the account administrators a chat-bot message linking to the list of open applications.
Impact on integrators
The request format, the response and the error codes are unchanged — no client change is needed. Note that every open link you issue is now visible to the account administrators.
BC-0810-2: a filter error in POST /v1/{entity}/batch is now a per-sub-call error, not a whole-request one
Old format supported until: 04.02.2027
Before
One bad filter key in any sub-call of POST /v1/{entity}/batch cancelled the
whole request: 400, the code in error.code, and the results of every other sub-call
discarded. The global POST /v1/batch behaved differently — it placed the error
next to the sub-call that caused it and ran the rest.
After
Both surfaces behave the same way. The response is 200, the refusal code sits in
data[i].error.code for the sub-call that caused it, and the remaining sub-calls run and
return their data. The set of refusals and their codes is unchanged — only the blast radius
is: UNKNOWN_FILTER_FIELD, INVALID_FILTER_OPERATOR, INVALID_FILTER_FIELD,
UNSUPPORTED_FILTER.
The message used to be prefixed with Call at index N: — the sub-call's position is now
visible from its place in the data array, so the prefix is gone.
Integrator migration
If your code reads a filter refusal as HTTP 400 with error.code, add handling for 200
with data[i].error.code — otherwise a sub-call whose filter was refused will read as
successful. Check each data element for an error, exactly as you already do for the
global POST /v1/batch.
BC-0810-3: batch reads no longer bypass an operation the entity turns off
Old format supported until: 04.02.2027
Before
An entity can turn a single operation off — usually because the generic handler is wrong for
it: the real list lives on its own route with a different envelope, or the Bitrix24 method
ignores the filter and returns the whole table. The single routes honoured that, and so did
batch writes, but batch reads did not. So POST /v1/{entity}/batch and
POST /v1/batch with action list, get, search or fields answered
200 where the same operation answers 404 on its own route — serving exactly the result the entity
turned the operation off to avoid.
Separately, the legacy GET /v1/{entity}/aggregate never asked whether the entity has an
aggregate at all. For an entity whose every numeric field is an identifier,
POST /v1/{entity}/aggregate answers 404 while this address answered 200.
After
Both batch surfaces answer a disabled operation with 400 ACTION_NOT_SUPPORTED before any
Bitrix24 call: on the per-entity batch that is the whole request, on the global one it is a
per-sub-call error and the remaining sub-calls still run. The legacy
GET /v1/{entity}/aggregate is registered by the same predicate as its POST sibling, so an
entity without an aggregate now answers 404 on both.
Integrator impact
Only the entity-and-operation pairs whose answer was already wrong are affected. Check all
four reads — list, get, search, fields: six entities disable search while keeping a
working list, so a batch that used to go through whole now answers ACTION_NOT_SUPPORTED
on that sub-call. The full
set of actions is in operations.batch of the GET /v1/guide response and in the OpenAPI
description; data.batch of GET /v1/{entity}/fields lists WRITE actions only and cannot
answer the question about reads. An entity with every
operation off keeps its route, but every action answers ACTION_NOT_SUPPORTED — the refusal
names the action, which a 404 on the path cannot.
BC-0810-4: a `filter` that is not an object is refused instead of being lost
Old format supported until: 04.02.2027
As in the sibling entry about an unknown field name, "the old format" here means a wrong answer, not a working one.
Before
filter is an object of conditions, but nothing stopped a client sending a string, a number,
a boolean or an array instead. A string and an array went to Bitrix24 as they were, a number
and a boolean became an empty filter — and in every case the request answered 200 with the
WHOLE collection:
POST /v1/tasks/search { "filter": [{ "responsibleId": 1 }] } -> 200, every task on the account
After
Such a request is refused before the Bitrix24 call — 400 with the code
INVALID_FILTER_SHAPE; the message says what arrived instead of an object and shows the
correct form.
This applies on every entity and every surface where filter arrives in a body: search,
aggregate and both batches.
Integrator migration
Send filter as an object. The query string is a separate story: conditions are written in
the bracket form there (?filter[responsibleId]=1), and a filter JSON-encoded as one string
is no longer dropped as of this release — it is parsed and applied, where it used to be lost
silently and return the whole collection.
BC-0810-5: an unknown filter field name is refused on fifteen more entities
Old format supported until: 04.02.2027
Until now such a filter answered
200with the whole collection — so "the old format" here means a wrong answer, not a working one.
Before
A filter on a name the entity does not have was sent to Bitrix24. Bitrix24 does not refuse
such a key — it drops it silently and answers 200 with the WHOLE collection. A typo in a
field name therefore looked like a successful request with an implausibly large result:
GET /v1/tasks?filter[responsable]=1 → 200, every task on the account
That was the behaviour of tasks, users, workgroups, requisites, bank details, requisite presets, addresses, sites, pages, timeline comments, workflow templates, Open Channels configs and universal-list elements.
After
Such a request is refused before the Bitrix24 call — 400 with the code
UNKNOWN_FILTER_FIELD and the available names listed in the message — that list IS the
precise answer to "what can I filter by".
Only the NAME is checked: operators, ranges, $in/$nin and AND logic behave as before.
Besides declared fields it accepts custom fields (UF_*, ufCrm*), and the id key on the
entities that declare one.
Separately, for workflow activities and
workflow robots the Bitrix24 method accepts no filter in
any form, so any filter key there is refused with the code UNSUPPORTED_FILTER.
Full description — Filtering and search.
Integrator migration
Check the field names in your filters against the list in the error message. A request that used
to "work" but returned more records than it should will now answer 400 naming exactly which
field was not found — which is the mistake it always contained. Other filters are unaffected.
FIX-0810-6: a host out of free disk space is a distinct retryable deploy failure, not a broken app
Before
When the shared host of a galaxy app ran out of free disk space, the build failed and POST /v1/infra/servers/:id/deploy answered 502 GALAXY_APP_BUILD_FAILED — the same code as an error in the app's own source. The app was marked broken even though it never stopped working: the failure happened during the build, before the container was replaced, so the previous version went on answering requests. Telling a full disk apart from a genuine build error was only possible from the log tail in buildLog, and re-sending the same deploy produced the same result.
After
The same case returns 502 GALAXY_LOW_DISK with retryable: true and a structured error.hint: the cause, what to do, and a warning not to delete the slot. The app is not marked broken — the slot, its container and its data volume are intact, and the version already running keeps serving traffic. Re-send the deploy once space has been freed on the host: disk is not reclaimed on its own, so an immediate retry hits the same refusal. The new code is listed in the machine-readable deploy contract returned by GET /v1/me and GET /v1/openapi.json.
Impact on integrators
Nothing to change: successful deploys are unaffected. Keep your branch on GALAXY_APP_BUILD_FAILED — it still arrives for genuine build errors, while a full disk now has its own code that makes clear the problem is not in your source. An automatic retry happens only where the platform rebuilds an app that already exists: there attempts continue without client involvement until the wait budget runs out. When an app is created together with its source in one step, the refusal arrives right away and is not retried — the decision to retry is yours.
FIX-0810-7: A catalog launch now warns that the app is not authorized
Before
An app opened from the Bitrix24 app catalog by a user who had not granted it access yet started as usual, but the gateway did not set the X-Vibe-Authorization header. API calls answered 401, and the app displayed the text our own documentation prescribed for a 401 — "re-open the app from the Bitrix24 menu". That advice led nowhere: the user had just done exactly that, and a catalog launch does not grant access.
After
The platform now intercepts such a launch and shows a screen with an "Authorize the app" button; the second route is to open the app once through a placement in the Bitrix24 menu. The same screen carries an unobtrusive "open without authorizing" link to the very same launch address, so an app that does not need the session opens exactly as before. The screen appears only where the button has somewhere to lead: a cloud account, an app key, and a registered OAuth application. Every other launch behaves as before.
Impact on integrators
No code changes are required, and no launch becomes unavailable. Only your own 401 message is worth revising: the status has two causes — an expired session and access that was never granted — so "re-open from the menu" as the single wording misleads the user. The recommendation is updated in App runtime.
NEW-0810-8: CRM document list now present in the machine-readable OpenAPI schema
The GET /v1/crm-documents endpoint is now described in the OpenAPI schema served at /v1/openapi.json. The call itself already worked, but clients and AI agents that build integrations from the machine-readable description treated it as non-existent. The description states the required entityTypeId parameter, the optional entityId, select, order and start, the required crm access, the response shape with the document array and the meta block holding total, start and next, plus the refusal codes MISSING_PARAMS, INVALID_ENTITY_ID, INVALID_START, TOKEN_MISSING and SCOPE_DENIED. The endpoint takes no page-size parameter and the description declares none: for the next page pass the meta.next value as start. The behaviour of the endpoint itself is unchanged.
FIX-0810-9: a direct-link visit now carries the visitor's name and access token
Before
An app opened by its direct address (rather than from its tile inside Bitrix24) received the visitor's name from their Vibecode account instead of their employee card: someone who belongs to several Bitrix24 accounts saw the name they hold in a different one. The X-Vibe-Authorization header did not arrive on this path at all, so the app could not call Bitrix24 on the visitor's behalf.
After
X-Vibe-User-Name and X-Vibe-User-Name-Encoded carry the name from the employee card of the Bitrix24 account that owns the app — the same as on the tile path. The X-Vibe-Authorization access token is resolved by the employee id, so it arrives here too.
Impact on integrators
The header format is unchanged, no client work is needed. When the employee card cannot be read, the name stays as before, taken from the Vibecode account: the header is never delivered empty.
FIX-0810-10: invoice user fields
Before
Invoice user fields could not be read or created through either path.
GET /v1/userfields/invoices answered UNKNOWN_ENTITY, and
GET /v1/items/31/userfields answered with a Bitrix24 access
error, because the invoice was addressed the same way as an ordinary smart process.
After
Both paths work and return the same result: six operations (list, type catalog, read, create,
update, delete) over invoice user fields. The /v1/userfields/invoices path is there for callers
who prefer the entity name over the numeric type id.
Impact on integrators
No action required. This covers invoices in their current form; legacy invoices remain unavailable through the API.
BC-0810-11: The product-row field reference now describes what the responses actually contain
Old format supported until: not provided
Before
GET /v1/deals/{id}/products/fields returned the Bitrix24 field set verbatim, and it disagreed with the product-row responses in three places at once. The discount amount was called discountSum in the reference but discount in the data and on write. The external code and the account-currency price arrived in every row yet were missing from the reference. Owner, owner type and warehouse were the other way round: present in the reference, stripped from the responses.
Worse, a client that generated its writer from this reference sent discountSum — the wrapper did not recognise the name, answered 201 Created and discarded the discount silently. Any other unknown field behaved the same way: success reported, data not written.
After
The reference is derived from the same tables that build the responses, so the two can no longer drift apart. The discount is called discount, matching the data, the write contract and the documentation. The previous name discountSum did not go away: it remains a deprecated alias, still returned by the reference and still accepted on write, so code written against the old field list keeps working — except that the discount now actually lands instead of being dropped silently. Product rows themselves carry only discount. priceAccount and xmlId were added — Bitrix24 returns them in rows but does not describe them in its own field set. ownerId, ownerType and storeId now arrive in the list and single row responses; storeId is null when inventory management is off.
isReadOnly and isRequired describe this API's contract rather than the Bitrix24 one: ownerId, ownerType, customized and measureName are marked read-only because the wrapper cannot write them, and ownerId and ownerType are no longer required — they are taken from the request path. id gained a description: it is read-only as an attribute, and inside PUT /products items it is accepted. Correction of 2026-08-18: a live check showed that echoing id back does not preserve row identity — a row whose fields are unchanged keeps its id even with no id in the body, and a modified row comes back with a new id. To edit a row and keep its identifier, use PATCH /v1/deals/:id/products/:rowId.
A write carrying an unknown field name no longer reports success: POST, PUT and PATCH return 400 INVALID_PARAMS and list the writable fields. Read-only fields are still accepted and ignored, so an object read back via GET can be sent as is.
A body id is accepted only inside PUT /products items. On create and update it is dropped: the row is identified by the request path there, and a body carrying the id of an existing row is exactly what you get by sending back an object read via GET.
On write, taxIncluded accepts a boolean again: true and false reach Bitrix24 as Y and N. The boolean used to be forwarded as is, leaving the row's tax inclusion effectively unset — so a row read via GET and sent straight back lost the flag. If you worked around this by sending "Y" and "N" as strings, nothing changes: that form is still accepted.
When Bitrix24 returns incomplete metadata the reference still comes back complete, but the response now carries meta.warnings[].code = "fields_partial" — previously such an answer was indistinguishable from a healthy one. The warning text distinguishes two cases: no metadata arrived at all, or only some fields were left undescribed (it then names them).
What integrators should do
What breaks is a write carrying a foreign key in the body. Check that creating or updating a product row sends nothing beyond the writable fields — your own bookkeeping markers, leftovers from an internal model, Bitrix24 upper-case names (PRICE_ACCOUNT, XML_ID). Such a request used to answer 201 Created and discard the value silently; it now answers 400 INVALID_PARAMS and lists what is accepted. There is deliberately no support window for the old behaviour: it was the defect this change exists to fix, and keeping it alongside means keeping silent data loss. The full list of writable fields comes back in the refusal and in the field reference.
Nothing else needs changing. discountSum is still returned and still accepted — move to discount at your own pace, that is the name product rows carry, while the alias lives only in this reference. If you branch on isReadOnly or isRequired, re-check the new values for ownerId, ownerType, customized and measureName. If you replace rows wholesale, note that the identifiers of modified rows change: re-read the set via GET after the write.
FIX-0810-12: server member lookup no longer comes back empty because of a recent key without Bitrix24 access
Before
When a server's managing key granted no Bitrix24 access, GET /v1/infra/servers/{id}/b24-users
and the member lookup in the interface fell back to the server owner's personal keys, but checked
only one — the most recently created eligible key. If that key had neither a webhook nor an
installed application token, the response came back empty (data: [] with a hint) even though the
owner had another active key with the required rights. Keys the platform issues itself for tasks
that never call Bitrix24 are always the most recent ones, so the lookup could stay broken
indefinitely.
After
All eligible personal keys of the owner are checked, newest to oldest, and the first one that really has Bitrix24 access is used. Keys without access are skipped, and Bitrix24 still receives a single request. Key requirements are unchanged: as before, only an active, non-expired personal key of the server owner in the same account qualifies, and it must not be bound to an application.
The hint text is corrected as well: it used to name only an unauthorized app and a revoked key, so
an active key looked revoked. A third reason is now named — no key grants Bitrix24 access — together
with the action to take, issuing a personal key with the required scope. The response shape is
unchanged.
FIX-0810-13: placement bind names the install-rights refusal instead of a generic gateway error
Before
Bitrix24 refused the embedding install and the account's plan state could not be confirmed at that moment — POST /v1/placements/bind answered 502 BITRIX_UNAVAILABLE and put the Bitrix24 code into details. There was no named code in the answer, and no hint about what to do next. The refusal reached accounts whose plan already allowed the install.
After
When the check cannot be performed and the account is already on record as entitled in Vibecode, the same refusal arrives as 403 B24_EMBEDDING_INSTALL_DENIED with details.remedy set to install-rights. No upgrade link is attached: the entitlement is there, what is missing is the right to install local applications. With no live entitlement from either source the answer stays 502 BITRIX_UNAVAILABLE.
Impact on integrators
Nothing to change. Your branch on 502 BITRIX_UNAVAILABLE keeps working for the other refusals, while this case leaves it for 403. Retrying it is pointless — the developer key has to belong to a user allowed to install local applications and holding access to the application.
BC-0810-14: commands, deploys and file uploads addressed by a galaxy host id are refused
Old format supported until: 07.08.2026
Before
A galaxy is a single machine that hosts the applications of several keys of one Bitrix24 account, each in its own container. The server list returns both the applications (kind=GALAXY_APP) and the machine carrying them (kind=GALAXY), and calling POST /v1/infra/servers/:id/exec, POST /v1/infra/servers/:id/deploy or POST /v1/infra/servers/:id/upload with the id of that machine ran on the machine itself, that is outside the caller's own container.
After
The same call against a GALAXY machine is refused: commands return 403 GALAXY_HOST_EXEC_FORBIDDEN, deploys and file uploads return 400 GALAXY_HOST_NOT_A_DEPLOY_TARGET. The refusal names the replacement: address the application by its own id (kind=GALAXY_APP). Nothing changes for STANDALONE machines or for galaxy applications.
What integrators should do
Take your application's row from the server list (kind=GALAXY_APP) and use its id. If the flow relied on reaching the machine to learn how much disk space is left, that figure is now available as data rather than as the output of a command.
There is no support window for the previous behaviour: the refusal applies from the moment this ships. The reason is that the previous behaviour opened access to a machine carrying other keys' containers, and access like that cannot be kept alive for six months for the sake of compatibility.
NEW-0810-15: galaxy host disk usage is exposed in the server list
A galaxy host row (kind=GALAXY) in GET /v1/infra/servers and in the single-server response now carries four fields: diskTotalMb and diskFreeMb — size and free space in mebibytes, diskState — the verdict (ok, warning, critical, or unknown when nothing has been measured yet), diskProbedAt — the measurement time in ISO-8601.
The platform keeps both lines well clear of the edge: warning means space is running out, critical means less is left than the platform considers safe; both light up before a deploy actually runs out of room. The measurement refreshes on its own while the machine is awake; a sleeping machine shows the last known value together with its timestamp.
For STANDALONE machines and for galaxy applications (kind=GALAXY_APP) all four fields are null: the former are never probed, the latter have no disk of their own.
NEW-0810-16: agent model bitrix/bitrixgpt-5.6-agent
GET /v1/models now lists a new agent model, bitrix/bitrixgpt-5.6-agent, with a 1,048,576-token context. It supports streaming, tool calls (tools) and schema-constrained output — response_format with type: "json_schema". The model public id is part of the ai.structuredOutputs.models list returned by GET /v1/me.
The model is additive and replaces nothing: existing calls are unaffected. It is enrolled in the quota programme, so it is also reachable with a Bitrix24 partner token.
Affected endpoints: GET /v1/models, POST /v1/chat/completions, GET /v1/me
NEW-0810-17: bitrix/bitrixgpt-5.5-agent is deprecated
Responses from POST /v1/chat/completions for bitrix/bitrixgpt-5.5-agent now carry Deprecation: true, X-Model-Replacement: bitrix/bitrixgpt-5.6-agent and a Link header pointing at the successor (rel="successor-version").
The model keeps working without restrictions and stays in the GET /v1/models listing. No shutdown date is set — the Sunset header is not sent, and no integration changes are required. The successor for new integrations is bitrix/bitrixgpt-5.6-agent.
FIX-0810-18: a transient database transaction failure no longer returns 500 with an internal engine code
Before
When a database transaction closed or expired before the operation finished, the request answered 500 and put the engine's internal code in the body — {"error":{"code":"P2028"}}. That code appears on no documentation page, the response carried no Retry-After header, and nothing in it said the request was worth repeating. It showed up most often on DELETE /v1/apps/{id}: the application stayed in place and a retry looked pointless.
After
The same class of failure returns 503 with code DB_TRANSIENT, an error.retryAfter field, and a Retry-After header — the same retry posture as POOL_EXHAUSTED. The change applies neither partially nor fully on such a failure, so a straight retry after a few seconds is safe. On the AI routes (/v1/ai/*, /v1/chat/*, /v1/audio/*, /v1/models) the code arrives lowercased — db_transient — in the OpenAI-compatible envelope. The engine's internal code no longer appears in the response body.
Impact on integrators
Nothing to change. If your handler treated 500 as a terminal refusal, this case now arrives as 503 with a stated delay and falls into your retry branch instead. No special handling of the DB_TRANSIENT code is required: honouring Retry-After on any 503 is enough.
FIX-0810-19: preserveEnv no longer loses .env when a deploy fails after the directory is cleaned
Before
With cleanDeploy: true together with preserveEnv: true the existing .env was read before the
clean but written back only at the env step — after the runtime and the dependencies were
installed. If the deploy aborted earlier, POST /v1/infra/servers/:id/deploy
answered DEPLOY_FAILED and the directory was left with no .env at all. The application
settings had to be uploaded again by hand.
After
The saved copy is put back on disk on any abort after the clean — on dependency install, on the
runtime, on the archive download, on the clean itself, and when the connection to the server
drops. The restore is best-effort and adds no separate step to the response. The precedence rule
is unchanged: an env passed in the same request still wins, and an explicit empty env: {}
means "clear it" and does not bring the saved copy back. A successful deploy behaves exactly as
before, including the time-zone injection for wake schedules. The flag still applies to a
dedicated virtual machine only (kind: "STANDALONE").
Impact on integrators
Nothing to change on your side. The DEPLOY_FAILED response shape and the set of steps in
data.steps are unchanged. If you worked around this defect by re-uploading .env by hand after
every failed deploy, that workaround is no longer needed.