For AI agents: markdown of this page — /docs-content-en/changelog/2026-08-21.md documentation index — /llms.txt
API changes: August 21, 2026
FIX-0821-1: iblockTypeId=structure is accepted on /v1/lists
Before
iblockTypeId accepted only lists, lists_socnet, and bitrix_processes. The value structure (the absence-calendar infoblock type) returned 400 INVALID_IBLOCK_TYPE, even though GET /v1/lists/:iblockId/type returned it.
After
structure is in the allowed set on every /v1/lists route. The Bitrix24 response for that type (data or 422/403) is no longer replaced by our 400. Other unknown types still return 400 INVALID_IBLOCK_TYPE. The default is unchanged: lists.
Impact on integrations
Requests to structure infoblocks, including the stock absence calendar, now reach Bitrix24. Existing integrations that use other types require no changes.
FIX-0821-2: currencyId and explicit selection of non-returned fields
Before
POST /v1/products and PATCH /v1/products/:id accepted the currency only under the currency name. An explicit selection of a field marked notReturned: true was accepted without a warning even though the response contained no value.
After
Product write methods accept currencyId as an additional name for currency. If both names are present, currency wins, and responses still carry only currency. The additional name is marked writeOnly and notReturned in /fields, /v1/guide, and OpenAPI. Explicitly selecting any non-returned canonical name now adds an UNKNOWN_SELECT_FIELD warning: products.currencyId, tasks.realStatus, product-sections.sort, bank-details.entityTypeId, telephony-lines.serverName, bizproc-templates.templateData, and catalog-products.iblockSection. The native Bitrix24 name select=CURRENCY_ID continues to select the readable products.currency field. Each name is covered by the field references for products, tasks, product sections, bank details, telephony lines, business process templates, and catalog products. Currency filtering remains unsupported and returns 400 UNSUPPORTED_FILTER.
Impact on integrators
Read requests still return 200 and the other selected fields, but now carry a warning. Remove non-returned names from select: use currency for the product currency and status for the task's actual status.
FIX-0821-3: product description filter is no longer rejected
Before
GET /v1/products with filter[description] and POST /v1/products/search with the same filter returned 400 UNSUPPORTED_FILTER, even though the description was stored when the product was created.
After
Exact match and $in on description are accepted the same way as on name. Operators, including $contains, still return 400. The price and currency filters are unchanged.
FIX-0821-4: the refusal on creating an employee without a department now names the field to pass
Before
POST /v1/users without departmentId on an account with the extranet module installed answered 422 with the text no_extranet_field. No field of that name exists in the request body or in the GET /v1/users/fields output, and nothing in the response pointed at the department — the refusal gave no way to tell what to correct. The employee was not created.
After
The response now carries a hint: it names the field in both spellings — UF_DEPARTMENT for a direct call and departmentId for the POST /v1/users wrapper (both are accepted) — points at GET /v1/departments as the source of values, gives the root department as a working example, and mentions POST /v1/users/invite, which supplies the department itself. For an external user the alternative is named — EXTRANET together with SONET_GROUP_ID instead of a department. The departmentId description in the field reference now states the requirement too, so the condition is visible before the request is sent.
What integrators should do
No action is required: the status code and the envelope shape are unchanged, only the hint was added. The departmentId field is deliberately not marked mandatory on the Vibecode API side — an account without the extranet module accepts a create with no department, and a hard requirement would break those requests.
NEW-0821-5: Open Channels dialog metadata via the API
A new endpoint POST /v1/openlines/dialogs/lookup — a wrapper over the Bitrix24 method imopenlines.dialog.get. It returns the card of a single Open Channels dialog (name, type, line, message count, dates) by one of the identifiers: chatId, dialogId or sessionId. It does not return the message history. Requires the imopenlines scope.
The response carries a derived lineId field — the line identifier parsed from the dialog binding; it is the entry point to GET /v1/openline-configs/:id. Lookup by sessionId resolves a dialog straight from a session identifier taken from the id field of POST /v1/openlines/sessions/search.
BC-0821-6: a structured value in a scalar field is refused instead of silently lost
Old format supported until: not provided
Before
An entity write accepted an object or an array in a field declared scalar and answered with success. POST /v1/deals carrying {"title": {"a": 1}} returned 201 while the deal card showed the string Array — that is how Bitrix24 casts an array to a string. The value could not be recovered and the client saw no error.
After
Such a request is refused before the Bitrix24 call — 400 with code INVALID_PARAMS and the field name. The rule covers fields declared string, number, boolean, date and datetime, on entity writes under /v1/<entity>: create and update, POST /v1/batch, per-entity batch write and import.
What did NOT change: a number or a boolean in a string field is still accepted (Bitrix24 stores 123456 and 1, so nothing is lost), null is still accepted, and fields declared object, array or multi-value accepted structures before and still do.
Impact on integrators
Review any code that builds a write body from an external source: where an object or an array reached a scalar field, the request used to succeed while losing the value and will now return 400. Send a scalar value to such a field.
The boundaries are stated explicitly. A name absent from the entity schema (user fields UF_*, propertyNNN, a typo) is not checked — it has no declared type. The refusal shape differs by surface, and so does its reach. The global batch refuses only its own sub-call and puts the failure in data.errors["<id>"] with separate code and message fields. The per-entity batch write and import refuse the whole request: a 400 with code BATCH_ITEM_VALIDATION or IMPORT_ITEM_VALIDATION, with the element index and INVALID_PARAMS inside message. Such a response carries no per-item results, and nothing reaches Bitrix24. A few bespoke write routes are not covered yet — addresses, task comments, document templates, Open Channels config and product rows; there the previous behaviour still applies, and they are closed separately.
FIX-0821-7: PATCH /v1/infra/servers/:id/access-policy now preserves the access list on policy change
Before
Switching PATCH /v1/infra/servers/:id/access-policy away from NAMED_USERS or DEPARTMENT to any other policy permanently deleted the access list (users and departments added via POST /access) — even though the docs promised the records stay in the database and simply stop applying until the policy becomes a named one again.
After
The list is preserved when leaving NAMED_USERS/DEPARTMENT — behavior matches the documentation again.
Impact on integrators
No action required — behavior now matches the already-published documentation.
BC-0821-8: a busy shared galaxy exec channel now answers 409 instead of 502
Old format supported until: not provided
Before
POST /v1/infra/servers/:id/exec for an application in a galaxy (kind: "GALAXY_APP") answered 502 with the EXEC_BUSY code when the host's shared exec channel was busy. The response carried neither a Retry-After header nor the retryable / retryAfter fields, so a machine client read the refusal as a gateway failure and did not retry. The same refusal on POST /v1/infra/servers/:id/deploy already arrived as 409 with a retry signal.
The error.hint in 409 EXEC_BUSY responses on galaxy routes offered to unstick the channel via POST /v1/infra/servers/:id/unstick. That call is not available to an application owner or a host owner by contract — it answers 409 GALAXY_UNSTICK_UNSUPPORTED, because the exec channel is shared by every application on the host.
After
A busy shared host exec channel arrives as 409 with the EXEC_BUSY code, a Retry-After header and the retryable: true / retryAfter (seconds) fields — the same contract as every other "busy" refusal. Other execution failures for an application in a galaxy still arrive with status 502.
Two different states now share the 409 status, and they are machine-distinguishable by the presence of error.hint.autoExpiresInSeconds: the application's own lock carries it (the remaining lock TTL), a busy shared host channel does not. recoveryAction cannot tell them apart: on a galaxy application and on the galaxy host itself that field names no concrete call at all — only "wait" and "retry" — because releasing a lock there can abort a running operation and, on a shared host, the commands of neighbouring applications with it. The machine-actionable advice is therefore the same in both states — wait and retry at the Retry-After interval.
Important: this is said about the galaxy states, not about the /exec route as a whole. On a dedicated virtual machine (kind: "STANDALONE") the recoveryAction of the same 409 still names DELETE /v1/infra/servers/:id/lock, unconditionally — and against a running deploy that call removes the per-server serialization the deploy relies on. So recoveryAction must never be executed without reading error.hint.recovery first, in any state. The lock-release address stays in error.hint.recovery, with the server identifier substituted, the owning key required and the caveat that it is only for when you have confirmed nothing is running.
Reading the log answers by the same contract now. GET /v1/infra/servers/:id/logs for an application in a galaxy goes through that same shared host exec channel, and with the channel busy it used to answer 200 with an empty logs list — claiming the application had no output. The busy state now arrives as 409 with Retry-After, and other read failures as 502 carrying the agent's code.
What integrators should do
A client that branched on the HTTP status and treated 502 on /exec as a terminal failure must stop doing so: the busy state now arrives as 409 and should be retried at the Retry-After interval. A client that read error.code needs no change. If your flow called POST /v1/infra/servers/:id/unstick because the platform suggested it, that call never worked on galaxy servers — replace it with a retry, and contact support if the refusal persists. If you branch between the two 409 states of /exec in code, key on the presence of error.hint.autoExpiresInSeconds rather than on the text of recoveryAction.
BC-0821-9: the per-entity batch now explains what it did with select
Old format supported until: not provided
Before
POST /v1/{entity}/batch with the list action applied select to the response but said
nothing about what it dropped. A name the entity does not have simply vanished: the record came
back narrowed and no explanation existed anywhere — no error, no warning. The single list and the
global batch both report such a name as a warning; this door was the only one where
field selection was completely silent.
After
The sub-call carries its own meta.warnings with the UNKNOWN_SELECT_FIELD code and the field
name — the same shape the single list uses. The warning belongs to ITS sub-call: neighbouring
items get no meta of their own, and the key never arrives as an empty array — nothing to say
means no key.
Entities where an unknown name answers with an error (today, calendar events) now refuse such a
sub-call here too — UNKNOWN_SELECT_FIELD before the Bitrix24 call. The refusal is per-call, as
in the global batch: only that item fails, the other forty-nine still run. Before, the hard
selection guard did not fire on this door at all.
The value * still means "return every field": no selection is applied and an unknown name next
to it refuses nothing. A warning still arrives: a select of *,titel returns every field AND
says the second name means nothing — exactly as on the single list and in the global batch.
Impact on integrators
The additive half breaks nothing: meta is a new optional key next to data and total. The
breaking half is the hard selection: a sub-call that used to answer with a narrowed record and
code 200 now answers with an error in its slot on calendar events. There is deliberately no
support window: the previous behaviour was itself the defect — the requested name vanished
silently, with nothing to tell that apart from "the field is not in the record". Review handlers
that treated the absence of an error as proof that every select name was recognised.
NEW-0821-10: Open Channels session transcript via the API
A new endpoint POST /v1/openlines/sessions/history — a wrapper over the Bitrix24 method imopenlines.session.history.get. It returns the message history of a chat's latest Open Channels session by the chat identifier: messages, participants and file metadata in one response. Requires the imopenlines scope.
Input is by the chat identifier only (chatId, the chat2043 form is accepted too). By it the latest session of the chat is taken. The method has no pagination — the transcript comes in full. For page-by-page reading of messages use GET /v1/chats/:dialogId/messages.
The endpoint is enabled gradually by the Vibecode platform: while it is off, the call answers 403 OPENLINES_HISTORY_DISABLED — a sign the capability is not active yet, not an integration error.
NEW-0821-11: galaxy app multipart deploy stores the archive in source storage
The multipart deploy of a galaxy app no longer buffers the whole archive in platform memory: where
multipart depositing is enabled, the archive is stored as a version in the
source storage and reaches the host through a signed link. Where the host
is allowed to fetch the archive itself, a recognised tar.gz is downloaded by the host — verifying
the size and checksum recorded for that version — and a failed fetch then arrives as a separate
UPLOAD_DOWNLOAD_FAILED / UPLOAD_EXTRACT_FAILED / UPLOAD_NO_SPACE code in the buildLog tail;
a zip and an unrecognised format keep the previous path, through the agent. The size limit on an
archive sent in the request body is unchanged — the body still travels through the platform. The
version stays in the depot even when the deploy fails: it is listed among the versions and can be
redeployed by source.versionId.