# API changes: September 25, 2026

[← Changelog](/docs/changelog) · [September 2026](/docs/changelog/2026-09)

### BC-0925-1: an unreadable field id is refused before a smart-process field is changed or deleted

> Old format supported until: not provided

**Before**

[PATCH /v1/items/{entityTypeId}/userfields/{id}](/docs/userfields/smart-processes/update) and [DELETE /v1/items/{entityTypeId}/userfields/{id}](/docs/userfields/smart-processes/delete) accepted any string in `:id`. A value starting with digits was silently cut at the first foreign character: an `:id` of `7abc` changed or irreversibly deleted the field numbered 7 — a different one than requested — and the response was a success (`{"updated": true}` and `204`). A value with no digits reached the Bitrix24 account empty, and the request ended with a Bitrix24 error about a missing required parameter.

**After**

`:id` must be a positive integer in plain notation: digits only, with no leading zero, sign, fractional part, exponent or spaces, and no greater than `9007199254740991`. Otherwise the Vibecode platform answers `400 INVALID_PARAMS` and names the parameter in the message text. No call to the Bitrix24 account is made on such a refusal, and the field is neither changed nor deleted.

**What integrators should do**

If the field id is taken from the [smart process field list](/docs/userfields/smart-processes/list) and passed through as is, nothing changes — such calls work as before.

Three classes of values are now refused, and they differ in what used to happen:

- **a FOREIGN field used to be changed or deleted under a success code** — a string with digits first and a foreign tail (`7abc` read as 7), an exponent (`7e2` read as 7, not 700), and an id greater than `9007199254740991` (which shifted to a neighbouring one when converted). These calls changed something other than what you asked for and said nothing about it;
- **the operation used to hit the RIGHT field under a success code** — an id written in a non-plain form: a leading zero (`007`), a sign (`+7`), a fraction (`7.0`), surrounding spaces. Such calls worked correctly and now get a `400`. This is the only class the change breaks for an integration that worked correctly — drop the extra characters and leave the digits only;
- **an account error used to come back** — a value with no digits, including a system field name such as `UF_CRM_…`. The Vibecode platform now answers `400` itself, before calling the account.

In all three cases, pass the numeric field id from the field list exactly in the form it comes in there.

**Affected endpoints:** [PATCH /v1/items/{entityTypeId}/userfields/{id}](/docs/userfields/smart-processes/update), [DELETE /v1/items/{entityTypeId}/userfields/{id}](/docs/userfields/smart-processes/delete) and their short addresses for smart invoices, `/v1/userfields/invoices/{id}`.

### FIX-0925-3: sign-in from Cowork now opens Galaxy apps too

**Before**

[POST /v1/cowork/app-login](/docs/cowork/app-login) issued a sign-in only for an app on a dedicated server. For the address of a Galaxy app it answered `404 APP_NOT_AVAILABLE`, even when the access policy admitted the key owner.

**After**

A Galaxy app goes through the same access check as an app on a dedicated server and, once admitted, gets an HTTP 200 response with the sign-in address. A Galaxy host machine still answers `404 APP_NOT_AVAILABLE`, since it is not an app.

### FIX-0925-4: a server whose creation failed on the cloud side no longer hangs in `PROVISIONING`

**Before**

When the cloud refused after creation had already started and the request carried an
`Idempotency-Key` header, the server stayed in `PROVISIONING` with no cloud machine id. A retry with
the same key returned the same record, and it looked like "still being created" forever:
`GET /v1/infra/servers/:id` returned `status: "PROVISIONING"` even though creation had already failed.

**After**

Such a server is given `status: "ERROR"` right away. The creation response itself is unchanged — it is
still `502 PROVIDER_ERROR` with the same text, and a retry with the same `Idempotency-Key` still
returns the same record instead of creating a second machine. Only the status in the record changed:
it now names the outcome honestly, and such a server shows up among the failed ones rather than among
those still being created.

### FIX-0925-5: the sleeping-app log read no longer names a wake address that is banned on the host

**Before**

[GET /v1/infra/servers/{id}/logs](/docs/infra/deploy/logs) on a sleeping Galaxy app returned `recovery.recoveryAction` with the wake address whenever no door refused the caller by their key. A wake ban on the galaxy host itself (a subscription or plan wall, a host block) was not part of that signal at all, so a fully privileged owner got the machine-readable address, called it and received `402` or `403 SERVER_WAKE_BLOCKED`. The same signal also emitted `recovery.deliveryWakesHost` — the paid second way up, which does not work on a blocked host either.

**After**

When waking is banned on the host, the answer names the wake address in no field — neither `recovery.recoveryAction` nor the `hint` text — and it does not name `recovery.deliveryWakesHost`. Instead of an address, `hint` names the condition: the ban sits on the shared host, the account owner lifts it, and changing the key does not help. The log-read response itself remains HTTP 200 with an empty list of lines, as before. The `recovery.wakeSchedule` field does not change in that state: creating a recurring window stays available, and the prose warns separately that a window will not bring the host up.

**Influence on integrators**

Nothing to change if you already branch on the PRESENCE of `recovery.recoveryAction`, as the documentation requires. A client that used the wake address unconditionally will stop receiving a guaranteed refusal from it.

### FIX-0925-6: a cursor with a corrupted date in revenue exports is rejected with INVALID_CURSOR instead of a 500 error

**Before**

A cursor with a corrupted date — for example, one assembled or edited by hand — broke the request: instead of rejecting the cursor, the export answered HTTP 500. This affected `GET /v1/platform/revenue/topups`, `GET /v1/platform/revenue/charges`, `GET /v1/platform/revenue/expirations`, `GET /v1/platform/revenue/refunds`, `GET /v1/platform/revenue/consumption/by-service`, `GET /v1/platform/revenue/consumption/reconstructed` and `GET /v1/platform/revenue/money-in/payments`.

**After**

Such a cursor is rejected with `400 INVALID_CURSOR`, just like a cursor issued for a different window. A cursor taken from the `nextCursor` field of the previous page works as before, and the traversal order and page contents are unchanged.

**Impact on integrators**

No changes are needed. A client that retried the request after an HTTP 500 response now gets `400 INVALID_CURSOR` right away: retrying with the same cursor will not help, so restart the traversal without `cursor`.

### FIX-0925-7: the charges export includes AI usage over the quota

**Before**

Vibe charges for AI usage over the Bitrix24 account quota did not appear in `GET /v1/platform/revenue/charges` and `GET /v1/platform/revenue/consumption/by-service`: such a charge row carried no per-tranche breakdown, and both exports are built from it. Purchased vibes consumed this way were visible only indirectly, as a lower `remaining` in `GET /v1/platform/revenue/topups`.

**After**

A charge for AI usage over the quota records its per-tranche breakdown like every other charge and appears in both exports: in `/charges` as a per-tranche movement and in `/consumption/by-service` with the `AI_TOKENS` service type. Such a charge is one row per Bitrix24 account per Moscow day: it belongs to the UTC day of its first overage and keeps growing until the end of that Moscow day, so re-request the latest closed UTC day after 21:00 UTC on the following day. External AI overage above quota is now recorded the same way, with its own row for each charge. Charges recorded before this fix carry no breakdown and stay out of the exports. The response format is the same, and the response remains HTTP 200.

### FIX-0925-8: bot file upload checks the required fields before calling Bitrix24

**Before**

[POST /v1/bots/:botId/files](/docs/bots/files/upload) passed the body to Bitrix24 as is. With `dialogId` missing, or the file sent under unrecognised field names (`fileName` and `fileContent`, for example), the request reached Bitrix24 without its required parameters and came back with code `100` — "required parameter missing" — naming no parameter.

**After**

The required fields are checked before the Bitrix24 call. Without `dialogId`, the file name or content, the answer is `400 MISSING_PARAMS`. The error text lists the unrecognised body fields and shows the correct request shape.

**Impact on integrators**

A correct request in any of the three supported body formats works as before. A client that used to get `100` now sees a specific refusal from the Vibecode platform and knows which field is missing.

### FIX-0925-9: Editing the scopes of a key whose webhook stayed on the previous Bitrix24 account address answers PORTAL_ADDRESS_CHANGED

**Before**

[PATCH /v1/keys/:id](/docs/management-keys) with a new `scopes` list for a key whose webhook was still issued for the previous address after the Bitrix24 account address changed answered one of the scope sync refusals — `410 STALE_DEVELOPER_KEY`, `502 RECOVERY_FAILED` or `502 DEVKEY_SCOPE_SYNC_FAILED`. None of them named the actual cause: retrying did not help, and the advice to reconnect the account did not fix this case. Every call of such a key to the Bitrix24 account already answered `409 PORTAL_ADDRESS_CHANGED`.

**After**

Editing the scopes of such a key answers the same `409 PORTAL_ADDRESS_CHANGED` and does not contact the Bitrix24 account. The key's scopes change neither on the platform nor in Bitrix24. The other scope edit responses are unchanged.

**Impact on integrators**

Handle `409 PORTAL_ADDRESS_CHANGED` the same way as on the key's other calls: reconnect the key — its webhook is re-issued for the current address — then repeat the scope edit. The code is described in [Authorization, keys and permissions](/docs/errors/auth).

### FIX-0925-10: the workflow list answers with a clear code on an account without the module

**Before**

When the business processes module is not included in the account plan or is switched off in its settings, Bitrix24 answers the `bizproc.workflow.instances` method with a "method not found" message. The [GET /v1/workflows](/docs/automation/workflows/list) request surfaced that as `404 ENTITY_NOT_FOUND` — a message about a missing entity, while both the entity and the request itself were fine. The `ENTITY_NOT_FOUND` code was absent from the page error table, and the cause of the refusal could not be read off the response.

**After**

The same account state answers `409 BIZPROC_MODULE_NOT_ENABLED`. The message names both possible causes and the action: ask an account administrator to enable business processes. The response remains a 4xx client refusal, and the error-loop protection does not count it — it stays informative for any number of retries instead of turning into `429 ERROR_LOOP_DETECTED`.

**Integrator impact**

No action required: the refusal was and remains a 4xx response. An integration that told the disabled module apart by the Bitrix24 message text can switch to the `409 BIZPROC_MODULE_NOT_ENABLED` code. The former `404 ENTITY_NOT_FOUND` was never promised by the documentation for this state. The other operations of the Workflows section answer as before — the change affects the list of running workflows only.

### FIX-0925-11: BitrixGPT 5.5 accepts json_schema when the model supports structured outputs

**Before**

[POST /v1/chat/completions](/docs/ai/chat/completions) with `response_format.type = json_schema` and model `bitrix/bitrixgpt-5.5` returned `400` `model_does_not_support_structured_outputs`, while [GET /v1/me](/docs/keys-auth/me) did not list structured outputs.

**After**

BitrixGPT 5.5 and Thinking models that support structured outputs appear in [GET /v1/me](/docs/keys-auth/me), and `json_schema` requests pass the capability check. Clients do not need to change their request format.

**Impact on integrators**

No integration changes are required: continue using `response_format.type = json_schema`.

### FIX-0925-12: storage: reads and deletes by key pick a non-deleted copy of the file

**Before**

When several copies of a file lay under one key and the earliest of them was deleted, [GET /v1/storage/objects/{key}](/docs/storage/objects/get), [HEAD /v1/storage/objects/{key}](/docs/storage/objects/head) and [DELETE /v1/storage/objects/{key}](/docs/storage/objects/delete) returned `410 STORAGE_OBJECT_DELETED`, although the file stayed in the [object list](/docs/storage/objects/list). After the first delete under such a key, every further DELETE returned the same, and the remaining copy was not deleted.

**After**

A request by key picks the earliest non-deleted copy of this file. While such a copy exists, the file is read and deleted by key, and each DELETE deletes one copy. When no non-deleted copies remain, the response is unchanged: `410 STORAGE_OBJECT_DELETED`.

**Impact on integrators**

No changes are needed. For a file with several copies, a repeated DELETE by key deletes the next copy and returns `410 STORAGE_OBJECT_DELETED` only when no copies remain.

### BC-0925-13: mailbox listing without the Mail module answers with a clear 409

> Old format supported until: not provided

**Before**

When the Mail module is absent from the Bitrix24 account, the method is not included in the plan, or the account has not yet received the update carrying `mail.mailbox.list`, Bitrix24 answered "method not found", and [GET /v1/mail/mailboxes](/docs/mail/mailboxes/list) surfaced it as `404 ENTITY_NOT_FOUND`. The account state was indistinguishable from a missing record.

**After**

Only `GET /v1/mail/mailboxes` answers `409 MAIL_MODULE_NOT_ENABLED`. The message names the possible causes — the module is not installed, the method is not included in the plan, or the account has not yet received the update carrying `mail.mailbox.list` — because there is no way to tell them apart from the outside. Other Bitrix24 refusals on this route are unchanged: a permission refusal stays `403`, and a rate limit stays `429` with its own `Retry-After`.

**Impact on integrators**

Successful responses are unchanged. If your code branched on `404` for mailbox listing, add a `409 MAIL_MODULE_NOT_ENABLED` branch. Do not blindly retry an unchanged `409`; retry after the module is enabled, the plan changes, or the account is updated. Neighbouring mailbox routes are not affected.

### BC-0925-14: the task chat feed for a read-only key — a page holds at most 50 messages

> Old format supported until: not provided

**Before**

[GET /v1/tasks/:taskId/chat/messages](/docs/entities/tasks/chat) could serve any key, a read-only key included, a page of up to 200 messages — as many as passed in `limit`.

**After**

For a read-only key a page of the task chat feed holds at most 50 messages for any `limit`: reading a page of up to 200 messages may make the user a member of the task chat, that is, it changes the data of the Bitrix24 account, and a read-only key does not change that data. For such a key `hasNextPage` is derived from how full the page is: a full page means the history may go on. The response remains HTTP 200 of the same shape. A key with write access reads the feed as before, up to 200 messages per page.

**What integrators should do**

If a read-only key requests a `limit` above 50, page through the history with the `lastId` cursor until `hasNextPage: false` and do not expect a page to hold the whole `limit`. To get up to 200 messages per page, switch the key to write access, as described on the [access rights](/docs/access-rights) page.

### NEW-0925-15: Chats: the format=v2 mode, chat loading, counters and messages around a message

The chats section gained the `format=v2` mode on two endpoints and three new endpoints. [GET /v1/chats/recent](/docs/chats/discovery/recent) with the `format=v2` parameter returns the list of recent dialogs in pages by the `lastMessageDate` cursor and the `hasNextPage` flag, and [GET /v1/chats/:dialogId/messages](/docs/chats/messages/list) with the same parameter reads the feed by the `lastId` cursor in both directions via `order`. Without `format=v2` both endpoints respond as before. The new endpoints [GET /v1/chats/:dialogId/load](/docs/chats/messages/load) open a chat in one request — the card, the first page of messages and the pinned ones, [GET /v1/chats/counters](/docs/chats/discovery/counters) returns unread counters per chat, and [GET /v1/chats/messages/:messageId/context](/docs/chats/messages/context) returns a message together with its neighbours. Limits outside the range are clamped with an echo in `meta`, an unknown or repeated parameter, except `format`, is refused with `400 INVALID_PARAMS`; a repeated `format` uses its last value. The `format[]=v2` form is also accepted when every element is `v2`. The Bitrix24 refusal code arrives in `error.b24Code` for `422` and `404` responses. A date that does not exist on the calendar (`2026-02-30`, `24:00`) in the `lastMessageDate` cursor and in `updatedAfter` is refused with `400 INVALID_PARAMS` instead of being shifted to a neighbouring day. Opening a chat, messages around a message and the v2 feed may make the user a chat member when the chat allows auto-join, so they count as writes: a read-only key gets `403 WRITE_BLOCKED_READONLY_KEY` for them, as described on the [access rights](/docs/access-rights) page.

### FIX-0925-16: a dropped connection to Bitrix24 answers 502 BITRIX_UNAVAILABLE

**Before**

When the connection to Bitrix24 dropped before an answer arrived (a reset or refused connection, a TLS failure, a break in the middle of the answer), a request for Bitrix24 data got `500 INTERNAL_ERROR`, as if the platform itself had failed. [POST /v1/batch](/docs/batch) answered the same way.

**After**

Such a drop answers `502 BITRIX_UNAVAILABLE`, as the [error reference](/docs/errors/platform) describes. `error.message` names the machine code of the cause when it is known, for example `ECONNRESET`, and `error.hint` gives the retry rule: a read is safe to repeat with backoff, and before retrying a write, re-read the record. The platform does not repeat the request itself.

**Impact on integrators**

No action required. If you retry with backoff on `BITRIX_UNAVAILABLE`, short network failures now land in that branch instead of `INTERNAL_ERROR`.

### NEW-0925-17: Vibe credits spend per Bitrix24 account: GET /v1/platform/revenue/spend

The Revenue Export API showed consumption of purchased Vibe credits only, for closed UTC days only, and broken down no deeper than the service type: `GET /v1/platform/revenue/consumption/by-service` returned neither gifted credits, nor the current day, nor the server plan, the Cowork tier or the AI model.

The new `GET /v1/platform/revenue/spend` method (key scope `revenue:spend`) returns the actual Vibe credits spend: day × Bitrix24 account × service × price item × credits source, for a window of up to 92 days from 2026-06-30 up to and including the current day, as JSON or CSV. The source (`funding`) tells apart purchased credits, the starter grant, Bitrix24 bonuses, manual credits, compensations, credits returned by a refund, charges made on credit and charges whose source was not recorded. A page is one UTC day, walked with `nextCursor`; days from `finalBefore` on are marked `provisional` and may still change. Other methods are unchanged.

### FIX-0925-18: a wake window on a server in the scheduled run mode can be created and edited through the API

**Before**

Entry FIX-0916-10 promised that a server in the `SCHEDULE` run mode is not treated as always-on and that an extra wake window on it is created the usual way. Through the API the promise did not hold: [POST /v1/infra/servers/{id}/wake-schedules](/docs/infra/wake-schedules/create) and [PATCH /v1/infra/servers/{id}/wake-schedules/{scheduleId}](/docs/infra/wake-schedules/update) answered `400 ALWAYS_ON_CONFLICT` for such a server on a non-preemptible plan with no sleep timeout — both to the owner's key and to the key of a server team admin.

**After**

A server in the `SCHEDULE` run mode gets a wake window through the API the usual way: creating returns `201`, editing returns `200`, as for any server a window is allowed on. A server in the `ALWAYS` run mode on a non-preemptible plan still gets the `400 ALWAYS_ON_CONFLICT` refusal, except an app in a galaxy: it inherits the galaxy's plan and is not treated as always-on.

**Impact on integrators**

No action needed.

### FIX-0925-19: employee directory uses available keys

`GET /v1/infra/servers/:id/b24-users` finds employees even when the server key has no access to the directory.

**Before**

If the server key lacked the `user` scope, Bitrix24 refused the call and the response was empty: `{ "success": true, "data": [] }` — with no reason given.

**After**

If the server key is denied by scope, Vibecode tries other keys of the same Bitrix24 account: the server's fallback key, then keys of active Bitrix24 account administrators. If no key has access to the directory, the response remains HTTP 200 with an empty `data` and includes a `hint` explaining which scopes to grant. On a network error the response is unchanged: HTTP 200 and `{ "success": true, "data": [] }`.

### BC-0925-20: Bitrix24 events and automation rule callbacks arrive without Bitrix24 tokens under a Read-only key

> Old format supported until: not provided

**Before**

A server on the Vibecode platform received Bitrix24 events and business process automation rule callbacks with `auth[access_token]` and `auth[refresh_token]` whatever the key modes were.

**After**

If the server key or the authorization key of the subscribed app is in the Read-only mode, the event and the callback arrive without `auth[access_token]` and `auth[refresh_token]`, and the gateway does not add the event user headers to them. The same applies to a server whose key is deleted or revoked, and to an app without an active authorization key. An event sent to an app address outside the platform — neither a Black Hole subdomain nor a server's custom domain — arrives without these tokens whatever the key modes. `auth[application_token]`, `auth[user_id]` and the event data stay. Exchanging the event token for a session through `POST /v1/oauth/placement-session` is not available to such a handler, and an automation rule waiting for an answer is never completed: answering it is a Bitrix24 write. When the app address points to a platform server, an event raised by a user whom that server's access policy does not admit is not delivered; an event sent to an external address is not filtered by the access policy.

What to do: for reads, call the Vibecode API with your own key — the calls run on behalf of the key owner. To get the tokens and the automation rule answers back, switch both keys — the server key and the app key — to Read and write. If the app address is external, move the handler to a platform server: it receives the tokens when both keys are in Read and write.

### BC-0925-21: deploying with a Read-only key does not post the new version announcement

> Old format supported until: not provided

**Before**

`POST /v1/infra/servers/:id/deploy` with the `changelog` field posted the new version text to subscribers in the app's Bitrix24 messenger channel feed whatever the key mode was.

**After**

Deploying with a Read-only key does not post the announcement: posting to the feed is a Bitrix24 write. The version note is still saved to the source depot, the catalog card and the icon are published, and the response `warnings` carries a notice. The HTTP 200 response is unchanged.

What to do: to post announcements, deploy with a key in the Read and write mode.

### NEW-0925-22: `note` field in the `eventDelivery` block of the `GET /v1/me` response

The `eventDelivery` block of the `GET /v1/me` response has a new `note` field. It explains under which key modes events and callbacks arrive without `auth[access_token]` and `auth[refresh_token]`, what follows from that, and how to get the tokens back. The field is present whenever the response has the `eventDelivery` block, whatever the mode of the key calling `GET /v1/me`.

### NEW-0925-23: Vibe credits by Bitrix24 account: GET /v1/platform/revenue/credits

The Revenue Export API returned only purchased tranches (`GET /v1/platform/revenue/topups`) and their expirations (`/expirations`): gifted credits — the starter grant, Bitrix24 bonuses, manual credits, compensations — as well as credits returned by a refund, purchased ones included, never appeared in the exports, so the path of credits in a Bitrix24 account from being credited to a zero balance did not add up.

The new `GET /v1/platform/revenue/credits` method (key scope `revenue:spend`) returns every Vibe credits tranche per Bitrix24 account, purchased and gifted, one row per tranche: the kind of credit (`kind` takes the same values as `funding` in `/spend`), the tranche source, for a refund the item of the refunded subscription, the amounts "credited / spent / remaining / expired / revoked" as of the request, and the credit, expiry and revocation dates. The `from`/`to` window is optional and filters the credit date, the current day included; a page holds up to `limit` rows, walked with `nextCursor`; the format is JSON or CSV. Other methods are unchanged.
