For AI agents: markdown of this page — /docs-content-en/changelog/2026-08-10.md documentation index — /llms.txt

API changes: August 10, 2026

← Changelog · August 2026

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 200 with 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.

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.