[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"docs-changelog\u002F2026-09-01":3,"docs-tabs-changelog\u002F2026-09-01":6},{"content":4,"lastmod":5},"# API changes: September 1, 2026\n\n[← Changelog](\u002Fdocs\u002Fchangelog) · [September 2026](\u002Fdocs\u002Fchangelog\u002F2026-09)\n\n### FIX-0901-1: a permission refusal on the sprint board arrives as 403, not as a platform error\n\n**Before**\n\nA key whose user has no access to the scrum project got `422 BITRIX_ERROR` for the sprint board columns — the same class the Vibecode platform uses to report a Bitrix24 business error. The code gave no way to tell \"no rights\" from \"something is temporarily off on the account\", so the request was retried even though the state is stable and a retry changes nothing.\n\n```\nHTTP 422\n{ \"success\": false, \"error\": { \"code\": \"BITRIX_ERROR\", \"message\": \"Access denied\", \"b24Code\": \"0\" } }\n```\n\n**After**\n\n`GET \u002Fv1\u002Fscrum\u002Fsprints\u002F{sprintId}\u002Fstages` and `POST \u002Fv1\u002Fscrum\u002Fsprints\u002F{sprintId}\u002Fstages` answer a permission refusal with `403 BITRIX_ACCESS_DENIED` — the code the common error reference already describes. It reads as \"do not retry, grant the user access to the scrum project instead\".\n\n```\nHTTP 403\n{ \"success\": false, \"error\": { \"code\": \"BITRIX_ACCESS_DENIED\", \"message\": \"Access denied\" } }\n```\n\nSuccessful responses are unchanged: the column list still arrives with HTTP 200 and a created column with HTTP 201. The answer for a missing sprint is unchanged too — it stays `404 ENTITY_NOT_FOUND`.\n\n### BC-0901-2: an empty employee photo is refused on update\n\n> Old format supported until: not provided\n\n**Before**\n\nOn employee UPDATE, an empty or whitespace-only string in `personalPhoto` reached Bitrix24 as a command to remove the current photo. The single `PATCH \u002Fv1\u002Fusers\u002F:id` returned `200 success:true` even though the photo was removed.\n\n**After**\n\nSuch values are refused with `INVALID_PARAMS` before the Bitrix24 call. The single `PATCH \u002Fv1\u002Fusers\u002F:id` returns `400 success:false`; batch routes keep their existing error envelopes. The global `POST \u002Fv1\u002Fbatch` also refuses `personalPhoto: null`: its sub-call encoder turns `null` into an empty query-string value, the same remove-the-photo command. On `PATCH \u002Fv1\u002Fusers\u002F:id` and in the per-entity batch, `null` gets no new refusal. To leave the photo unchanged, omit `personalPhoto`. On CREATE in `POST \u002Fv1\u002Fusers`, an empty string does not get the new refusal; the working inline pair and separate employee-invite validation are unchanged.\n\n### FIX-0901-3: rate limit descriptions now name the per-process share, not just the total cap\n\n**Before**\n\nThe descriptions of [POST \u002Fv1\u002Ffeedback](\u002Fdocs\u002Ffeedback\u002Fsubmit) in the `GET \u002Fv1\u002Fguide` directory, in the `feedback.limits` block of the [GET \u002Fv1\u002Fme](\u002Fdocs\u002Fkeys-auth) response and in the `GET \u002Fv1\u002Fopenapi.json` schema named the limit as a number — \"5 requests per minute\". Requests are served by several platform processes and each one holds its own share of the limit, so the client received less than promised and the `X-RateLimit-Limit` header disagreed with the description. The same mismatch was present on the help pages for submitting a ticket and uploading an attachment, and in the schema for five operations of the Cowork section.\n\n**After**\n\nThe descriptions name the number and explain next to it that the cap is divided across platform processes, while the value actually in force arrives in the `X-RateLimit-Limit` header. For the Cowork operations in the schema, the `429` refusal description is aligned with the form already used on the help pages. A single shared `429` row on the feedback page is split in two — creating a ticket and uploading an attachment carry different counters.\n\n**Impact on integrators**\n\nThe limit values did not change and the behaviour is the same as before. A client that relied on the number from the description was refused earlier than expected — the source of the value in force is now named explicitly: read `X-RateLimit-Limit` from the response.\n\n### BC-0901-4: task comment updates reject read-only fields\n\n> Old format supported until: not provided\n\n**Before**\n\nOn a legacy card,\n[PATCH \u002Fv1\u002Ftasks\u002F:taskId\u002Fcomments\u002F:id](\u002Fdocs\u002Fentities\u002Ftask-comments\u002Fupdate)\ncould return HTTP 200 when a client sent a comment object from GET with an\nupdated `message`, forwarding read-only fields to Bitrix24. An item in\n[POST \u002Fv1\u002Ftasks\u002F:taskId\u002Fcomments\u002Fbatch](\u002Fdocs\u002Fentities\u002Ftask-comments\u002Fcomments-batch)\ncould similarly finish with `success:true`.\n\n**After**\n\nA single PATCH containing a read-only field returns HTTP 400\n`READONLY_FIELD` before Bitrix24 is called. For a successful legacy-card\ncomment update containing only `{message}`, the response remains HTTP 200; the\nexisting `410 GONE` remains unchanged on the new card.\n\nIn the custom batch, the top-level response remains HTTP 200 with\n`success:true`; the rejected item gets `success:false` and\n`error:READONLY_FIELD`, is not sent to Bitrix24, and does not stop valid sibling\nitems.\n\n**What integrators should do**\n\nFor a single PATCH, build a new body containing only `{message}`; do not send\nresponse fields such as `id`, `taskId`, `authorId`, `createdAt`, or their aliases\nback to the endpoint. In a custom batch item, keep lowercase `id` as the comment\nselector and send only `message` beside it; remove an additional `ID` and all\nother read-only fields. Clients that already send only documented writable\nfields need no changes.\n\n### FIX-0901-5: the 401 TOKEN_MISSING error now names the reason on every V1 route\n\n**Before**\n\nThe reason a personal key had no Bitrix24 webhook was named only on entity routes\n(`\u002Fv1\u002Fdeals`, `\u002Fv1\u002Ftasks` and so on) and in [POST \u002Fv1\u002Fbatch](\u002Fdocs\u002Fbatch). Every other\nfamily — chats, lists, mail, scrum, notes, Open Channels, bots, userfields and the rest —\nanswered with the flat text \"API key has no tokens configured.\" and no `error.details`\nfield. The same broken key got an explanation on `\u002Fv1\u002Fdeals` and a text that implied\nnothing on `\u002Fv1\u002Fchats`.\n\n**After**\n\nThe `401 TOKEN_MISSING` body is built the same way on every V1 route: a personal key gets\nthe reason in `error.details.reason` plus the text naming the required action, an app key\ngets a pointer to the missing OAuth step. The response remains HTTP 401 and the set of\ncodes is unchanged.\n\n**Impact on integrators**\n\nRead `error.details.reason` — that is the contract, not `error.message`. The message text\non these routes has changed, so code comparing it with the literal \"API key has no tokens\nconfigured.\" stops matching; that parsing was unreliable before as well. The reasons are\nlisted in the `GET \u002Fv1\u002Fme` response under `b24Credentials`.\n\n### FIX-0901-6: direct upload can be repeated after object deletion\n\n**Before**\n\nAfter a successful [DELETE \u002Fv1\u002Fstorage\u002Fobjects\u002F{key}](\u002Fdocs\u002Fstorage\u002Fobjects\u002Fdelete), a new [direct POST \u002Fv1\u002Fstorage\u002Fobjects\u002Fupload](\u002Fdocs\u002Fstorage\u002Fupload\u002Fdirect) with the same key returned `409 STORAGE_KEY_DELETED` until the object was physically purged 30 days later.\n\n**After**\n\nFor an app object in the `COMPLETED` state without a multipart session, direct upload returns HTTP 200, preserves the same `id` and `visibility`, and writes new content. A deleted public link with that `id` becomes available again with the new bytes. This does not restore the previous content.\n\nPath B remains disabled and returns `503 STORAGE_PRESIGNED_UPLOAD_DISABLED` without a URL. Path C, deleted `PENDING` objects, and objects with a multipart session continue to return `409 STORAGE_KEY_DELETED`. DELETE itself and subsequent reads without a new eligible direct upload continue to return `410 STORAGE_OBJECT_DELETED` during the retention period.\n\n**Impact on integrations**\n\nNo request changes are required. After deleting an eligible object, send the new bytes with a regular direct upload using the same key.\n\n### FIX-0901-7: a transient image-registry outage no longer marks a galaxy app broken\n\n**Before**\n\n`POST \u002Fv1\u002Finfra\u002Fservers` with a `source` field that hit `502 GALAXY_BASE_IMAGE_UNAVAILABLE` — or ran the host out of disk — flipped the slot to `status=error`, even though the same refusal on `POST \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fdeploy` left the status untouched and the error text promised the slot, its container and its `\u002Fdata` volume were intact. Re-sending the deploy never cleared that `error`: `GET` kept reporting a broken slot, and the app stopped holding its host awake.\n\n**After**\n\nBoth transient refusals leave the slot at `status=provisioning`; the cause stays readable in `provisionError` of `GET \u002Fv1\u002Finfra\u002Fservers\u002F:id`. Re-sending the same deploy to a slot broken by such a cause clears `error` at the start of the attempt. An abandoned slot is still failed by the platform after ~20 minutes, but it keeps its real cause instead of the generic `DEPLOY_INCOMPLETE`. The `error.hint` copy, the `\u002Fv1\u002Fme` checklist and the docs no longer promise a \"2–3 minutes\" window: the registry outage can last an hour.\n\n### BC-0901-8: Regular chat-bot blocks key deletion\n\n> Old format supported until: not provided\n\n**Before**\n\nDeleting an API key could delete its linked regular chat-bot together with the token required for normal unregistration on Bitrix24.\n\n**After**\n\nDeleting a key with a regular chat-bot, including a disabled Bot Platform row, returns `409 KEY_HAS_LINKED_AGENT`. Take numeric `bitrixBotId` from `details.bots` in the `409` response, then unregister the bot through `DELETE \u002Fv1\u002Fbots\u002F:botId` or transfer it to another key.\n\n### FIX-0901-9: file uploads and AI requests now answer 429 under memory pressure\n\n**Before**\n\n`POST \u002Fv1\u002Ffiles\u002Fupload` (up to 70 MB), `POST \u002Fv1\u002Fnote\u002Fdocuments\u002F{id}\u002Ffiles` (up to 40 MB),\n`POST \u002Fv1\u002Fchat\u002Fcompletions` (up to 30 MB) and `POST \u002Fv1\u002Faudio\u002Ftranscriptions` (audio up to 25 MB)\naccepted any number of concurrent large bodies. Several parallel uploads at the ceiling could drop\nrequests without a response — other requests in flight at that moment included.\n\nOne more observable difference: a request with a missing or invalid key on these routes could get\n`400` (unparsed JSON), `413` (size) or `415` (content type) — an answer about the body before the\nanswer about the key.\n\n**After**\n\nConcurrent bodies over 1 MiB are bounded by their total size. A request over the bound gets `429`\nwith code `LARGE_BODY_BACKEND_BUSY` and a `Retry-After: 5` header, emitted before any Bitrix24\ncall, so it carries no side effects — and identically whether or not `Content-Length` is\ndeclared:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"LARGE_BODY_BACKEND_BUSY\",\n    \"message\": \"Too many large request bodies are being processed. Retry in a few seconds.\",\n    \"retryAfter\": 5\n  }\n}\n```\n\nOn the OpenAI-compatible `POST \u002Fv1\u002Fchat\u002Fcompletions`, `POST \u002Fv1\u002Fai\u002Fchat\u002Fcompletions` and\n`POST \u002Fv1\u002Faudio\u002Ftranscriptions` (+ `POST \u002Fv1\u002Fai\u002Faudio\u002Ftranscriptions`) the envelope matches the\nrest of that surface — no `success`, lowercase code:\n\n```json\n{\n  \"error\": {\n    \"message\": \"Too many large request bodies are being processed. Retry in a few seconds.\",\n    \"type\": \"rate_limit_exceeded\",\n    \"code\": \"large_body_backend_busy\",\n    \"retryAfter\": 5\n  }\n}\n```\n\nA request with a missing or invalid key now gets `401` on all of these routes — the key is checked\nbefore the body is read. Successful requests and bodies under 1 MiB are unaffected; a retry after\n5 seconds succeeds.\n\n**Important:** on transcriptions the bound is counted from the declared `Content-Length`. An upload without one\n(`Transfer-Encoding: chunked`) is not counted against the shared total and is bounded only by its\nown 25 MB per-file ceiling.\n\n### FIX-0901-10: creating a Hermes agent no longer fails because of the startup payload size\n\n**Before**\n\nCreating a Hermes agent failed right after the server step. The virtual machine was never created,\nthe agent stayed in the error state with an incident code, and the server field on the card stayed\nempty. Retrying produced the same result. The cause was the cloud startup payload growing past the\nsize limit the cloud allows for such payloads.\n\n**After**\n\nInternal comments are no longer shipped to the cloud together with the install script, so the\nstartup payload fits well within the limit again and the virtual machine is created. The install\nscript itself and every other delivery path for it are unchanged. In addition, a size check now\nruns before the cloud is called: should the payload ever grow past the limit again, no cloud\nrequest is sent at all and Vibecode returns the usual provider error with an incident code.\n\n### NEW-0901-11: the business-process editor is available through the API\n\nA new `\u002Fv1\u002Fworkflow-designer\u002F*` section works with templates of the new business-process editor in the editor's own language: blocks, connections, a catalog of block types, graph validation, drafts and publication. It is not the same as `\u002Fv1\u002Fbizproc-templates`, which reads and writes template rows with the graph as an opaque blob.\n\nThe `bizprocdesigner` scope is required — a separate one from `bizproc`. Add it to the key and re-issue the key.\n\nWriting takes two steps. `POST \u002Fv1\u002Fworkflow-designer\u002Ftemplates\u002F{id}\u002Fdraft` saves a **draft**: the running business process keeps executing its published version, so a draft can be shown to a person, rewritten or thrown away. `POST \u002Fv1\u002Fworkflow-designer\u002Ftemplates\u002F{id}\u002Fpublish` makes the draft the working version — irreversibly, and it deletes **every** draft of the template, other people's included. Publication requires the `draftFingerprint` from `GET \u002Fv1\u002Fworkflow-designer\u002Ftemplates\u002F{id}`, so that exactly the graph you read and showed the person is the one published; a draft someone rewrote in the meantime is refused instead of quietly replacing it.\n\nStart with `GET \u002Fv1\u002Fworkflow-designer\u002Fcapabilities`: it answers whether the editor is available to this Bitrix24 account and this key, and names the reason when it is not (`DESIGNER_NOT_SUPPORTED` — Bitrix24 is not updated to a version that serves the editor through the API, or the module is absent, `DESIGNER_UNAVAILABLE` — switched off or not in the plan, `SCOPE_DENIED` — the key lacks the scope, `DESIGNER_REQUIRES_WRITE_KEY` — the key is read-only and the operation writes, `DESIGNER_PROBE_FAILED` — the check did not complete and the editor state is unknown). These states cannot be told apart from a refusal of the methods themselves, which is why the check is a call of its own.\n\nA read-only key is served by this section: the graph, the block catalog, the document fields, the instructions from Bitrix24 and `validate` all read. `DESIGNER_REQUIRES_WRITE_KEY` arrives only on the three writing operations — creating a template, saving a draft and publishing one.\n\n**Affected endpoints:** `GET \u002Fv1\u002Fworkflow-designer\u002Fcapabilities`, `POST \u002Fv1\u002Fworkflow-designer\u002Ftemplates`, `GET \u002Fv1\u002Fworkflow-designer\u002Ftemplates\u002F{id}`, `GET \u002Fv1\u002Fworkflow-designer\u002Ftemplates\u002F{id}\u002Fbrief`, `GET \u002Fv1\u002Fworkflow-designer\u002Ftemplates\u002F{id}\u002Fblocks`, `GET \u002Fv1\u002Fworkflow-designer\u002Ftemplates\u002F{id}\u002Fblocks\u002F{blockId}`, `GET \u002Fv1\u002Fworkflow-designer\u002Ftemplates\u002F{id}\u002Fdocument-fields`, `POST \u002Fv1\u002Fworkflow-designer\u002Ftemplates\u002F{id}\u002Fvalidate`, `POST \u002Fv1\u002Fworkflow-designer\u002Ftemplates\u002F{id}\u002Fdraft`, `POST \u002Fv1\u002Fworkflow-designer\u002Ftemplates\u002F{id}\u002Fpublish`\n\n### BC-0901-12: deleting a server now deletes the agent bound to it\n\n> Old format supported until: not provided\n\n**Before**\n\n[DELETE \u002Fv1\u002Finfra\u002Fservers\u002F:id](\u002Fdocs\u002Finfra\u002Fservers\u002Fdelete) deleted the server only. The agent that ran on it stayed alive on the Vibecode platform: its state still read as running, its bot stayed registered in the Bitrix24 account, its key stayed valid, and the agent pointed at a server that no longer existed.\n\n**After**\n\nThe agent bound to the server is deleted together with it. The agent row is marked deleted, the bot is removed from the Bitrix24 account, the agent key is revoked, and the application card is removed from the Bitrix24 catalog. A successful delete still answers HTTP 200 with the body `{\"success\": true}`.\n\nOne new outcome appears. If another request was changing that same agent concurrently, the answer is `409 AGENT_DELETE_CONFLICT` in the usual rejection shape `{\"success\": false, \"error\": {\"code\": \"...\", \"message\": \"...\"}}`. In that case the server is not deleted either: it keeps running and keeps being billed, and the request has to be repeated.\n\n**What integrators should do**\n\nAn integration that expected the agent to survive a server delete and be reused has to be reworked: create the agent again after creating the new server. Treat `AGENT_DELETE_CONFLICT` as retryable — the server is intact, so repeat the same request. Deleting a server with no agent and deleting a server of kind `GALAXY_APP` behave as before.\n",null,{},1794739477623]