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

API changes: September 4, 2026

← Changelog · September 2026

NEW-0904-2: the Supports BitrixMobile flag at application registration

POST /v1/apps accepts an optional mobile: boolean field, defaulting to false. When true, the platform reports the Supports BitrixMobile flag to Bitrix24 at application registration on the account, and the application becomes visible in the mobile client. The mobile field now appears on the application object too — in the creation response, application data, the list and relink. Applications created earlier carry false. The flag is set only at creation — PATCH /v1/apps/:id does not accept mobile. When the account registers the application through a path that cannot carry the flag, the application is created with mobile: false and the creation response carries a warnings line starting with mobile:. Existing requests without mobile keep working. Details — Create an application.

BC-0904-3: the self-hosted refusal code is renamed to SELFHOSTED_NOT_AVAILABLE

Old format supported until: not provided

Before

A self-hosted Bitrix24 account on the international installation was refused with the code INT_BOX_PARTNER_REQUIRED. The message explained that access comes with a partner licence, that the key carried no confirmed partner mark, and asked the customer to re-register the module in the account. The button pointed at the connect documentation: details.upgradeUrl and alternatives[0].url carried the address of /docs/connect-self-hosted-bitrix24.

After

The same refusal arrives with the code SELFHOSTED_NOT_AVAILABLE. A client that branches on the code must replace the string — the old code is no longer returned on any surface.

The rest of the response is unchanged: the status is still HTTP 402, details.requiredTariffs is still empty (no plan purchase clears this refusal), and alternatives keeps the same members in the same order.

The message and the button address changed. The message no longer names the reason and no longer asks for anything to be done in the account: self-hosted is not available yet, access is being opened gradually, and the account's servers and data are kept as they are. details.upgradeUrl and alternatives[0].url now carry a mailto: support address — the only address where this is resolved.

FIX-0904-4: trial deployment hints account for galaxy host eligibility

Before

GET /v1/me promised one-shot deployment whenever any running or sleeping galaxy host existed. If that host could not accept the app, POST /v1/infra/servers rejected the request and its hint suggested two-step creation, which then hit the trial limit of the account.

After

GET /v1/me shows the one-shot path only when a known host appears eligible and explicitly marks the prediction as advisory. POST /v1/infra/servers remains authoritative; when an ineligible host occupies the trial limit and trial enforcement is active, the response no longer recommends an unavailable two-step create.

Impact on integrators

Check deployment.galaxyApp before a one-shot create and follow error.hint after a refusal. Before using the two-step path, also check capabilities.servers.create.available and choose a plan from capabilities.servers.create.limits.allowedPlans in the same response.

FIX-0904-5: a catalog card is no longer published without its Bitrix24 account binding

Before

Publication could complete with b24CatalogSync.status set to SYNCED even though the catalog card had no binding to the sender. In that state, the new-version notification did not arrive in the application chat and no reason was displayed.

After

When calling POST /v1/infra/servers/:id/b24-catalog/publish, such a card is no longer created. For a paired account, publication completes only after the card's binding to the sender has been confirmed. Until it is confirmed, b24CatalogSync.status stays other than SYNCED, pendingOp stays ADD, and lastError names the reason. The response is still HTTP 200, and a successful publication returns exactly what it returned before.

Impact on integrators

Requests do not need to change. If an integration monitors the asynchronous status, pendingOp=ADD together with a status other than SYNCED means that the card has not been published yet. Wait for the status to change or show the lastError value to the user.

FIX-0904-6: expiresAt of presigned URLs now equals their signature lifetime

Before

POST /v1/storage/objects/multipart/create computed parts[].expiresAt from the requested lifetime — 24 hours for every part — and GET /v1/apps/{id}/sources/{versionId}/download and GET /v1/infra/servers/{id}/sources/{versionId}/download from a fixed 30 minutes; the redirect link of GET /v1/storage/objects/{key} was documented as valid for 10 minutes. The currently disabled POST /v1/storage/objects answers 503 STORAGE_PRESIGNED_UPLOAD_DISABLED and computed expiresAt the same way. The URL signature could expire earlier, and storage answered 403 while expiresAt was still in the future.

After

expiresAt in these responses is taken from the URL signature itself and equals its lifetime. It may be shorter than the 24-hour multipart session and shorter than 30 minutes for download URLs; the redirect link may also expire before 10 minutes. Nothing changes for the disabled POST /v1/storage/objects: it still answers 503, and once enabled its expiresAt will also come from the signature. The response remains HTTP 200, the field format is unchanged.

Impact on integrators

Schedule uploads and downloads by the expiresAt from the response and start as early as you can, not by the uploadId session lifetime and not by the documented 30 minutes. Clients that already relied on expiresAt change nothing.

FIX-0904-7: the latin locales call Bitrix24 trial access a trial

Before

The latin locales called Bitrix24 trial access a demo, while Bitrix24 itself names it a trial on the international installation. The mismatch reached the contract too: GET /v1/me returned Demo period in data.tariff.name, and the INT_TARIFF_REQUIRED refusal told the reader to activate a paid plan "or its demo". Within a single activation flow neighbouring messages disagreed with each other: one said trial, the next said demo about the very same access.

After

The plan value and the refusal text in the latin locales are settled on the word trial. GET /v1/me returns Trial period for that plan, and the INT_TARIFF_REQUIRED refusal now offers a paid plan "or its trial". A client comparing this field by string should re-check the comparison: the codes, statuses and response fields themselves are unchanged, and a successful response stays successful. The Russian locale keeps its own wording.

This entry covers the contract: the plan value and the refusal text. At publication time the apps.bindPlacements capability note of the same endpoint and the documentation articles still said demo, and they were translated later, in FIX-0907-11.

FIX-0904-8: batch currencies sub-call no longer returns meta.total 0 next to a non-empty data

Before

POST /v1/batch with a currencies list sub-call published data.meta.<id>.total: 0 and hasMore: false while data.results.<id> held records. The Bitrix24 method crm.currency.list reports envelope total: 0 next to a non-empty result, and the batch door accepted that literal zero as the collection size. A client paging by hasMore under a limit smaller than the catalog read the first window and treated the set as exhausted, losing the tail.

After

On a counted sub-call the envelope zero no longer beats the measured set size: data.meta.<id>.total and data.totals.<id> report the size before the client-side window, and hasMore is computed as offset + returned < total, staying true while a tail remains. This matches the single endpoints, which already normalized this method behaviour. An uncounted sub-call (params.withTotal: false, a negative params.start) still gets no total, and a genuinely empty catalog still returns total: 0. The response remains HTTP 200.

NEW-0904-9: a partner application can revoke its own key

A new POST /v1/connect/revoke endpoint follows RFC 7009: the application sends client_id, client_secret (public clients send none) and token, and exactly the presented key is killed. Its slot in the user's key limit for that Bitrix24 account is freed immediately, and requests with the key start answering 401 KEY_INACTIVE.

A 200 also comes back when there was nothing to revoke — for an unknown token, for a key issued to another application and for an already revoked one — so the call is idempotent and cannot be used to probe whether someone else's keys are alive. A missing client_id or token gives 400 invalid_request; an unknown client or a wrong secret gives 401 invalid_client. A key issued before the client was deactivated can be revoked as well.

The endpoint is announced in the discovery document /.well-known/oauth-authorization-server through revocation_endpoint and revocation_endpoint_auth_methods_supported. Until now a key issued to an application could only be revoked by the user in the "Connected apps" section, by the application owner deleting the client outright, or by a platform administrator; all three keep working as before.

BC-0904-10: offset in the statuses and deal-categories references works at any depth

Old format supported until: not provided

Before

GET /v1/statuses returned the same records for offset=0, 50, 100 and 300. The Bitrix24 method behind this reference answers with the whole collection in one response and applies no navigation, while the wrapper cut that response from the beginning — the client received the first page in place of the fiftieth. At an offset that was not a multiple of 50 the window repeated with a period of 50: offset=130 returned the same records as offset=30.

An offset walk did terminate, but it collected duplicates and never reached the end of the reference: out of 267 records only about 50 were reachable through the list, while meta.total reported an honest 267. There was no error at any step — every response came back with status 200. GET /v1/deal-categories and list sub-calls inside POST /v1/batch behaved the same way.

A list sub-call inside a batch with NO explicit limit also behaved differently from the same list issued as a single request: a single GET /v1/statuses returned 50 records by default, while the batch sub-call returned the entire reference — all 267 records in one response.

After

The [offset, offset + limit) window is computed on the Vibecode side over the full set, in the order the core returned. offset=50&limit=5 yields records 51 through 55, a limit above 50 no longer truncates the tail, meta.total equals the size of the filtered set, and meta.hasMore turns false on the last page. An offset walk terminates and covers every record exactly once. The response remains 200.

A list sub-call inside a batch now follows the same default as a single request: with no explicit limit it returns the first 50 records and hasMore: true, not the whole reference.

Sorting and filtering are still performed by Bitrix24, so the sort parameter behaves as before. The same behaviour applies to POST /v1/statuses/search and to list sub-calls inside POST /v1/batch.

Impact on integrators

Loops that previously re-read the same records and never reached the end of the reference now return the full selection — they need no code change.

One case does require a code change: a POST /v1/batch sub-call listing statuses or deal categories with no explicit limit. It used to return the whole reference; it now returns the first 50 records. There is no error — the response comes back with status 200 and hasMore: true — so the remaining records are lost silently unless they are requested.

What to do: either set limit explicitly, or read the reference page by page, advancing offset by the page size until meta.hasMore becomes false. The second option is preferable — it does not depend on the size of the reference and works on any Bitrix24 account.

BC-0904-11: Workday history now returns the time-zone offset

Old format supported until: not provided

Before

GET /v1/workday/records required only timeman. Records did not contain a required tzOffset, so a client could not reliably obtain the employee's local time.

After

The endpoint requires timeman and one of user_brief, user_basic, or user. Every record contains a required tzOffset: seconds east of UTC calculated for the startTime instant using the historical rules of the current IANA TIME_ZONE identity in the employee profile. The value is not proof that this zone was assigned when the record was created. The startTime and endTime strings are unchanged. A non-empty page returns 502 BITRIX_UNAVAILABLE if the current profile zone or its offset cannot be determined reliably; the API cannot detect a zone reassignment after record creation and does not promise a 502 for it.

What integrators should do

Reissue existing keys with timeman and one user-family scope, then process the required tzOffset in the GET /v1/workday/records response.

BC-0904-13: Entity read calls now have a per-account rate limit

Old format supported until: not provided

Before

Entity reads — list (GET /v1/deals and the same call on every entity), search (POST /v1/deals/search), aggregate (POST /v1/deals/aggregate, including the legacy GET …/aggregate), field definitions (GET /v1/deals/fields), related records (GET /v1/deals/{id}/contacts, …/activities), product rows (GET /v1/deals/{id}/products) — and the per-entity batch (POST /v1/deals/batch and the same on every entity) accepted requests without a rate limit — while the global POST /v1/batch already carried one. One client reading a list more than roughly ten times per second slowed lists and search for every account on the same instance, up to request timeouts. A HEAD request to a list was served as a full GET: the data was read in full and only the headers were returned.

After

Every such read is capped at 300 requests per minute per Bitrix24 account; all API keys of one account share one limit, and each entity and each operation is counted separately. On exceeding it the Vibecode API answers 429 RATE_LIMITED with a Retry-After header; the body carries no delay — read it from the header. The current limit value arrives in the x-ratelimit-limit header. The per-entity batch is capped tighter — 30 requests per minute per Bitrix24 account, the same as the global POST /v1/batch: one such request fans out into hundreds of Bitrix24 calls. The HEAD method on these paths is no longer served by the read handler — use GET with limit=1 instead. Successful responses, error codes and payload formats are unchanged.

What integrators should do

Handle 429 on every entity read and on the batch the same way as on /v1/search and /v1/batch: wait for the delay in Retry-After and retry. If the limit triggers regularly, read less often and in larger pages (limit up to 5000 per call), cache results on your side and do not run identical reads in parallel. If you probed list availability with HEAD, use GET with limit=1 instead. Make batches larger rather than more frequent: one request takes up to 500 items.

NEW-0904-14: invoices support include=deal

GET /v1/invoices/:id, GET /v1/invoices, and POST /v1/invoices/search now accept include=deal. The related deal from parentId2 is returned in _included.deal. When no relation exists, the value is null.