For AI agents: markdown of this page — /docs-content-en/changelog/2026-09-18.md documentation index — /llms.txt
API changes: September 18, 2026
FIX-0918-1: quote filter and sort by the not-returned contacts field answer with the platform's refusal, not a Bitrix24 error
Before
On quotes the contacts field is marked as not returned (the Bitrix24 item API never returns it, the bound contacts are in contactIds), yet ?filter[contacts]=…, ?sort=contacts and ?order[contacts]=… were forwarded to Bitrix24 and answered 422 BITRIX_ERROR with Unknown field definition CONTACTS — Bitrix24's words, whereas companies, leads, deals and invoices answer the same requests with 400 UNKNOWN_FILTER_FIELD / 400 UNKNOWN_SORT_FIELD.
After
On quotes, filter, sort and order by contacts are refused before Bitrix24 is called: 400 UNKNOWN_FILTER_FIELD and 400 UNKNOWN_SORT_FIELD, with the accepted names listed in the message — like the other CRM entities. Nothing else about the field changes: a write answers 400 READONLY_FIELD, select answers 400 SELECT_FIELD_NOT_RETURNED.
Impact on integrators
None: those requests never worked. Filter by contactId (the primary contact) or by contactIds.
Affected endpoints: GET /v1/quotes, POST /v1/quotes/search, POST /v1/quotes/aggregate, POST /v1/{entity}/batch, POST /v1/batch.
BC-0918-2: A server development-team member gets their own key, and the key block gains the collaborator slot
Old format supported until: not provided
Before
Only the application owner could get an application key, and slot in the card's key block was
auth, api or null. Server development routes refused a team member.
After
A member of the server development team (an employee of the Bitrix24 account with role ADMIN or DEVELOPER) gets
their own key through POST /v1/cowork/applications/:id/key and can see another application's
sources and activeOperation. On the card such a key arrives with slot: "collaborator"; the
owner's key is never shown to them. The key works on thirteen development method/path pairs for
its own server, including deploy-lock release and short-lived api-bearer access tokens with a
required ttlSeconds of at most 600. The server icon is not among them — it belongs to the owner
and an ADMIN. The share-url mode is closed with 403 PORTAL_COLLABORATOR_TOKEN_MODE_FORBIDDEN,
an invalid TTL returns 400 INVALID_TTL, and only the caller's own token may be revoked (another
one returns 404). The new response can carry COLLABORATOR_KEY_REQUIRES_SERVER,
COLLABORATOR_MEMBERSHIP_BIND_CONFLICT, PORTAL_COLLABORATOR_KEY_OUT_OF_SCOPE,
PORTAL_COLLABORATOR_KEY_WRONG_SERVER, PORTAL_COLLABORATOR_NOT_A_MEMBER, and the
ENV_SYNC_NOT_APPLICABLE_FOR_COLLABORATOR warning.
What integrators should do
If your client parses slot as a closed set, add collaborator to it or map an unknown value to
null. A development-team key calling a method/path pair outside the list below answers
403 PORTAL_COLLABORATOR_KEY_OUT_OF_SCOPE.
Affected endpoints: POST /v1/cowork/applications/:id/key,
GET /v1/applications,
GET /v1/applications/:id and the thirteen pairs the member key works
on: GET /v1/infra/servers/:id,
POST /v1/infra/servers/:id/deploy,
POST /v1/infra/servers/:id/exec,
POST /v1/infra/servers/:id/upload,
GET /v1/infra/servers/:id/logs,
POST /v1/infra/servers/:id/wake,
GET /v1/infra/servers/:id/sources,
POST /v1/infra/servers/:id/sources,
GET /v1/infra/servers/:id/sources/:versionId,
GET /v1/infra/servers/:id/sources/:versionId/download,
DELETE /v1/infra/servers/:id/lock,
POST /v1/infra/servers/:id/access-tokens,
DELETE /v1/infra/servers/:id/access-tokens/:tokenId.
NEW-0918-3: An application created from Cowork no longer has to name its scopes
b24Scopes in POST /v1/cowork/applications is now optional. Omit it and the key gets every
Bitrix24 scope this account can grant: the catalog minus the feature-flag-gated scopes, plus the
gated ones this account is allowed, minus the ones Bitrix24 refuses to store on an incoming
webhook. An explicit list is still accepted and is how you ask for LESS; an empty array is
refused as before, because a key with no Bitrix24 scopes cannot be widened later. The widening is
not silent: the response carries B24_SCOPES_DEFAULTED_TO_ALL in warningCodes, and both audit
rows — application created and key issued — now carry scopesSource, either default or explicit. Omitting the
field and sending the full list explicitly are different bodies: the idempotency fingerprint
hashes the body as it arrived, not the resolved default.
BC-0918-4: The envSync field on a galaxy application key replacement returns the real delivery outcome
Old format supported until: 17.03.2027
Before — for an application on the Vibecode platform whose server is a Galaxy container,
replacing the key with syncServerEnv: true always answered
{ "status": "skipped", "reason": "galaxy" }: the platform could not deliver the key into the
container, and the field only reported that.
After — the platform delivers the key into the container environment, and the field returns
the outcome of that delivery: { "status": "recreated", "variable": "..." } (the key was
delivered and the container was recreated from the same image; alsoBakedInImage: true is added
when the OLD value is ALSO baked into the image and keeps answering until the app is redeployed
from source, with bakedVariable naming that image variable), { "status": "not_required" }
(the container environment carries no platform key at all),
{ "status": "baked_in_image", "bakedVariable": "..." } (the key comes from the image rather
than from the run: the variable can be overridden, only a rebuild fixes it for good), { "status": "recreate_failed", "reason": "..." }
(delivery failed, see reason), { "status": "unreachable" } (the galaxy host or the
application is asleep — nothing was touched), { "status": "not_found" } (the container runs on
a key generation the platform cannot account for), { "status": "superseded" } (delivery was
overtaken by a newer key replacement) or { "status": "failed" } (the platform could not even
work out where to deliver the key; that outcome is not galaxy-specific and is reachable on either
kind of server). The former
{ "status": "skipped", "reason": "galaxy" } stays reachable — it arrives when container
delivery is switched off for the account.
What integrators should do — handle every envSync.status value listed above, not just
skipped. A branch treating { "status": "skipped", "reason": "galaxy" } as the only possible
answer for a galaxy application now receives values it does not expect.
- Treat the key as delivered ONLY on
recreated. Onrecreate_failed,unreachable,baked_in_image,failedandskippedthe container still runs the previous key; onnot_foundthe platform cannot tell which key it runs at all — the environment holds a generation it never issued. In every one of those cases write THIS replacement's key in by hand, and do it beforepreviousKey.graceUntilfrom the same response. That deadline is not always a day and can be missing:nullmeans there is no grace at all (the previous key is blocked) and access is already gone. supersededis a separate case: this replacement's key has already been overtaken by a newer one, so writing it into the container is WRONG — the application would end up on a stale key. Follow theenvSyncoutcome of that NEWER replacement and use ITS raw key: the secret is handed out once, in the response to the replacement itself, and nothing can recover it afterwards — the application card carries metadata only. If you no longer have that response, make one more replacement with a NEWIdempotency-Keyand work from its answer.- On
recreatedwithalsoBakedInImage: trueand onbaked_in_image, also rebuild the application from source: the old value is baked into the image (named inbakedVariable), and the image hands it back on every deploy that skips a rebuild. Writing the variable by hand, as step 1 says, does work — it overrides the image value — but the override lives only until the next deploy: the rebuild is the permanent fix, the manual write only covers thepreviousKey.graceUntilwindow. - Treat an unknown
statusas an undelivered key rather than a success: the set of outcomes will grow.
The key replacement itself still succeeds regardless of the envSync outcome, and recreating
the container restarts the application and drops its open connections for the duration of the
restart. The former { "status": "skipped", "reason": "galaxy" } stays reachable during the
support window too — it arrives when container delivery is switched off for the account.
Affected endpoints: POST /v1/cowork/applications/{id}/key
FIX-0918-5: the icon upload tells a permission refusal apart from a missing server
Before
POST /v1/infra/servers/:id/icon answered 404 SERVER_NOT_FOUND even when the server does exist in the account but is bound to a different API key. A permission refusal was indistinguishable from a typo in id or a deleted server: a key that passes GET /v1/infra/servers/:id, deploy and source deposits was told "no such server" on the icon — with no statement of what was wrong or how to fix it.
After
The icon answers with the same code as the rest of the control plane: the server exists in your account but another key manages it — 403 WRONG_KEY, and the body carries error.hint with the recovery order (rebind the server in your account, then send the new key in the X-Api-Key header). 404 SERVER_NOT_FOUND stays on exactly its previous grounds: no server with that id, it was deleted, or it belongs to another account. Upload rights did not change — the icon still takes the managing key, because it rides on the application's catalog card. Details — App icon and Server access recovery.
FIX-0918-6: an application's external API now reaches the application instead of answering "not reachable"
Before
The ANY /v1/applications/{id}/api/* channel answered 503 with code APP_API_UNAVAILABLE
and the message "Application is not reachable" on every call that had passed the admission
checks — even when the application was running and opened fine from the cabinet and through an
external-access link. The refusal came back instantly, without an X-Vibe-Request-Id header,
and did not depend on where the application was hosted. The "Check schema" button on the
application card, which uses the same channel, reported that the application server was
unavailable.
After
The call reaches the application: its answer is returned as is — its own HTTP status, headers
and body are preserved, and a successful call stays successful. A transient 503
APP_API_UNAVAILABLE with Retry-After now means exactly what the channel documents — the
application really is not answering, rather than the channel being broken altogether.
FIX-0918-7: a function-call turn no longer duplicates the reasoning into content
Before
When a reasoning model called a function, the non-streaming POST /v1/chat/completions response came with finish_reason: "tool_calls", the reasoning in reasoning_content and the same chain in content as well: the client received the model's reasoning as the assistant's message. In streaming the reasoning did not reach content.
After
On a function-call turn content is null and the reasoning arrives in reasoning_content only, as in the function calling example. Responses without reasoning and responses with a final text are unchanged.
Affected endpoints: POST /v1/chat/completions.
NEW-0918-8: company-parked seats in the Cowork members method
The GET /v1/platform/cowork/members response now carries a new parking field in the seats
block — the number of paid seats parked with the company. A seat lands there when an employee
leaves the Bitrix24 account: it stays with the company, its term keeps running, and an administrator can
hand it to another employee.
Such seats count in neither assigned nor expiringWithin30Days. A seat whose term has already
ended is not counted: it can no longer be handed to another employee. For an unknown Bitrix24 account the
field comes as zero.
FIX-0918-9: employees who left no longer appear in the Cowork members list
Before
The data of the GET /v1/platform/cowork/members response also carried people who had left the
Bitrix24 account while a paid seat stayed assigned to them. Such a person looked like an employee without a
plan (plan.status: NONE), matched the NO_PLAN filter, and was counted in portal.usersTotal
and portal.usersInVibecode.
After
People who left do not appear in data and are not counted in usersTotal / usersInVibecode.
Their seats are not lost: a seat parked with the company is counted in the seats.parking field.
FIX-0918-10: an account whose plan was never asked about no longer gets a "paid plan required" refusal
Before
The 403 PORTAL_TARIFF_UNREADABLE refusal ("the plan could not be read") was only issued when the last licence probe had recorded a failed outcome. An account with no plan code AND no probe outcome at all — never probed, or probed before that mark existed — fell into the previous branch and got a 402 advising to buy a commercial Bitrix24 plan. That advice rested on an empty plan code, and the code is equally empty for an account whose licence the platform never asked about.
After
Both "could not ask" states are now told apart from "the account is on a free plan" in the same way: no plan code AND the probe either failed or was never recorded answers 403 with the code PORTAL_TARIFF_UNREADABLE, details.requiredTariffs is empty, and details.upgradeUrl and alternatives[0].url point to support. An account whose licence WAS read successfully keeps its behaviour: an empty code after a successful probe still means a free plan and still answers 402 with the previous copy.
FIX-0918-11: an access refusal now tells an unread subscription from a missing one
Before
An account whose subscription state the Vibecode platform could not read was refused with 402 and the code MARKETPLACE_REQUIRED on server creation and on key issuance, and was offered to buy a subscription. That advice could be wrong: the subscription state only arrives in the market envelope of the licence probe, which runs under a developer key of an account member — Bitrix24 refuses a key with a narrow rights set, and an account with a live subscription then looked exactly like an account without one.
After
The two states are now told apart. When the subscription state could not be read, the answer is 403 with the code PORTAL_SUBSCRIPTION_UNREADABLE: details.requiredTariffs is empty — no purchase clears this refusal — while details.upgradeUrl and alternatives[0].url point to support. The userMessage field names an action that can actually be taken: reconnect the application under the account main administrator so the developer key gets the rights to read the licence. The MARKETPLACE_REQUIRED code keeps its own state — the subscription was read and there is none: it still answers 402 with the previous copy. The change grants access to nobody: a refusal stays a refusal, and only the code, the status and the suggested action change.
NEW-0918-12: a server can be created already in the run mode you need
POST /v1/infra/servers accepts an optional runMode block of the same shape as
PATCH /v1/infra/servers/{id}/run-mode: {"mode": "ALWAYS"}, {"mode": "IDLE", "idleMinutes": 60}
or {"mode": "SCHEDULE", "scheduleId": "..."}. The machine is born in that mode — "create, then
switch" cost an extra call, and the machine hours of default running that were already billed.
The refusal comes before the machine is created, so a failed request leaves nothing behind:
WORK_SCHEDULE_NOT_FOUND — no schedule with that id in the account, WORK_SCHEDULE_EMPTY — the
schedule has no windows, RUN_MODE_UNAVAILABLE — run modes are not enabled for the account yet. The
body is discriminated on mode, so a field that does not belong to the chosen mode is rejected
rather than silently dropped. A request without the runMode block behaves exactly as before.
NEW-0918-13: storage and key-count ceilings for an account without a commercial plan
A Bitrix24 account without a commercial plan now has two optional ceilings the platform can switch on: on stored volume and on the number of live account keys. An account on a commercial plan — and any account that ever was on one — gets no ceilings.
A storage write that would take the account past the volume ceiling answers 507 with code STORAGE_QUOTA_EXCEEDED. Issuing a key past the key-count ceiling answers 409 with the already existing code KEY_LIMIT_REACHED; the details object of that response gains a scope field set to portal, which is what tells the account-wide ceiling apart from the per-employee limit, whose response carries no scope. The field is additive: a client that does not read it sees no difference. Requests below the ceiling answer exactly as before.
NEW-0918-14: a payment carrying a Cowork seat composition now creates seat rights
The composition assembled by the buyer in the Bitrix24 checkout is now applied: the
metadata.application block of payment.paid turns into paid seat rights, and rights[] in
GET /v1/platform/cowork/members fills up with plan-and-term pairs. A seat addressed to a
specific employee is reserved for them.
The composition is applied once per order — keyed by sale_order_id, at the moment every
receipt of the purchase has arrived.
Composition intake is switched on per Bitrix24 account. Until handing a right to an employee from the company console ships, it is open only on Bitrix24 accounts agreed for testing. On the rest such a payment is credited as a plain vibes top-up, and the composition of that payment is not applied.
Worth building into the client: after a Cowork purchase the Bitrix24 account balance does not grow by the
full payment amount. Paid seats are fenced off from the shared wallet right away, so they cannot
be spent on anything else. If the tokens did not cover the whole composition, as many rights are
created as the money covers and the remainder stays on the balance as vibes — in this order:
seats[] top to bottom first, then unassigned[].