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

API changes: September 18, 2026

← Changelog · September 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.

  1. Treat the key as delivered ONLY on recreated. On recreate_failed, unreachable, baked_in_image, failed and skipped the container still runs the previous key; on not_found the 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 before previousKey.graceUntil from the same response. That deadline is not always a day and can be missing: null means there is no grace at all (the previous key is blocked) and access is already gone.
  2. superseded is 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 the envSync outcome 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 NEW Idempotency-Key and work from its answer.
  3. On recreated with alsoBakedInImage: true and on baked_in_image, also rebuild the application from source: the old value is baked into the image (named in bakedVariable), 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 the previousKey.graceUntil window.
  4. Treat an unknown status as 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[].