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

API changes: July 9, 2026

← Changelog · July 2026

FIX-0709-1: transient DB overload now returns 503 with Retry-After instead of 500

Before

In a rare form of transient DB overload (connection exhaustion), some requests (including POST /v1/infra/servers) returned 500.

After

Such requests return 503 with code POOL_EXHAUSTED and a Retry-After header. The error is transient — retry the request with backoff.

Impact on integrators

Clients that retry on 5xx must now handle 503 and honor Retry-After. POST /v1/infra/servers is non-idempotent — a retry can create a duplicate server, so retry with backoff rather than immediately.

FIX-0709-2: placements/bind reports the Bitrix24 plan requirement clearly

Before

Binding a placement with an app key on an account whose Bitrix24 plan does not permit developer-key REST calls made Bitrix24 deny access, and POST /v1/placements/bind returned an opaque 502 BITRIX_UNAVAILABLE with no cause. The plan is checked on the developer-key path, but GET /v1/me gave no advance signal about this prerequisite.

After

The denial is now classified: POST /v1/placements/bind returns 403 with code INT_TARIFF_REQUIRED and a readable userMessage — the developer-key path needs a commercial Bitrix24 plan on the account. An app key's GET /v1/me gains a placements.bindPrerequisite block that describes the Bitrix24-side prerequisite up front and lists the error codes. Other bind failures (unknown clientId, stale embedding) still return 502 BITRIX_UNAVAILABLE.

Impact on integrators

No action required, successful calls are unchanged. If you handled 502 BITRIX_UNAVAILABLE on bind, also handle 403 INT_TARIFF_REQUIRED and prompt the user to upgrade the account to a commercial Bitrix24 plan.

FIX-0709-3: bindPrerequisite in GET /v1/me now reflects the Bitrix24 plan

Before

The placements.bindPrerequisite block on an app key described the bind prerequisite identically for every account: subscriptionRequired: true, a remedy that does not apply to an account whose access is granted by the Bitrix24 plan, and an errorCodes list carrying codes such an account never receives. On it the bind denial arrives as INT_TARIFF_REQUIRED.

After

The block now depends on the account. On an account whose access is granted by the Bitrix24 plan, subscriptionRequired is false, the note describes the commercial-plan requirement, and errorCodes carries INT_TARIFF_REQUIRED and BITRIX_UNAVAILABLE — only the codes that account can actually receive.

Impact on integrators

Successful calls are unchanged. If you read errorCodes from bindPrerequisite as an exhaustive list, note that it is now narrowed to the codes reachable on the calling account. The codes themselves and the behavior of POST /v1/placements/bind are unchanged.

BC-0709-4: CRM, task and landing fields aligned with the real Bitrix24 contract

Old format supported until: 09.01.2027

Before

GET /v1/deal-categories returned isLocked as the string "Y"/"N", and GET /v1/payments returned paySystemIsCash as the string "Y"/"N". GET /v1/leads exposed the internal searchContent field (Bitrix24's full-text search index) in every response. The payment fields paySystemXmlId, dateMarked, dateResponsibleId passed through as-is, with no date normalization.

After

isLocked (pipelines) and paySystemIsCash (payments) are now boolean (true/false). searchContent is no longer returned by GET /v1/leads. Payments now declare paySystemXmlId (string), dateMarked and dateResponsibleId (datetimes normalized to a single ISO form with a Z suffix). Tasks gained changedBy/closedBy/statusChangedBy (read-only — writing them returns 400 READONLY_FIELD). GET /v1/currencies/fields now returns human-readable label values and a lang field; GET /v1/deal-categories/fields returns label values; GET /v1/sites/fields sets a nullable flag on fields that may come back empty.

What integrators should do

Read isLocked and paySystemIsCash as boolean instead of comparing to the string "Y". If your code relied on the leads searchContent field, stop: it was an internal, undocumented field.

FIX-0709-5: creating an entity with an empty body is rejected with an explicit error

Before

Creating an entity with an empty body (or no body at all but a Content-Type: application/json header) reached Bitrix24 and silently created an entity with default values — including deals, leads, contacts, companies, invoices and smart processes. A retry or a bodyless client thus spawned junk records in CRM. This affected all three create surfaces: single POST /v1/{entity}, per-entity POST /v1/{entity}/batch and global POST /v1/batch.

After

Such a request returns 400 EMPTY_CREATE_BODY (a per-item error in batch calls) before Bitrix24 is called, on all three surfaces. A meaningful create always carries at least one field — send the fields you need in the request body. Entities that already validate their required fields behave as before.

FIX-0709-6: releasing a server lock accepts an empty JSON body

Before

DELETE /v1/infra/servers/:id/lock with a Content-Type: application/json header and an empty body returned 400 (empty JSON body). Releasing a stuck lock required sending an explicit {}, and a client unaware of that hit a dead end.

After

An empty body with that header is accepted as {}; a bodyless request works and releases the lock. An explicit {} still works too.

FIX-0709-7: Bitrix24 events now wake a sleeping galaxy app

Before

A Bitrix24 event sent to a sleeping galaxy app's subscription did not wake it — delivery retried and was lost after the attempts ran out.

After

The platform wakes a sleeping galaxy app on event delivery and delivers the event once it is up — same as for a standalone server.

NEW-0709-8: camelCase keys inside communications when creating an activity

POST /v1/activities now accepts the nested keys of communications items in camelCase (type, value, entityTypeId, entityId) — consistent with the rest of the API. Previously the nested keys were accepted only in Bitrix24 UPPER case (TYPE, VALUE, ENTITY_TYPE_ID, ENTITY_ID), and the camelCase form was silently dropped — communications: [{ "type": …, "value": … }] returned 422 "COMMUNICATIONS is not defined or invalid", while [{ "TYPE": …, "VALUE": … }] created the activity. UPPER case still works; if both forms of the same key appear in one object, the UPPER-case one wins.

FIX-0709-9: unpublishing removes the tab across all of the app's handlers

Before

POST /v1/apps/:id/unpublish unbound the placement only by the handler matching the assumed platform address. If the tab was bound to the app's own technical address, Bitrix24 did not find it and did not remove it — the tab stayed stuck in the CRM card and could no longer be removed via the API.

After

Unpublishing now removes the placement across all of the app's handlers, including one bound to the server's technical address — the orphaned tab disappears.

NEW-0709-10: Key self-description in GET /v1/guide

The GET /v1/guide response now carries a data.keysAuth block. It describes the two self-description endpoints — GET /v1/me and GET /v1/guide — and links to their documentation: the response contract of each, the key access mode, and the overview of key types.

Both endpoints work with the X-Api-Key header alone, no session token is required.

The field is additive: existing clients are unaffected.

Affected endpoints: GET /v1/guide

FIX-0709-11: GET /v1/{entity}/:id now honours ?select=

The ?select= field projection was ignored on single-record reads by id: the response always came back with every field, even though /v1/me states ?select= works "on list, get by id, and POST /search". Get-by-id now projects the response the same way list and search do, bringing the behaviour in line with what was documented.

Before

GET /v1/leads/42?select=id,title returned the full record (all fields).

After

GET /v1/leads/42?select=id,title returns only id and title. The comma (?select=id,title), array (?select[]=id&select[]=title) and indexed (?select[0]=id&select[1]=title) forms are all accepted; id is always included in the response. An unknown field name is silently skipped — it is not an error. When ?select= and ?include= are combined, the included relation is preserved in the response. The indexed form (?select[0]=…) previously returned 500 on the list endpoint GET /v1/{entity} too — it now projects correctly there as well.

FIX-0709-12: /fields for orders, basket items, requisite presets and document templates now matches the real B24 contract

Before

GET /:entity/fields (and the OpenAPI schema generated from it) declared fields that Bitrix24 never returns: provider on document templates; reserved, sumPaid, dateBill, datePayBefore, datePaid, empPaidId, userEmail, userName at the order top level; module, fUserId, lid, dateRefresh, subscribe, reserved, reserveQuantity on basket items; originatorId on requisite presets. Filtering and sorting by those fields silently did nothing. Meanwhile real fields were left undeclared: requisiteLink on the order, type/properties/reservations on the basket item. Requisite presets accepted countryId/entityTypeId on update, where Bitrix24 silently ignores them.

After

The non-existent fields are removed from /fields and OpenAPI. The real fields are declared: on the order — requisiteLink (object requisiteId/bankDetailId/mcRequisiteId/mcBankDetailId, read-only); on the basket item — type, properties, reservations (read-only). On requisite presets, countryId and entityTypeId are marked create-only: an update returns 400 READONLY_FIELD instead of a silent no-op.

Impact on integrators

list/get responses are unchanged — the removed fields were never returned. If a request filtered or sorted by a removed field it now returns 400 — use the real fields from /fields (for example, an order's payment dates and sums live inside the payments array, not at the top level). Updating countryId/entityTypeId on a requisite preset is now explicitly rejected — set those fields on create only.

Affected endpoints: GET /v1/orders/fields, GET /v1/basket-items/fields, GET /v1/requisite-presets/fields, GET /v1/doc-templates/fields

NEW-0709-13: GET /:entity/fields returns human-readable labels and descriptions for deals, leads, invoices, activities, statuses and timeline comments

The response of GET /v1/deals/fields, /v1/leads/fields, /v1/invoices/fields, /v1/activities/fields, /v1/statuses/fields and /v1/timelines/fields now carries a human-readable label and description for every field, returned in English. Fields with magic-number codes gained enum dictionaries: deals — stageSemanticId (P — in progress, S — success, F — failure); activities — typeId, direction, priority, status, notifyType and descriptionType. Existing calls keep working unchanged — these are additive metadata fields, the response shape does not change.

FIX-0709-14: POST /v1/batch — unified sub-call error shape and totals only for list/search

Before

A Bitrix24 error inside a successful 200 POST /v1/batch response (for example, a get of a missing element) landed in data.errors[<id>] in Bitrix24's native { error, error_description } form — not the V1 envelope { code, message } used by validation errors and every other API response. Meanwhile data.totals[<id>] was populated for any action, including get/create/update/delete, where a lone number next to a single record is meaningless.

After

A sub-call error is mapped to { code, message } (error → code, error_description → message), consistent with all other errors. data.totals[<id>] is populated only for list and search actions, where a match count actually has meaning.

FIX-0709-15: GET /v1/openline-configs — empty-value normalization in the response

Before

GET /v1/openline-configs and GET /v1/openline-configs/:id returned service fields in shapes awkward for clients: KPI_FIRST_ANSWER_LIST, KPI_FURTHER_ANSWER_LIST, DEFAULT_OPERATOR_DATA came back as null (client .map/.length broke on them); AUTO_CLOSE_TEXT for an unset value came back as "" in the card but null in the list; WORKTIME_HOLIDAYS/WORKTIME_DAYOFF for an empty set came back as [""] (an array with one empty string).

After

Lists are normalized: null → []. AUTO_CLOSE_TEXT converges on a single null for an unset value in both the list and the card. WORKTIME_HOLIDAYS/WORKTIME_DAYOFF return [] for an empty set. The list and the card now return the same shape for these fields.

FIX-0709-16: GET /v1/users/fields returns the possible values (items) for enumeration user-fields

Before

A custom enumeration user-field (UF_USR_* of the "list" type) came back from GET /v1/users/fields as { "type": "string", "label": "…" } — with no list of possible values. Reason: for UF fields the user.fields method returns only a label, not the type or the variants, so an enumeration was indistinguishable from a string.

After

Such a field now carries its real type and the list of variants: { "type": "enumeration", "label": "…", "items": [ { "ID": "…", "VALUE": "…", "DEF": "…", "XML_ID": "…" }, … ] }. The values are read from user.userfield.list — the key needs the user.userfield scope for this; without it the field is still returned with its label but without items (graceful degradation). Other UF fields (money, date, …) likewise now show their real type instead of string.

FIX-0709-17: the empty-queue hint now appears after sustained emptiness

Before

GET /v1/bots/:botId/events incremented the empty-response counter on exactly every request, and the hint field appeared strictly after the fifth consecutive empty response. The number in the hint text matched the number of requests made.

After

The empty-response counter is updated periodically rather than on every request, so hint appears after the queue stays empty for a sustained period — at the recommended 2–5 second polling interval, after roughly a couple of minutes of continuously empty polling. The number N in the text reflects the count of observed empty periods, not the exact number of requests made. The "an event was delivered → counter and hint reset" rule is unchanged.

Impact on integrators

No code change is needed. If you relied on hint appearing strictly on the fifth request or read N as an exact request count — use persisted and the presence of events as the primary signal, and treat hint as a diagnostic hint.