For AI agents: markdown of this page — /docs-content-en/changelog/2026-09-23.md documentation index — /llms.txt
API changes: September 23, 2026
NEW-0923-1: ERP connection management through the Cowork API
On the RU platform, the Vibecode API adds 20 /v1/cowork/onec/* methods for connection status, administrator eligibility, credential issuance and revocation, catalog refresh, and access management. The international platform exposes only the safe status operation, which reports that ERP management is unavailable in that segment. An older Cowork key can request the required scopes through /v1/connect/device/authorize and /v1/connect/token.
The listTools result now also supports format "2", which carries each tool's complete normalized input schema. Format "1" remains unchanged for stored operations and existing clients.
On the RU platform, the surface starts disabled and is enabled separately for each account after server readiness checks. The international status remains UNAVAILABLE. The change does not add a user interface and does not imply that the methods are already enabled for accounts.
NEW-0923-2: turning chat notifications off and on
The new endpoint POST /v1/chats/:chatId/mute turns chat notifications off ("mute": true) or back on ("mute": false). The setting is personal: it changes for the user the call is made on behalf of. To change it for an employee who opened your application, call the endpoint with the application key and that employee's session. The mute field is required, and a request without it is refused with 400 MISSING_PARAMS before the Bitrix24 call.
BC-0923-3: creating a deal or a lead with a stage outside the dictionary no longer answers with success
Old format supported until: not provided
Before
POST /v1/deals and POST /v1/leads with a stage the account's dictionary does not hold answered 201: Bitrix24 accepted the write and silently put the record on the default stage. A typo in stageId, a different letter case, extra spaces, another pipeline's stage without its categoryId and a deal stage in a lead's statusId all behaved this way. The caller saw success while the record sat where nobody asked. The other doors — PATCH, /move, batch writes and import — already refused such a stage.
After
Before the write the stage is checked against the account's dictionary literally, the way Bitrix24 itself checks it: for a deal — against the stages of the pipeline named by categoryId (without it — the main one, DEAL_STAGE; for another pipeline — DEAL_STAGE_{categoryId} with stages like C{categoryId}:NEW), for a lead — against the STATUS statuses. A stage missing from the dictionary answers 400 UNKNOWN_STAGE and no record is created. message names the key and the dictionary and suggests the closest exact spelling or the categoryId that must accompany another pipeline's stage; details.knownStages lists the dictionary's stages (up to fifty), details.entityId says which dictionary to read via GET /v1/statuses?filter[entityId]=…. Any spelling of the key Bitrix24 reads as STAGE_ID or CATEGORY_ID (raw STAGE_ID, stage_id, StageId) is checked the same way; when several spellings are sent, the last one in the body counts and the error names it in details.field. A list or an object in place of a stage name under a raw spelling of the key answers 400 UNKNOWN_STAGE too, because Bitrix24 would put the record on the default stage (under stageId / statusId the shape check refuses it earlier — 400 INVALID_PARAMS); that refusal needs no dictionary: details.knownStages is empty and details.stageId names the shape of the value. An empty stage ("", null) is still not checked — the record lands on the default stage. The dictionary is read once per account and pipeline and kept for five minutes, so an ordinary create did not get more expensive; if the dictionary cannot be read or comes back incomplete, the write proceeds as before. A stage from the dictionary answers as it did.
What integrators should do
Make sure the stages in your requests match the dictionary letter for letter: GET /v1/statuses?filter[entityId]=DEAL_STAGE for the main deal pipeline, DEAL_STAGE_{categoryId} for the others, STATUS for leads. Send another pipeline's stage together with its categoryId. Handle 400 UNKNOWN_STAGE — repeating the request with the same stage is pointless, pick one of details.knownStages.
BC-0923-4: CRM_CREATE in open-line configurations now accepts only supported values
Old format supported until: not provided
Before
POST /v1/openline-configs and PATCH /v1/openline-configs/:id returned 200 for contact, company, and other unsupported crmCreate values, after which Bitrix24 stored the mode as lead.
After
crmCreate and its accepted key-name variants now allow only the exact strings none, lead, and deal. Any other value returns HTTP 400 with code VALIDATION_ERROR and a message naming the field and the complete set of allowed values. The request is rejected before the configuration is changed.
What integrators should do
Send only none, lead, or deal. Clients that sent contact or company must select a supported mode and handle 400 VALIDATION_ERROR.
FIX-0923-5: Field-name validation when reading requisites
Before
Some nonexistent field names, including constructor, __proto__, and toString, incorrectly passed requisite filter and sort validation.
After
Field validation now rejects inherited names before calling Bitrix24: with 400 UNKNOWN_FILTER_FIELD for filters and 400 UNKNOWN_SORT_FIELD for sorting. Request processing order is unchanged: body parsing may reject a request before field validation, and the order object still takes precedence over sort. Valid fields and their supported aliases work as before.
Affected endpoint: GET /v1/requisite-presets/:presetId/fields.
Impact on integrators
Requests using valid field names do not need to change.
FIX-0923-6: Reuse the application server after rebinding its key
Before
For applications with a previously lost key binding, a repeated POST /v1/infra/servers could attempt to create another server. A rebind conflict could leave the server and its application record linked to different keys.
After
A rebind conflict preserves the previous server and application binding in full. Creation recognizes a single eligible application server already managed by the current key and restores an empty binding. The reuse response remains HTTP 201 with reused: true. A concurrent binding change returns HTTP 409 APPLICATION_REUSE_CONFLICT with retryable: true; the request can be retried. GET /v1/me exposes optional deployment.standalone.reuseTarget and a checklist for the existing server without changing bindings. TRIAL_PORTAL_LIMIT carries retryable: false and guidance that avoids repeated creation.
FIX-0923-7: Bracket form of `order` and `scope` no longer causes an internal error
Before
A request with a bracket-form parameter, such as ?order[x]=1 on GET /v1/tasks/:taskId/history or ?scope[x]=1 on POST /v1/pages/:id/publication, answered 500 instead of a regular response.
After
GET /v1/tasks/:taskId/history treats such an order as an unknown value and sorts ascending, as without the parameter; the response remains HTTP 200. POST /v1/pages/:id/publication rejects such a scope with 400 INVALID_SCOPE and shows the received value in the error message, as for any other invalid scope.
Impact on integrators
Requests with string order and scope values do not need to change.
FIX-0923-8: galaxy app diagnostics now check the public address
Before
The reachability block of GET /v1/infra/servers/:id derived its effectiveStatus verdict from
the container and the routing on the galaxy alone. Both facts are measured on the host, while the
last leg of the path to https://app-XXXX.vibecode.bitrix24.com is the app's tunnel at the
gateway, which is invisible from the host: a started routing unit means it was started, not that it
reached the gateway. An app whose public address answered nothing from outside could therefore be
reported as "effectiveStatus": "running".
After
The verdict accounts for the public entrance, and a new field reachability.publicEntry ships
beside it — live when the gateway holds a live tunnel for the app subdomain, no-tunnel when
there is none, and unknown when the gateway's state could not be read. An app with a live
container and active routing but no tunnel answers "effectiveStatus": "unreachable". The value
unknown never worsens the verdict: "could not look" is not "nothing works". The other
effectiveStatus values and every previous field of the block are unchanged, and no request needs
editing. The values are explained in Galaxy app sleep and wake.
FIX-0923-9: revoking server access now ends sessions that are already open
Before
A user who had opened the application the ordinary way received a browser session, and from then on it lived on its own: removing their access-list entry (DELETE /v1/infra/servers/{id}/access/{accessId}) did not affect it. The same tab kept opening the application, and the session extended itself while the user stayed active — so access persisted indefinitely. A refusal appeared only on a fresh sign-in.
After
The right to enter is re-checked for an already-open session too. Once access is removed — by the user entry, by department, or by changing the policy — the application stops opening within a minute, and the session no longer extends itself.
Owner-only mode is the exception: some already-open sessions live out their own term, up to 40 minutes, and can no longer extend themselves. Count on that term if you switch a server to that mode to close access immediately.
Integrator impact
No action required. Share-link access is unchanged: it does not follow the server access list and is revoked separately, by revoking the link itself.
FIX-0923-10: Legacy GET aggregates report incomplete data
Before
GET /v1/{entity}/aggregate could return HTTP 200 with numbers computed over only some records without indicating incompleteness.
After
The response remains HTTP 200. Its existing numeric fields and grouping are unchanged. data.meta always contains recordsProcessed and truncated. A known overall record count adds totalRecords, a positive shortfall adds recordsShortfall, and a failed page adds a sanitized pageErrorSample containing code and message.
A partial result includes truncated: true in data, each data.groups element and data.total when grouped. A group marker describes the incomplete overall selection, not proven missing rows in that specific group. data.meta.warnings includes code AGGREGATE_TRUNCATED. Complete responses omit conditional markers and the warning, with data.meta.truncated set to false.
When the overall count is unknown, totalRecords and recordsShortfall are omitted. An explicit incompleteness signal is retained even without a quantitative estimate of missing records. The count operation on this GET path also counts fetched records and can be partial. The existing HTTP 422 refusal above 10,000 records and deprecation headers are retained.
Impact on integrators
Check data.meta.truncated before treating numbers as final. Narrow the filter for partial results. The POST aggregation contract is unchanged.
BC-0923-11: fieldName is checked before Bitrix24 is called when creating a custom field
Old format supported until: not provided
Before
POST /v1/userfields/:entity forwarded fieldName to Bitrix24 exactly as received, without checking it. An empty string, null, a number, an array or an object reached Bitrix24, and what happened next was decided there: an empty name came back as an error with no code — 422 BITRIX_ERROR with b24Code: "0" on the way out, which tells nothing about what the request got wrong — while a non-string value could be coerced into a string and create a field with a garbage name under a success answer.
After
The value is checked before Bitrix24 is called: it must be a string carrying at least one non-whitespace character, otherwise the refusal is 400 INVALID_FIELD_NAME. What is checked is the value that actually leaves for Bitrix24 — when the body carries both spellings, fieldName and its raw counterpart FIELD_NAME, the last one is what travels, and that one is judged. The same rule now covers the mandatory userTypeId, and for a body carrying ONE spelling nothing changed: a missing or empty type is refused 400 MISSING_FIELD exactly as before. The difference is a body carrying both spellings at once — the first non-empty one used to be judged, now it is the one that travels to Bitrix24. The body {"userTypeId": "string", "USER_TYPE_ID": ""} used to reach Bitrix24 and return its opaque answer, and now gets 400 MISSING_FIELD; the opposite body {"userTypeId": "", "USER_TYPE_ID": "string"} used to be refused 400 MISSING_FIELD, and now creates a field of type string. The name format still belongs to Bitrix24: the UF_CRM_ prefix, the length and the character set are its checks.
What integrators should do
Calls with a valid fieldName are untouched, and so are calls without the key at all — there Bitrix24 generates the field name. Code changes are needed where fieldName carried a non-string, an empty string or a whitespace-only string: such a call now answers 400 INVALID_FIELD_NAME instead of its previous answer. A name taken from untrimmed user input or from a spreadsheet cell falls under this too — trim it on your side. To have the platform generate a name, the key is omitted entirely: an empty string and null do not serve that purpose.
NEW-0923-12: server detail returns the last deploy's start command, install command and port
The GET /v1/infra/servers/:id response gains three fields: startCommand, installCommand, deployPort — a
snapshot of start/install/port from the body of the last SUCCESSFUL
POST /v1/infra/servers/:id/deploy. For a server with no successful deploy yet, all
three fields return null. A development-team member (access.via: "collaborator") receives these fields exactly as the owner does.
NEW-0923-13: app active-servers refusal now names the server owner
In the 409 APP_HAS_ACTIVE_SERVERS response of DELETE /v1/apps/:id, every element of details.servers gained an optional ownerName field — the name of the server owner, or null when the server has no owner. Until now the response gave no way to tell whose server was blocking the app deletion, and the owner had to be looked up by hand. The other fields of the element and the error code are unchanged, so requests from existing clients keep working.
BC-0923-14: retrying Cowork application creation after a network failure returns the first attempt's outcome — while it is still fresh
Old format supported until: not provided
Before
POST /v1/cowork/applications permanently claimed the Idempotency-Key after a failure during key issuance (for example, Bitrix24 Network being unreachable while the Bitrix24 account was already contacted): a retry with the same Idempotency-Key answered a generic 409 IDEMPOTENCY_KEY_ALREADY_USED regardless of what actually failed, and the draft application could not be recovered — but a retry with the same key was guaranteed to never create a duplicate: it either replayed into the 409 or never went through at all.
After
Such a retry, within 20 minutes of the first failure, now gets the SAME response the first attempt got — same HTTP status, same error.code, same body — with an added Idempotent-Replayed: true header. After 20 minutes the Idempotency-Key is forgotten and a retry with it runs as a brand-new attempt, every check re-evaluated from scratch. If the first failure happened AFTER Bitrix24 was contacted (for example 502 BITRIX_UNAVAILABLE), the account may already have received a webhook from the first attempt; a same-key retry past the 20-minute window does not check for that and can mint a SECOND key and create a second application on top of the first. 409 IDEMPOTENCY_KEY_ALREADY_USED now means only "the key belongs to an application that has since been deleted" — a permanent refusal, as before.
What integrators should do
Clients will no longer see IDEMPOTENCY_KEY_ALREADY_USED for the "first attempt reached Bitrix24 and failed there" scenario: within a 20-minute window they instead get the original failure (e.g. 502 BITRIX_UNAVAILABLE) with Idempotent-Replayed: true. Retry with the SAME Idempotency-Key only WITHIN that window — replay there is deterministic and safe. More than 20 minutes after the first failure, changing the Idempotency-Key value does not protect against a duplicate: retrying with the old key or a new one both run as a genuinely new attempt, and if the first failure happened after Bitrix24 was contacted, it can mint a second key on top of the first. Usually there is nothing on the platform to check either — the failed reservation is removed together with its outcome, and most failures at this stage never mint a key locally; look for a duplicate on the Bitrix24 account itself if you can, and treat the risk as inherent past the window. The exception is 500 APPLICATION_CREATE_FAILED: if the key was already minted and only saving the card failed, that stray key can be found and revoked in the platform's Keys section; an empty list means there is nothing to clean up. No change to how error.code is branched on; do not rely on IDEMPOTENCY_KEY_ALREADY_USED staying permanent for failures after the Bitrix24 account was contacted.