# API changes: August 13, 2026

[← Changelog](/docs/changelog) · [August 2026](/docs/changelog/2026-08)

### FIX-0813-1: a failed native-module build now states its own cause instead of "Python not found"

**Before**

A Node app with a dependency shipping compiled code (`better-sqlite3`, for one) deployed only
sometimes. Such dependencies install in two steps: first a prebuilt file is downloaded, and if that
fails the module is compiled from source on the spot. When the download broke on the external
network, the second step began and failed — the app image carries no build tools. The deploy
response and the app card then showed the last line of that second step,
`Could not find any Python installation to use`, which pointed away from the real cause: the
customer went looking for a bug in their own code and in their Python version, while neither was
broken. The failure category came out generic — `INSTALL_FAILED`, with a hint about a missing
compiler.

**After**

This failure has its own category, `NATIVE_PREBUILD_UNAVAILABLE`, delivered in the `category` field
of the 502 `GALAXY_APP_BUILD_FAILED` response and in the app card's `buildHint`. The hint says what
is true: the prebuilt artefact could not be downloaded, the external source did not respond, this
is temporary, retry the deploy in a few minutes. The category is assigned only when both pieces of
evidence appear in the build log — the download broke on the network AND there was nothing to build
from source with; failures a retry cannot cure (no prebuilt artefacts are published for this
platform, the host ran out of disk) stay out of it and keep their previous text. The client retries
the deploy — the platform does not re-send the build on its own.

### FIX-0813-2: API response shapes are described in the machine-readable schema

**Before**

The machine-readable schema did not show the actual response shapes for [POST /v1/duplicates/find](/docs/duplicates) and [GET /v1/lists/{iblockId}/elements](/docs/lists/elements). This made it harder to handle duplicate-search results and Bitrix24-native list-element fields.

**After**

The schema describes an entity-type keyed object or an empty array for duplicate search, and a list-element array in its envelope with dynamic property keys and a `meta.total` counter, without changing the actual API response.

**Impact for integrators**

Integrations can use the machine-readable schema to select the response shape. For list-element requests, account for the `limit` parameter being ignored.

### NEW-0813-3: the real state of a sleeping galaxy app in the server GET and in the logs answer

`GET /v1/infra/servers/:id` now returns a `reachability` block for a galaxy app, answering the question "can the app respond right now". The `status` field is unchanged — it is the state of the app record, and during a wake it lags behind the machine: the record stays `sleeping` while the platform brings the galaxy up, which takes minutes on a cold machine. Beside it, `reachability` carries the summary `effectiveStatus`, the carrying galaxy state `hostStatus` and `hostTunnel`, the live-read `container` and `forwarder`, the check outcome `probe` and its time `probedAt`. The check runs only against a galaxy that is up and never wakes a sleeping one, so for a sleeping galaxy `container` and `forwarder` arrive as `unknown`. For every other server type the block is `null`.

The `GET /v1/infra/servers/:id/logs` answer for an app on a sleeping galaxy gained a `recovery` field and names the path that works: the wake call, the `logsPreserved` marker (the wake starts the same container, so lines written before the sleep stay in the log), the cold-galaxy boot budget in seconds, what to check readiness with, and whether a recurring wake window is available for this app. The `hint` field is still a string and now names the wake call as well.

The states and the order of actions — [Galaxy app sleep and wake](/docs/infra/galaxy-sleep).

### FIX-0813-4: file download declares its required scope in openapi.json

**Before**

The `GET /v1/files/{fileId}/download` operation carried no `x-required-scope` field in the machine-readable spec, although the runtime answers `403 SCOPE_DENIED` without a scope. A client or agent deriving key scopes from the spec read it as "no scope needed" and was refused on the first call. The same operation also appeared in every `openapi.json?scope=` slice, including slices of other modules.

**After**

The operation declares `x-required-scope: disk`, its primary scope. The handler also accepts `crm`: a key holding only that scope downloads a file attached to a CRM file-type user field. The second scope is declared in a new optional `x-alternative-scopes` field next to the primary one. In slices the operation is kept for `openapi.json?scope=disk` and `?scope=crm`, and is gone from the rest.

**Integrator impact**

A client reading only `x-required-scope` will request `disk` and keep working as before. A client holding a crm-only key can now see from the spec and from the operation page that the call is available to it — previously that was stated only by the refusal message and the key self-description in `/v1/me`.

### FIX-0813-5: A readable build error instead of raw builder output and external addresses

**Before**

When a build failed because the base-image registry was unreachable, `provisionError` and the 502 body carried the builder output verbatim: the external registry name, the request path and a public IP address. Other build failures could also carry external links and public IP addresses in their text.

**After**

An unreachable image registry is now described by a fixed sentence: the cause plus the action — re-send the same deploy in a few minutes, the slot and its data are untouched. The failure category (`provisionErrorCategory`) and the retryable flag are unchanged. For every other build failure the cause is still passed through as-is, but external addresses in it are replaced with markers — full links, a scheme-less image-registry name where the line itself is about the image, an address named with no path at all in a network-failure line (`getaddrinfo ENOTFOUND …`, "could not resolve host"), and public IP addresses. First-party platform addresses, local and internal ones stay visible, and so do the image name with its tag, file names and package names: the marker goes where the text itself calls the token an address — so a package named `socket.io` stays readable even though its shape is identical to a registry's. One exception: a four-part numeric run (`11.0.16.1`) is indistinguishable from an IP address, so a version of that shape becomes a marker too.

**Impact on integrators**

Nothing to change: error codes, categories and the retryable flag are the same. A client that parsed `provisionError` by substring now gets a stable sentence for this failure class instead of changing builder output, and the full build log is still available in the `buildLog` field of the same response.

### NEW-0813-6: the agent bundle manifest now reports the runtime verification mode

Two optional fields were added to the [GET /v1/agent-bundles/:kind/manifest.json](/docs/agents) response.

`runtime_integrity` is either `"on"` or `"off"` and tells the agent whether to verify the files of its environment against the checksums recorded at install time. The agent verifies only on `"on"`; an absent field means verification is disabled.

`runtime_integrity_budget` is an integer that the platform currently always sends as one. The agent reads it as permission: above zero means recovery is allowed, zero forbids it. The agent caps the attempt rate on its own at one per hour, so values above one do not change behaviour. Recovery is governed by a platform setting rather than by this field; the field exists so that an agent receiving no value performs no recovery at all.

Existing requests are unaffected: the fields are optional, and the remaining manifest fields and the archive checksum are unchanged. Clients reading the manifest need to do nothing.

### NEW-0813-7: the agent bundle manifest gained a targeted runtime repair operation

The `operations` object of [GET /v1/agent-bundles/:kind/manifest.json](/docs/agents) now carries a `repair_runtime` key.

The operation reinstalls the agent runtime in place when the files of its environment have diverged from the hashes recorded at install time. Previously such a divergence could only be cleared by recreating the whole application.

Its `available_to` field holds the single value `admin`: the operation is rare and manually triggered, so a call made on behalf of scheduled jobs is rejected with `OPERATION_FORBIDDEN`. Success requires more than a zero exit code — the agent must also confirm that the environment now agrees; otherwise the operation answers `status: "failed"` with the reason in the `error` field.

Existing requests are unaffected: the other operations, the manifest fields and the archive checksum are unchanged. Clients reading the manifest need to do nothing.

### FIX-0813-8: The reauth refusal no longer advises OAuth to keys that have none

**Before**

[POST /v1/bots/{botId}/reauth](/docs/bots/management/reauth) answered `410 REAUTH_REQUIRED` with one and the same advice on any dead credential — re-run authorization through `POST /v1/oauth/authorize`, or recreate the personal key. A key that reaches Bitrix24 through an inbound webhook has no way to follow it: there is no authorization flow and no refresh token behind such a key. Its owner read the instruction and hit a dead end.

**After**

The refusal text now depends on how the key authorizes, and `error.details` carries a new `credentialKind` field — `oauth`, `webhook` or `unknown`. For a webhook-backed key the refusal names the real cause (the webhook is dead, most often deleted on the Bitrix24 side) and the real remedy — re-minting the webhook, which preserves the key id and string so linked bots keep working. For an OAuth-backed key the text is unchanged.

**Impact on integrators**

Nothing to change: the response code and its status are the same. A client that parsed the refusal text as a string will see new wording for webhook-backed keys — branching on `error.details.credentialKind` is the sturdier option.

### FIX-0813-9: deleting a galaxy app on a host that cannot be woken now answers a terminal 409

**Before**

`DELETE /v1/infra/servers/{id}` for an app on a galaxy host that cannot be woken (frozen
balance or a wake block) answered `502 GALAXY_HOST_UNREACHABLE` with an `error.hint` object
and advice to retry later. That advice never worked: the wake was refused, not failed, so
clients kept retrying until their own timeout.

**After**

The refusal is terminal and arrives as `409 GALAXY_HOST_WAKE_BLOCKED`. The body carries
`error.reason` with the cause: `BILLING_FROZEN`, `ACCESS_EXPIRED`, `STOPPED` or `UNKNOWN`.
The `error.hint` object is gone on this path — it described recovering an unreachable host,
and there is nothing to recover here; for genuine unreachability the `502` with `hint` stays
unchanged. Retrying is pointless — the cause has to be cleared.

The `409 GALAXY_HAS_APPS` text for deleting the host itself was corrected too: deleting a
galaxy together with its apps is available in the dashboard, and the public API has no such
operation.

### NEW-0813-10: the Marketplace trial can be started from a Cowork/Code desktop key

A new endpoint [POST /v1/cowork/activate-market-trial](/docs/cowork/activate-market-trial) starts the one-time Marketplace trial for the Bitrix24 account the calling Cowork/Code desktop key is bound to. Until now the programmatic path existed only for personal keys and application keys — the desktop key was rejected by `POST /v1/portals/{id}/activate-market-trial`, and that refusal stays in place.

There is no path parameter: the account comes from the key, so a client never needs an internal identifier. The body is required — `{"acknowledgedOneTimeConsumption": true}`, the literal `true` and nothing else. With it the client confirms that the user was shown that a one-time, non-revocable trial is being started, and was told when it ends. The confirmation is recorded in the account's audit trail.

A successful answer is `{"success": true, "data": {"status": "activated", "trialEndsAt": "…"}}`; instead of `activated` you may receive `already_active` (the trial or the paid access is already in force) or `pending` — the activation went through and Bitrix24 has not confirmed it yet, and in that case the request must not be repeated. The length is set by Bitrix24, so show the user the date from `trialEndsAt` rather than a number of days of your own.

Refusals: `400 DISCLOSURE_REQUIRED` (no confirmation), `403 INSUFFICIENT_SCOPE` (the key has no `vibe:cowork` right), `403 COWORK_DESKTOP_KEY_REQUIRED` (a key of another class — an agent seat key carrying the same right, for example), `403 WRITE_BLOCKED_READONLY_KEY`, `409 ALREADY_ACTIVATED`, `409 TRIAL_ACTIVATION_UNAVAILABLE`, `503 TRIAL_ACTIVATION_RETRY`. The ceiling is 3 requests per hour per account.

Before showing the activation step, read `activation.marketTrial.available` on [GET /v1/cowork/state](/docs/cowork/state). It is a forecast: `false` is final, `true` means offering is fine but does not promise success, so keep handling a refusal at activation time. In regions on the tariff access model this trial does not exist as a product: the pre-check answers `region_not_supported` and the call itself refuses with `409 TRIAL_ACTIVATION_UNAVAILABLE`.

### FIX-0813-11: transient errors no longer close the trial for good

**Before**

Every account carried a counter of failed trial activations, and it advanced on ANY error — including the ones Bitrix24 never ruled on: the request did not reach it, it answered with an internal error, or our own authorization failed. Once the counter reached its ceiling the account got `409 TRIAL_ACTIVATION_UNAVAILABLE` on every further request, with nothing to bring it back. Meanwhile `503 TRIAL_ACTIVATION_RETRY` invited a retry — so the advice led straight into the trap where a run of network failures cost the account its one-time trial.

**After**

Only Bitrix24 verdicts about the account itself spend the counter. A transport failure, an internal error on the Bitrix24 side and a problem with our authorization no longer cost an attempt — retrying after `503 TRIAL_ACTIVATION_RETRY` is now as safe as the response says.

**Impact on integrators**

Nothing to change. Retry after a `503` exactly as before, except that it genuinely no longer moves the account closer to a refusal. `409 TRIAL_ACTIVATION_UNAVAILABLE` stays final and keeps its meaning: a Bitrix24 verdict about the account itself.

### FIX-0813-12: activating the trial again allows three attempts per hour

**Before**

[POST /v1/portals/{id}/activate-market-trial](/docs/activate-market-trial) promised three requests per hour per account in its description but delivered one: the `x-ratelimit-limit` header came back as `1`, and a second request within the hour got `429 RATE_LIMITED` with a `retry-after` of about an hour. For a user whose activation failed on a transient error, a "Try again" button stayed useless for the rest of the hour.

**After**

An account gets the three attempts per hour the description promises. The `x-ratelimit-limit` value matches what is documented, and `429` arrives on the fourth request.

**Impact on integrators**

Nothing to change. If you hard-coded an hour-long pause after the first `429`, you can go back to honouring the `retry-after` header.

### NEW-0813-13: new access refusal code INT_VIBE_PLUS_REQUIRED

The code reference gains `INT_VIBE_PLUS_REQUIRED` (HTTP 402). On the international surface it arrives where `INT_TARIFF_REQUIRED` arrives — on infrastructure creation and wake and on key issuance — and means the Bitrix24 account needs a Vibe+ plan. The addition is additive: existing codes and the response shape are unchanged, so treat an unknown code as a denial and branch on `error.code` rather than on the message text. Code breakdown — in [Errors](/docs/errors).

### NEW-0813-14: Cowork/Code state now reports the paid term

The [GET /v1/cowork/state](/docs/cowork/state) response gained two fields inside the `subscription` object. `termMonths` is how many months the seat is paid for at once (`1`, `3`, `6` or `12`; `1` for a monthly seat). `paidThroughAt` is the date the seat is paid through, in ISO 8601.

This is not the same as `currentPeriodEnd`. The billing period is the quota window and it rolls every 30 days regardless of the term bought. On a seat paid for a year `currentPeriodEnd` falls a month from now while `paidThroughAt` falls eleven months from now.

The description of `subscription.pendingTier` is clarified along with it: a tier downgrade takes effect on `paidThroughAt`, not on `currentPeriodEnd`. Before terms existed the two dates always coincided, so the earlier wording was accurate; the coincidence now holds only for monthly seats. The same date is how long access lasts once a subscription is cancelled: cancelling stops the automatic renewal, and the term already paid for is served out in full.

Existing calls keep working unchanged — both fields are additions, nothing was renamed or removed.

### FIX-0813-15: a server rename now shows up in the dashboard

**Before**

[PATCH /v1/infra/servers/:id](/docs/infra/servers/update) changes a server's `displayName` and `description` — the documentation for that endpoint calls them the two texts a person sees in the dashboard and on the application card in the Bitrix24 catalog, and there is no other endpoint for them. But the dashboard's application list and detail rendered neither: they rendered a copy of the server's name and description taken when the application was created. After a rename `GET /v1/infra/servers/:id` and `GET /v1/me/sources` returned the new value while the dashboard kept the old one, with no way to correct it.

**After**

An application with a linked server shows the server's `displayName` in the dashboard. The description comes from the application card when one was set there, otherwise from the server, and a description edited through this endpoint now reaches the application even when it had its own description: the last edit wins.

The name is now one value behind two doors: renaming the application in the dashboard changes the server's `displayName`, and renaming the server updates the card. The Bitrix24 catalog card title is re-published automatically after either edit. A name typed in the dashboard when the application is created now also becomes the new server's `displayName`, not just the card's.

No client action is required. Applications created before this fix follow the server too: the name shows up in the dashboard at once, the description from the first edit through this endpoint onwards, and until then the dashboard keeps the previous text.
