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

API changes: September 12, 2026

← Changelog · September 2026

POST /v1/connect/device/authorize appends device_id to the verification_uri_complete link when the value sent in the request body fits the identifier shape: up to 128 characters, Latin letters, digits and the characters . : - _ only. The value goes into the link as it arrived, but under URL rules, so a : appears as %3A in the address — ordinary query-string parsing decodes it back.

A value outside that shape is dropped silently: the endpoint still answers 200, the link arrives without the parameter, and the response carries no sign of refusal. This is deliberate — the field is optional, and refusing would break sign-in for anyone whose identifier drifted.

This exists for login statistics: people often open the confirmation link in a different browser from the one where they started — copying it, or handing it to the system browser — and without a parameter inside the link itself there is nothing to tie the steps of one login together.

The change is additive: a request without device_id gets the link in its previous form, the other response fields are unchanged, and no client action is required.

BC-0912-3: a meaningless body on message edit, chat members and post update is refused, not executed

Old format supported until: not provided

Before

Three messenger and feed routes accepted a body without the required data and answered with success while doing something other than what the client asked. PATCH /v1/chats/{dialogId}/messages/{messageId} without message — empty, whitespace-only, [BR] line-break tags only, or with the text under another field name ({"text": …}) — sent an empty text to Bitrix24 and answered 200 "Message updated", while Bitrix24 deletes the message on a text that is empty by its own rule in this method (verified 2026-09-12: after such a call the message shows as deleted). The whole request body travelled to Bitrix24 too, and Bitrix24 folds parameter names case-insensitively taking the last key: {"MESSAGE": "text", "Message": ""} deleted the message as well (verified 2026-09-12). DELETE /v1/chats/{chatId}/users and POST /v1/chats/{chatId}/users forwarded userId and the elements of users to Bitrix24 as given, and Bitrix24 casts them to a number by PHP rules: the array [47] becomes 1, a word becomes 0. The call {"userId": [47]} answered 200 and removed the user with ID 1 — the account owner — from the chat instead of 47; on addition {"users": ["abc"]} answered 200 "added" while adding nobody, and a nested array crashed Bitrix24 (verified 2026-09-12). PATCH /v1/posts/{id} without a single changeable field ({} or the text under the name message) answered 200 "Post updated" after two calls to Bitrix24 that changed nothing.

After

Message edit requires a message with text by Bitrix24's own rule: empty, blanks only, [BR]/[br] tags only, neither a string nor a number, under another field name — 400 MESSAGE_REQUIRED before Bitrix24 is called, with the unrecognized field named in the error text; a finite number is accepted and stored as text. Deleting a message is only via DELETE /v1/chats/{dialogId}/messages/{messageId}. userId on member removal and every element of users on member addition must be a positive integer — a number, or a string of decimal digits without leading zeros or spaces, up to 2^53−1; otherwise 400 INVALID_PARAMS (for users the element index and type are named), an absent userId or an empty users — 400 MISSING_PARAMS. The value reaches Bitrix24 as a normalised number. String forms Bitrix24 resolved itself and that passed through the former forwarding (department<id>, structure<id>, network<id>) are now refused — only user ids are accepted. On message edit and member removal only the checked parameters travel to Bitrix24 (MESSAGE; CHAT_ID and USER_ID): other body keys, including native upper-case Bitrix24 keys (URL_PREVIEW, IS_EDITED, ATTACH, KEYBOARD) that used to ride along with the body, are no longer forwarded. A post update without any of text, title, recipients, files — 400 MISSING_PARAMS before Bitrix24 is called, with the unrecognized field named. A message edit call without a request body, which answered 500, now gets the same 400 MESSAGE_REQUIRED.

What integrators should do

Make sure a message edit always carries the text in the message field, and chat members are passed as numbers (or strings of digits without leading zeros) — userId as a scalar, users as a flat array. A post update must carry at least one changeable field. If the body of a message edit or a member removal carried native Bitrix24 keys beyond the documented fields — they no longer take effect. Calls that used to succeed with these bodies were actually deleting the message, touching the wrong member or changing nothing — now they get a refusal with an explanation.

Affected endpoints: PATCH /v1/chats/{dialogId}/messages/{messageId}, POST /v1/chats/{chatId}/users, DELETE /v1/chats/{chatId}/users, PATCH /v1/posts/{id}.

BC-0912-4: numeric path ids of the chat and notification endpoints are checked before the Bitrix24 call

Old format supported until: not provided

Before

The path id was read leniently: a value with a trailing tail or a fraction (125abc, 125.9) reached Bitrix24 as 125 and the operation ran against record 125 — the response was 200 or 204, as for a correct call. A fully non-numeric value (abc) reached Bitrix24 empty and was refused there (403 or 422).

After

The id must be a positive integer in canonical form: no sign, leading zero, fraction, exponent or spaces. Otherwise the endpoint answers 400 before the Bitrix24 call: for chatId — INVALID_CHAT_ID (as PATCH /v1/chats/:chatId already answered), for messageId, fileId and a notification id — INVALID_PARAMS. The error text names the parameter. Well-formed ids are processed as before.

Affected endpoints: DELETE /v1/notifications/:id, GET /v1/chats/files/:fileId, PATCH /v1/chats/:dialogId/messages/:messageId, DELETE /v1/chats/:dialogId/messages/:messageId, POST /v1/chats/:chatId/leave, POST /v1/chats/:chatId/owner, POST /v1/chats/:chatId/users, DELETE /v1/chats/:chatId/users, POST /v1/chats/:chatId/files, GET /v1/chats/:chatId/folder.

What integrators should do

Pass the id exactly as the API returned it: digits only. If the id is assembled from a string, strip tails and separators before putting it into the path. Clients that already pass ids as numbers change nothing.

FIX-0912-5: a pre-existing NodeSource configuration now survives an interrupted runtime install

Before

When deploying a node20* runtime onto a reused or hand-configured virtual machine, Vibecode moved your /etc/apt/sources.list.d/nodesource.list and nodesource.sources off the live path for the duration of the install and put them back at the end. If the install step was interrupted — by a timeout or by a dropped connection — the files stayed deleted on the server: later Node.js installs and upgrades over apt stopped using your repository until you restored it by hand.

After

An interrupted install step no longer loses your configuration: it stays on the server, and the next runtime deploy puts the files back on the live path on its own. When the NodeSource install succeeds, the live path keeps your file and the installer-created one is removed. Those two files are what gets restored: /etc/apt/preferences.d/nodejs, nsolid and the /usr/share/keyrings/nodesource.gpg key are overwritten by the installer and the platform does not put them back. If your nodesource.sources was created by the ordinary NodeSource install command, it matches the installer's file content for content, the platform cannot tell them apart, and an interrupted install may delete such a file — the same command restores it.

One special case: if your own nodesource.list sits next to it and names the same NodeSource repository and the same release (nodistro) on an active line, the platform moves its own file off the live path on an ordinary deploy, with no interruption at all. The file is not deleted — it stays beside it under a name suffixed .vibe-node20-conflict.disabled, apt does not read it, and you can remove it by hand. If an identical file from an earlier such fix is already there, the repeat one is simply removed from the live path. What this is for: if your line names that repository with a different key, apt refuses to read the source list as a whole and no packages can be installed on the server any more. If the key is the same there is no conflict, but the platform still steps aside: the live path keeps your file. If your .list points at a different repository — your own mirror, say — or the line is commented out, the platform leaves everything alone.

If you replace the file on the live path yourself between installs, your choice is left untouched and the saved copy of the previous configuration stays beside it under a name suffixed .vibe-node20-parked.disabled — apt does not read it, and you can delete it by hand. One exception: if you restore the configuration with the NodeSource install command itself rather than with your own file, the platform cannot tell it from its own, removes it and puts the saved copy back on the live path. On machines without a NodeSource configuration of their own, nothing changed.

FIX-0912-6: the eighth unavailability reason now reaches self-hosted accounts as well

Before

In the activation.marketTrial block of GET /v1/cowork/state the reason not_required_for_data was returned only on cloud accounts whose platform access is opened by a paid plan of that kind, and only once the matching platform setting was enabled. A self-hosted account that was offered the trial (available: true) kept getting that offer with the setting enabled too.

After

The reason is still returned only on accounts whose platform access is opened by a paid plan of that kind, and only once the matching platform setting is enabled, but now on both account types: a self-hosted account that would be offered the trial gets available: false with reason not_required_for_data instead of available: true — the same as a cloud account. Without the setting the answer is unchanged on both types. The set of eight reasons is unchanged, and so is their precedence — a reason from the ladder, where one applies, still wins. Clients need to do nothing: the default branch on any false already hides the activation step.