[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"docs-changelog\u002F2026-09-08":3,"docs-tabs-changelog\u002F2026-09-08":6},{"content":4,"lastmod":5},"# API changes: September 8, 2026\n\n[← Changelog](\u002Fdocs\u002Fchangelog) · [September 2026](\u002Fdocs\u002Fchangelog\u002F2026-09)\n\n### FIX-0908-1: warehouses no longer substitute a different id, and refuse an unsupported filter out loud\n\n**Before**\n\nA warehouse id in the request path was read leniently: `GET \u002Fv1\u002Fwarehouses\u002F12.5` was not rejected but quietly became `12`, so a read, an update or a delete landed on a different, genuinely existing warehouse and looked like a success. `12abc`, `1e2`, `007`, an id with a space and an id beyond integer precision behaved the same way, and the same lenient reading applied to `productId` and `limit`. The `filter[]` parameter on the warehouse list, on warehouse stock and on the stock totals was not read at all: any condition — an invented field name as well as a real one — was silently ignored and the full list came back. In the API description seven warehouse operations and the aggregation operation declared success as their only outcome, and warehouse deletion was declared as a 200 response although it answered an empty 204.\n\n**After**\n\nA warehouse id, `productId` and `limit` are accepted only in canonical form — digits only, no sign, no leading zero, no fraction, no exponent, and within integer precision. Anything else is refused with 400 `INVALID_PARAMS` before Bitrix24 is called, so a typo in the id can no longer reach someone else's object. A `filter[]` passed to the three endpoints above is refused with 400 `UNSUPPORTED_FILTER` listing the keys received, instead of a full list that looked filtered. Requests with a canonical id and without `filter[]` work as before, and their successful 200 response is unchanged. The API description now carries the real refusal codes (400, 401, 403, 404, 422) for the warehouse operations and for aggregation, and warehouse deletion is declared as 204 — the response it was already sending.\n\n### BC-0908-2: A value of the wrong type in a write field is refused instead of silently corrupting data\n\n> Old format supported until: not provided\n\n**Before**\n\nA field declared as a number in the entity schema accepted any string: `{\"amount\":\"one hundred\"}`\non [POST \u002Fv1\u002Fdeals](\u002Fdocs\u002Fentities\u002Fdeals\u002Fcreate) answered `201` while the amount was stored as\nzero — Bitrix24 casts a non-numeric string to `0`. On update this erased an already-stored\namount: a `PATCH` with an unparsable value answered `200` and zeroed the field. Every other\nnumeric field in the registry behaved the same way (`sort` on catalogs and products), boolean\nfields accepted any word, and the `color` string field on order statuses was silently truncated\nat the database column width.\n\n**After**\n\nSuch a value is refused with `400` and error code `INVALID_PARAMS` before any call reaches\nBitrix24: a numeric field accepts a JSON number or a numeric string using `.` as the decimal\nseparator; a boolean accepts `true`\u002F`false` and the recognized string forms (`\"yes\"`\u002F`\"no\"`,\n`\"y\"`\u002F`\"n\"`, `\"1\"`\u002F`\"0\"`, `\"true\"`\u002F`\"false\"`); a string field with a declared length limit\naccepts a value within that limit. The `priority` and `status` fields on tasks accept only the\nvalues listed in their enumeration.\n\n**What integrators should do**\n\nSend monetary amounts as a number or as a string using a dot: `1234.56`, not `\"1234,56\"` — a\ncomma decimal separator is refused with an explicit error rather than quietly reinterpreted.\nReview places where values arrive from external systems as strings: such a request used to\nanswer with success while the data was lost, and now returns an error naming the field.\n\n### FIX-0908-3: Paging through recent dialogs no longer loses or repeats records\n\n**Before**\n\n[GET \u002Fv1\u002Fchats\u002Frecent](\u002Fdocs\u002Fchats\u002Fdiscovery\u002Frecent) forwarded the page size and offset to\nBitrix24 as they were, where they bound an internal table join rather than the dialog count. A\npage could return fewer records than requested while promising another one; the last record of a\npage arrived with its chat and last-message fields empty; and neighbouring windows overlapped, so\na `offset += size` walk returned some dialogs twice and missed others.\n\n**After**\n\nThe page is read with headroom and sliced on the Vibecode side: a window holds exactly the number\nof fully populated records requested, under-populated rows are not returned, and the has-more flag\nis computed from the window actually served. A window past the end of the list comes back empty and\nno longer promises another page. A request that names no page size now returns 50 records — the\nsize used to be Bitrix24's to choose and is now stated explicitly.\n\nA caveat about deep windows: the overlap protection holds while offset plus page size stays at or\nbelow 190 — the window together with the headroom for under-populated rows has to fit inside one\nBitrix24 page, and that page is 200 records. Past that the offset is forwarded to Bitrix24 and, on very long lists,\nneighbouring windows may overlap again — a limitation of the method itself, not of the platform.\n\n**What integrators should do**\n\nNothing: the requests are unchanged and the response is now honest. If your code de-duplicated\ndialogs by hand, that workaround is no longer needed.\n\n### BC-0908-4: Fields that Bitrix24 assigns itself are now declared read-only\n\n> Old format supported until: not provided\n\n**Before**\n\nFive fields were declared writable and accepted with `200`\u002F`201`, but Bitrix24 never stored\nthem — the value stayed empty, and the response gave no way to tell that apart from a\nsuccessful write: the origin identifiers on sales pipelines, the owner module on\n[document templates](\u002Fdocs\u002Fentities\u002Fdoc-templates\u002Fupdate), and the problem flag with its\nreason on [payments](\u002Fdocs\u002Fentities\u002Fpayments\u002Fupdate) when updating.\n\n**After**\n\nThese fields are declared server-assigned: a write attempt is refused with `400` and error code\n`READONLY_FIELD` before any call reaches Bitrix24, and the field description states plainly that\nthe platform sets the value. The payment problem flag and its reason are still accepted when\ncreating a payment — only the update path, where the value was lost, is closed. The pipeline\norigin identifiers were not declared in the schema at all and were picked up as writable; they\nare declared now and refused.\n\n**What integrators should do**\n\nRemove these fields from an update request body. The platform stamps the template module itself,\nthe pipeline origin identifiers are never persisted, and the payment problem flag should be set\nwhen the payment is created.\n\n### FIX-0908-5: search and aggregate answer with an error on a wrongly typed parameter instead of silent emptiness\n\n**Before**\n\nIn the body of `POST \u002Fv1\u002F{entity}\u002Fsearch`, the `limit` and `select` fields accepted a value of any type. A non-numeric `limit` (for example `\"abc\"`) produced `HTTP 200` with an empty record list and `meta.hasMore: true` at the same time — a client paging while the platform promises more went into an endless loop, receiving neither a record nor an error. A `select` value that was neither a string nor a list of strings turned into a field name such as `\"999\"` or `\"[object Object]\"`, travelled to Bitrix24 and came back as `HTTP 502` with the code `BITRIX_UNAVAILABLE` — the platform reported its own unavailability where the fault was in the client request.\n\nIn the body of `POST \u002Fv1\u002F{entity}\u002Faggregate`, the `groupBy` field was checked only when it arrived as a string or a list of strings. A number, a boolean or an object was dropped silently: the answer came back as `HTTP 200`, without the `groups` key and without a warning — the client asked for a breakdown, received the overall total, and had no way to notice.\n\nThe `meta.total` key of list answers carried a fabricated number on pages beyond the collection, growing together with the offset: on an account holding five storages, `GET \u002Fv1\u002Fstorages?offset=100` answered `total: 100`, and `GET \u002Fv1\u002Fstorages?offset=1000` answered `total: 1000`.\n\n**After**\n\nA wrongly typed `limit` is refused with `INVALID_LIMIT`, a wrongly typed `select` with `INVALID_SELECT_TYPE`, and a wrongly typed `groupBy` with `INVALID_PARAMS`; all three refusals arrive as `HTTP 400` and name the type that came in. A numeric `limit` behaves as before, including the string spelling of a number (`\"50\"`), while `null`, an empty string and an empty list still mean \"parameter not supplied\" and are not errors.\n\nThe `meta.total` key is withheld when the page is empty and the offset is above zero: nothing can vouch for a count there, and the description of the field already warns that the key may be absent at a non-zero offset. Wherever the page is non-empty or the offset is zero, `meta.total` arrives as before, and the response remains HTTP 200.\n\n### FIX-0908-6: task favorites, comments and time entries answer honestly\n\n**Before**\n\nAdding a task to favorites and removing it from them answered `200` with `success: true` even for a task that does not exist: Bitrix24 confirms that action for any identifier while saving nothing. The `404 TASK_NOT_FOUND` promised by the API description never arrived.\n\nThe task comment list ignored `offset` on a request without a filter and with a sort by identifier — the same page came back at any value — and `meta` was counted over raw chat messages. On a task whose slice held only system notifications the answer was `data: []` together with `total: 1` and `hasMore: true`, so a `while (hasMore) offset += limit` walk never finished.\n\nAddressing a checklist item or a time entry that does not exist answered `422 BITRIX_ERROR` carrying the internal Bitrix24 exception text (`TASKS_ERROR_EXCEPTION_#512; …; 512\u002FTE\u002FITEM_NOT_FOUND_OR_NOT_ACCESSIBLE`), and on the time route that text spoke about a checklist.\n\nThe `from` and `to` parameters of `GET \u002Fv1\u002Ftask-time` were not validated: `?from=notadate` silently returned the whole range with status `200`, as if no period had been requested.\n\n**After**\n\nBefore adding to or removing from favorites the service checks that the task exists and is accessible to the key, and answers `404 TASK_NOT_FOUND` when it is not. On a task that exists the response remains `200`. Rate limiting and an authorization failure are not substituted by \"not found\".\n\nThe comment list honours `offset` on every read path, and `meta.hasMore` and `meta.total` are counted over comments rather than raw chat messages: a slice of only system notifications reads further, and `hasMore: false` with `total: 0` means there are no comments.\n\nA missing checklist item and a missing time entry answer `404 NOT_FOUND` — without the internal Bitrix24 text and in terms of the requested resource. The code is declared in the API description for every method that addresses a single record.\n\nAn unparseable `from` or `to` answers `400 INVALID_PARAMS` before Bitrix24 is called; `YYYY-MM-DD` and ISO 8601 are accepted. An empty value still means no filter. The API description of `GET \u002Fv1\u002Ftasks\u002F{taskId}\u002Fcomments` now declares the `limit` and `offset` it actually reads.\n\n### BC-0908-7: Lead conversion stamps backlinks and answers in the standard read format\n\n> Old format supported until: not provided\n\n**Before**\n\n[POST \u002Fv1\u002Fleads\u002F{id}\u002Fconvert](\u002Fdocs\u002Fentities\u002Fleads\u002Fconvert) answered `200`, but the deal, contact\nand company it created carried no link back to the lead they came from: filtering deals by lead\nreturned nothing, and a deal could not be traced to its source. The response itself came in raw\nBitrix24 shape — upper-case underscored keys and stringified numbers — the only such response\namong the routes of this section: the platform read the created records through legacy methods\nwhose field names did not match the schema, so the values bypassed normalization.\n\n**After**\n\nEvery created record gets a link to the source lead, and the lead itself gets links to the\ncontact and company that were created (only to those actually created). The response is read\nthrough the same method as an ordinary read of a deal, contact or company, so its shape matches\n[GET \u002Fv1\u002Fdeals\u002F{id}](\u002Fdocs\u002Fentities\u002Fdeals\u002Fget) and its neighbours — field names in the platform's\nusual style, numbers as numbers.\n\n**What integrators should do**\n\nIf your parsing of the conversion response was written for the raw Bitrix24 shape, switch it to\nthe ordinary read format used by every other route of this section. The backlinks only add data\nand break nothing: filtering deals by lead now finds the created deal.\n\n### BC-0908-8: a payment currency that disagrees with the order is no longer swapped silently\n\n> Old format supported until: not provided\n\n**Before**\n\n[Creating a payment](\u002Fdocs\u002Fentities\u002Fpayments\u002Fcreate) accepted any `currency` with `201`, while\nBitrix24 stored the payment in the currency of its order — the requested one was dropped\nwithout a word. The field description meanwhile promised a free choice with a pointer to\n`GET \u002Fv1\u002Fcurrencies`, so the discrepancy only surfaced during reconciliation.\n\n**After**\n\nWhen `currency` is passed and does not match the currency of order `orderId`, the request is\nrefused with `409` and error code `CURRENCY_MISMATCH` before any call reaches Bitrix24; the\nmessage names both currencies and the order. A matching currency is still accepted, so a\npayment read and sent back whole keeps working. The same rule applies to\n[batch](\u002Fdocs\u002Fbatch) payment creation. When the order currency cannot be read, the request is\nlet through rather than refused.\n\n**What integrators should do**\n\nEither omit `currency` entirely to inherit the order currency, or send exactly the one the\norder carries: `GET \u002Fv1\u002Forders\u002F:id` shows it.\n\n### NEW-0908-9: the standalone server auto-sleep policy is now visible through the API\n\n[POST \u002Fv1\u002Finfra\u002Fservers](\u002Fdocs\u002Finfra\u002Fservers\u002Fcreate) returns the saved `data.sleepAfterMinutes` for every successful create, reuse, and idempotency replay. This is a response field, not a new create parameter. A successful standalone deploy returns the same policy snapshot in `data.sleepAfterMinutes`, while SSE returns it as `sleepAfterMinutes` in the `done` event. For a regular standalone server that is not covered by agent\u002Fbot idle-sleep protection, a numeric timeout with no enabled recurring wake windows appends an entry starting with `AUTO-SLEEP POLICY:` to `warnings[]` with two choices: a wake window for a task that finishes before the timeout, or a deliberate always-on mode. A window only wakes the server and does not keep a background process running. The existing successful `slept: false`, `reason: \"WAKE_IMMINENT\"` outcome of [POST \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fsleep-now](\u002Fdocs\u002Finfra\u002Flifecycle\u002Fsleep-now) is now documented for an imminent wake window. The HTTP 200 response itself is unchanged.\n\n### BC-0908-10: writing an amount to a smart-process type without product rows no longer answers with a false success\n\n> Old format supported until: not provided\n\n**Before**\n\n`POST \u002Fv1\u002Fitems\u002F{entityTypeId}` and `PATCH \u002Fv1\u002Fitems\u002F{entityTypeId}\u002F{id}` accepted `opportunity` and `isManualOpportunity` on any smart-process type and answered `201`\u002F`200`. When the type has product rows disabled (`isLinkWithProductsEnabled: false`), Bitrix24 does not store those fields — the amount was silently lost while the caller saw success and moved on.\n\n**After**\n\nThe platform compares what was requested against the re-read record — it already re-read it before responding, so no additional Bitrix24 calls were added. When the request EXPLICITLY asked for manual mode (`isManualOpportunity: true`) and did not get it, the answer is `422 AMOUNT_NOT_APPLIED`: `error.details.unappliedFields` lists the fields that were not applied, and `data` carries the actual state of the record that was already created or updated. An amount sent WITHOUT that flag is not checked: from the response it is indistinguishable from a legitimate recalculation from the product rows, which works as documented — so those requests answer exactly as before. The check does not touch deals, leads, invoices or quotes — those always have product rows.\n\n### FIX-0908-11: the host from galaxyId is now available for reading\n\n**Before**\n\nThe Galaxy application creation response already contained `galaxyId`, but [GET \u002Fv1\u002Finfra\u002Fservers](\u002Fdocs\u002Finfra\u002Fservers\u002Flist) did not show that shared host, and [GET \u002Fv1\u002Finfra\u002Fservers\u002F:id](\u002Fdocs\u002Finfra\u002Fservers\u002Fget) answered `404 NOT_FOUND` to the same API key.\n\n**After**\n\nBoth GET operations return a limited host projection with `access.via: \"galaxy-reference\"`. It contains only safe read fields. Host management and application operations still require a resource managed by the current key.\n\n**Impact on integrators**\n\nRequests require no changes. A client using the newly visible shared host should check `access.via`: `galaxy-reference` means read-only access.\n\n### NEW-0908-12: key issuance now says whether the key got access to Bitrix24 data\n\n`POST \u002Fv1\u002Fconnect\u002Ftoken` now returns an optional `b24_credentials` field, and\n`GET \u002Fv1\u002Fcowork\u002Fme` the same object as `b24Credentials`. It answers whether the issued key can\nread Bitrix24 account data: `ready: true` — the rights work, `ready: false` — they do not, and\na `reason` from a closed set comes with it (`WEBHOOK_NOT_CONFIGURED`, `WEBHOOK_MINT_FAILED`,\n`WEBHOOK_MINT_REFUSED_BY_PORTAL`, `INT_TARIFF_REQUIRED`, `VIBE_SCOPES_ONLY`), plus an\n`upgradeUrl` for the reason that names a paid plan. The `401 TOKEN_MISSING` body already\ncarries the same set, so a client parses it with one branch of code.\n\nWhy it exists. A key is issued even when the account refuses to connect it to its own data:\nthe key still works for AI calls, while every request for account data answers\n`401 TOKEN_MISSING`. Until now a client only found that out by hitting it, and could not tell\na missing right from a network failure. The state now arrives together with the key and can be\nre-read at any time from `GET \u002Fv1\u002Fcowork\u002Fme`.\n\nThe field is additive: the other fields and the response status are unchanged, and requests\nthat ignore it behave exactly as before. It is absent where the question does not apply — for\nexample an app key, whose account tokens are stored separately.\n\n### FIX-0908-13: workday history is returned while a day is still open\n\n**Before**\n\n`GET \u002Fv1\u002Fworkday\u002Frecords` answered `502 BITRIX_UNAVAILABLE` whenever the page contained a\nrecord of a workday that was not finished yet. Such a record carries no end time, no duration\nand no approval flag, and the endpoint required those fields to be filled in, rejecting the\nwhole page — including the closed days that arrived in the same response. In practice every\nemployee currently at work got an error.\n\n**After**\n\nThe record of an unfinished day is returned as is, together with the rest of the page: the\nHTTP 200 response is unchanged and the closing fields arrive exactly as Bitrix24 sent them.\nThe checks that keep a wrong answer from passing silently are untouched: the page is still\nrejected when a record belongs to a different employee or carries no valid start time, which\nis what `tzOffset` is derived from. No client action is required.\n\n### NEW-0908-14: agents are available for Open Channel binding\n\nAdded `GET \u002Fv1\u002Fagents`: the method returns agents owned by the API-key owner and the `bitrixBotId` value accepted by `welcomeBotId` in an Open Channel configuration. `agentId` is not used for a Hermes agent; `queue` continues to contain human operator IDs.\n\n### NEW-0908-15: department include in user records\n\nThe `include=department` parameter adds an `_included.departments` array to a user record with the full records of every department referenced by `departmentId`. A user without a department returns an empty array. The request requires the `user` and `department` scopes.\n\n**Affected endpoints:** [GET \u002Fv1\u002Fusers](\u002Fdocs\u002Fentities\u002Fusers\u002Flist), [GET \u002Fv1\u002Fusers\u002F:id](\u002Fdocs\u002Fentities\u002Fusers\u002Fget), [POST \u002Fv1\u002Fusers\u002Fsearch](\u002Fdocs\u002Fentities\u002Fusers\u002Fsearch)\n\n### NEW-0908-16: labels and descriptions for every basic field\n\nIn `GET \u002Fv1\u002F{entity}\u002Ffields` responses, every declared basic field now has a localized label and description: Russian for Russian-language Bitrix24 accounts and English for international ones.\n\n### NEW-0908-17: protection against writing over newer sources\n\n`POST \u002Fv1\u002Finfra\u002Fservers\u002F{id}\u002Fsources` accepts an optional `X-Parent-Version: v\u003CN>` header —\n\"this is the version I edited\". If the latest version has moved on by the time the write\nlands, it is refused with `SOURCE_VERSION_CONFLICT` (409) and\n`error.details.latestVersionId` names the current one (`null` when the server has no live\nversions at all). A malformed header value is `400 INVALID_VERSION_ID`. Without the header the\nendpoint behaves exactly as before.\n\n\"Current\" here means the newest LIVE version — the same one that heads the version list and can\nbe downloaded, so the version named in the refusal can always be fetched and the write retried.\nDeleted versions do not count, though their numbers are never reused. Deduplication is no\nexception: an archive byte-identical to an existing version is refused with `409` too if the\nhead has moved on.\n\nThe guarantee runs in a single direction: your write will not land on top of a newer version.\nThe reverse half does not exist — sources saved from the browser come from the deploy\nauto-save, which has no parent to declare, so a later browser deploy can still save over\nyours. The refusal is decided in the same place the version is created, so two uploads\ndeclaring the same parent cannot both win, and the refused archive does not stay in storage.\n\nThe `POST \u002Fv1\u002Fcowork\u002Fdeploy-key` response gained two fields about the move onto the fresh key:\n`repointTruncated` — not everything moved, the remainder goes on the next call, and\n`applicationSlotBlocked` — an application card stayed on its previous key because the fresh\nkey's slot is taken by another card. Previously a partial outcome was visible only in the\nplatform's own journal.\n\n**Affected endpoints:** `POST \u002Fv1\u002Finfra\u002Fservers\u002F{id}\u002Fsources`, `POST \u002Fv1\u002Fcowork\u002Fdeploy-key`\n\n### NEW-0908-18: replace an application key in one call, and see its mask on the card\n\nAn application can be added to Cowork from the catalog — created earlier, on another\nmachine, or before the Code tab existed. Such an application has no raw key and none can be\nrecovered: the platform stores only a hash. There was nothing to publish with.\n\nThe new `POST \u002Fv1\u002Fcowork\u002Fapplications\u002F{id}\u002Fkey` brings an\napplication key into working order in one call. An empty personal-key slot gets a key issued,\nan occupied one gets it replaced; `issued` (`minted` or `rotated`) says which happened. The raw\nkey is returned once, as it is on application creation. `Idempotency-Key` is required, and a\nreplay answers `rawApiKey: null` with `KEY_NOT_REPLAYABLE`. If the request failed AFTER a key was issued\n(a 500 naming `orphanKeyId`), that Idempotency-Key is spent for good: repeating it answers\n`IDEMPOTENCY_KEY_ALREADY_USED` rather than minting a second key. Read the card, then retry\nwith a NEW Idempotency-Key. One case apart: when the key was issued but the card could\nnot be read afterwards, the answer is still `201` with the secret, `application` arrives null,\nand `warningCodes` carries `APPLICATION_CARD_UNAVAILABLE`. Read the card with\n`GET \u002Fv1\u002Fapplications\u002F{id}` — there is no reason to lose the only copy of the secret over it.\n\nThe previous key is not revoked immediately: it gets a one-day expiry, so a publish already in\nflight finishes. `previousKey.graceUntil` names the moment it stops authenticating. The request\nbody accepts `syncServerEnv: true` — the platform then swaps the key in the deployed server's\nenvironment and restarts the application, and `envSync` reports the outcome: updated, no such\nline in the file, server asleep, restart failed, and so on. Without it the application keeps\nthe old key and starts getting refusals a day later.\n\nRefusals arrive as distinct codes rather than a generic `400`: `NOT_APPLICATION_OWNER` — the\napplication belongs to someone else; `KEY_ROTATE_OAUTH_APP_KEY`, `KEY_ROTATE_SYSTEM_KEY`,\n`KEY_ROTATE_LIVE_OAUTH_GRANT`, `KEY_ROTATE_NOT_ACTIVE` — the key in the slot cannot be\nreplaced, the code says why; `KEY_ROTATE_KEY_VANISHED` — the key was removed while the request\nwas in flight; `KEY_LIMIT_REACHED` — the key quota is spent;\n`APPLICATION_KEY_REPLACE_IN_PROGRESS` — a replacement for this application is already running.\nOne replacement per application at a time, whatever Idempotency-Key the second request carries:\notherwise both would issue a key and the slot would go to whichever finished last. A replacement\nabandoned by a crashed request stops blocking after two minutes.\n\nOn an empty slot the issue goes through the platform-wide access gate, like every other\nkey-issuing door: an account without access gets a `402`. A rotation deliberately does not —\nit carries the previous key's rights forward and creates none, and an owner whose access has\nlapsed must still be able to wind their affairs down.\n\nThe application card gained a `key` block: `slot` (`auth` or `api` — which key is shown),\n`present`, `prefix`, `suffix`, `status`, `expiresAt`, `lastUsedAt`, `rotatable` and\n`rotateBlockedReason` (whether replacement works and why not), `affectsShownKey` (whether it\ntouches the very key shown). The mask is assembled client-side from `prefix` and `suffix`.\nThe `rotatable` flag reports what the platform ALREADY\nknows, and neither side of it is a guarantee: `false` means \"a refusal is known, and here is\nits reason\" (`rotateBlockedReason`, which covers access as well as key state), `true` means \"no\nknown refusal\". Driving the button off it is convenient — grey it out on `false` and show the\nreason — but do not turn `false` into a dead end: the refusal may already be gone while the\ncard has yet to learn of it (on the lag and on the two gates outside the flag, see below).\nJudge by the door's answer, not by the flag alone. The block is ALWAYS there; someone the application was merely shared with\nreceives empty fields (`present: false`) and `rotatable: false` with reason `NOT_MANAGER` —\nwhich means \"not for you\", not \"there is no key\". On an EMPTY slot, where the call would issue a key, the flag also covers the issuing\ngates: `ISSUANCE_BLOCKED` (the account has no platform access), `INFRA_DISABLED`\n(infrastructure is switched off), `KEY_QUOTA_REACHED` (the key quota is spent) and\n`READONLY_POLICY` (the account issues read-only keys only). About the CALLING key the field\nspeaks the door's own codes, and they repeat on EVERY card of the response: `INSUFFICIENT_SCOPE`\n(the key does not carry the `vibe:cowork` scope) and `COWORK_HARNESS_KEY_FORBIDDEN` (the key was\nissued for an external agent). Accuracy is one-sided by design: `false` on an empty slot is read\noff the access state the platform already knows, so an account whose Bitrix24 commercial plan\nchanged seconds ago may still show `false` until that state refreshes. TWO Cowork gates stay\nOUTSIDE the field and can still refuse a call made on a `true`: the platform-wide kill switch\n(503 `COWORK_FEATURE_DISABLED`) and the state of your Cowork seat (403\n`COWORK_NOT_ACTIVATED`, which `GET \u002Fv1\u002Fcowork\u002Fstate` already reports). Neither is a fact about the\ncard itself, so neither is repeated per card — handle the door's refusal instead of reading `true`\nas a guarantee.\n\n**Affected endpoints:** `POST \u002Fv1\u002Fcowork\u002Fapplications\u002F{id}\u002Fkey`,\n[GET \u002Fv1\u002Fapplications](\u002Fdocs\u002Fapplications), `GET \u002Fv1\u002Fapplications\u002F{id}`\n\n### NEW-0908-19: read the sprint list, active sprint, and a sprint by ID\n\nThe Vibecode API adds [GET \u002Fv1\u002Fscrum\u002Fsprints](\u002Fdocs\u002Fscrum\u002Fsprints\u002Flist) for reading visible sprints, [GET \u002Fv1\u002Fscrum\u002Fsprints\u002Factive](\u002Fdocs\u002Fscrum\u002Fsprints\u002Factive) for reading a project's current sprint, and [GET \u002Fv1\u002Fscrum\u002Fsprints\u002F:id](\u002Fdocs\u002Fscrum\u002Fsprints\u002Fget) for reading one sprint by ID. The response contains its name, dates, status, and project link. If no sprint is active, the active-sprint endpoint returns `data: null`.\n\n### BC-0908-20: a paused Cowork seat no longer opens Bitrix24 past the account plan\n\n> Old format supported until: not provided\n\n**Before**\n\nA Cowork desktop key and an agent key opened the Bitrix24 REST API on an account whose plan does not carry REST access, regardless of whether the seat itself was in force. A paused seat kept that ability until its key was revoked — about a month.\n\n**After**\n\nThe ability follows the state of the seat. The bypass applies only to a seat in the `ACTIVE` state, including a seat with a scheduled cancellation, until the end of its paid term. For a paused, cancelled or parked seat the key is served by the ordinary account rule: where the Bitrix24 plan already carries REST access nothing changes, and on a plan without it the calls to Bitrix24 get the same account refusal as any other application.\n\n**What integrators should do**\n\nResume the Cowork subscription, or move the account onto a paid Bitrix24 plan. The current state of the seat arrives in the `subscription.state` field of the [GET \u002Fv1\u002Fcowork\u002Fstate](\u002Fdocs\u002Fcowork\u002Fstate) response.\n\n### FIX-0908-22: the sources registry no longer advertises a door that answers 403 to the calling key\n\n**Before**\n\n[GET \u002Fv1\u002Fme\u002Fsources](\u002Fdocs\u002Fsource-storage\u002Fregistry) derived `reachableViaApi` from ROW\nreachability alone — \"is the server alive, is the app not deleted\" — and never looked at the KEY\nthat called. The doors its pointers lead to are key-scoped and reject an OAuth application key\nwhenever the server belongs to someone else.\n\nIn practice: the listing enumerates snapshot owners per USER, not per key, so calling with\nyour own application's key returned your servers created with PERSONAL keys carrying\n`reachableViaApi: true` and a working-looking `listEndpoint` — while\n[GET \u002Fv1\u002Finfra\u002Fservers\u002F{id}\u002Fsources](\u002Fdocs\u002Fsource-storage\u002Fservers) answered that very key\n`403 NOT_AUTHORIZED` at that very address.\n\nThe same lie appeared on application rows: the author of applications A and B, calling with the\nkey of B, saw the row of A with a working pointer whose door answers\n`403 SOURCE_APP_ID_MISMATCH`.\n\n**Now**\n\n`reachableViaApi` answers one question: will the CALLING KEY be admitted through the drill-in. A\nrow whose door that key will not open carries `reachableViaApi: false` with `listEndpoint` and\n`latestDownloadEndpoint` set to `null`. Both `kind` branches of the listing (`server` and\n`legacy-app`) compute it with the same predicate the door itself uses.\n\nThe row itself does NOT disappear from the listing: a false promise was withdrawn, not visibility.\nThe owner still sees that snapshots exist and whose they are via `user.id`.\n\n**What integrators should do**\n\nCheck `reachableViaApi` before following `listEndpoint` or `latestDownloadEndpoint`. Both fields\nwere already declared `string | null` and already arrived empty for an orphaned server and a\nsoft-deleted application, and `reachableViaApi: false` was already a documented value for those\nsame two cases — so a client that honoured the contract needs no change. If you get `false` where\nyou expected `true`, you called with an application key: repeat the call with a personal key\n(your own or the server owner's) or with a Bitrix24 account administrator's key. The same holds for an\napplication row — an application key opens only its own application.\n\n**What this does NOT change**\n\nAccess. No door started or stopped admitting anyone: the access rule is unchanged, only the\nlisting stopped promising access that was never there. Collapsing the doors of one server onto a\nsingle ownership model is a separate breaking change and ships as its own entry — and so do the\npublish and deploy refusal hints, because there the same address is withdrawn TOGETHER with the\n`hint.requiredAction` field, which is a documented-field removal.\n",null,{},1794739478560]