For AI agents: markdown of this page — /docs-content-en/changelog/2026-07-10.md documentation index — /llms.txt
API changes: July 10, 2026
NEW-0710-1: wake-schedule window management (wake-schedules)
New CRUD contract on Black Hole servers: GET/POST /v1/infra/servers/:id/wake-schedules, PATCH/DELETE /v1/infra/servers/:id/wake-schedules/:scheduleId. Lets you declare one or more recurring wake windows (cronExpr + a required IANA timezone, optional label/lead/enabled) — the platform wakes a sleeping server ahead of the window, and the app's own in-VM cron runs the task from there.
Rolling out gradually and not yet available on every Bitrix24 account — until enabled on a given account, the call returns 403 with code WAKE_SCHEDULE_DISABLED. Available only for BLACKHOLE-mode servers (otherwise 400 BLACKHOLE_ONLY) and not yet supported for galaxies (400 GALAXY_NOT_SUPPORTED on both the host and a nested app — coming later). The minimum cadence between occurrences and the per-server window cap (50) are platform-configured; violating either returns 400 CADENCE_TOO_LOW and 403 WAKE_SCHEDULE_LIMIT respectively. The create/update response additionally carries a tzWarning field — a heads-up that the VM's in-VM timezone may have drifted from the window's timezone if the server hasn't been redeployed since. PATCH replaces the whole window (PUT semantics): omitted optional fields reset to their defaults — enabled→true, label/lead→null. Send the full window object on update.
Affected endpoints: GET|POST /v1/infra/servers/:id/wake-schedules, PATCH|DELETE /v1/infra/servers/:id/wake-schedules/:scheduleId.
NEW-0710-2: machine-readable tz-warning code in wake-schedules responses (`tzWarningCode`)
The wake-schedule create/update response (POST/PATCH /v1/infra/servers/:id/wake-schedules[/:scheduleId]) now additionally carries a tzWarningCode field alongside the existing text tzWarning — "SINGLE_ZONE" / "MULTI_ZONE" / null (when the server ends up with zero enabled windows after the mutation). It's a machine-readable companion to the same advisory, letting clients localize the message themselves instead of rendering the raw English tzWarning text. The field is additive — tzWarning is unchanged and stays for backward compatibility.
Affected endpoints: POST|PATCH /v1/infra/servers/:id/wake-schedules[/:scheduleId].
FIX-0710-3: wake-schedules: schedules rejected on always-on servers
POST/PATCH /v1/infra/servers/:id/wake-schedules now reject creating or updating a wake window on an always-on (24/7) server — the call returns 400 with code ALWAYS_ON_CONFLICT. Such a server never auto-sleeps, so a wake schedule would silently break the paid always-online guarantee. Ordinary sleeping Black Hole servers and preemptible agents/bots are unaffected. Turn off always-on to declare wake windows.
Affected endpoints: POST /v1/infra/servers/:id/wake-schedules, PATCH /v1/infra/servers/:id/wake-schedules/:scheduleId.
FIX-0710-4: wake-schedules can now be declared on nested galaxy apps
Before
POST/PATCH /v1/infra/servers/:id/wake-schedules returned 400 GALAXY_NOT_SUPPORTED for any galaxy-family server — both the host itself and nested apps (kind=GALAXY_APP).
After
Nested galaxy apps (kind=GALAXY_APP) are now accepted — you can declare a wake window on a specific app, and the platform wakes it (and the host, if needed) ahead of the window. The galaxy host itself still returns 400 GALAXY_NOT_SUPPORTED — schedule the individual apps, not the host.
Affected endpoints: POST|GET /v1/infra/servers/:id/wake-schedules, PATCH|DELETE /v1/infra/servers/:id/wake-schedules/:scheduleId.
FIX-0710-5: sleep-now declines to sleep when a scheduled wake is imminent
Before
POST /v1/infra/servers/:id/sleep-now always put the server to sleep immediately, even when the next scheduled wake was a minute away — the server woke straight back up.
After
When the server has an enabled wake schedule and the next wake falls within the guard margin, the call does not sleep the server and responds 200 with { "success": true, "data": { "slept": false, "reason": "WAKE_IMMINENT" } }. Otherwise the server sleeps and the scheduler wakes it at the next window.
Impact on integrators
The data block with slept: false arrives only when the call declines to sleep the server — check for its presence if you rely on the server always sleeping after this call. On a successful sleep the response is just success: true. Existing calls to servers without a schedule are unchanged.
FIX-0710-6: non-streaming chat/completions and embeddings no longer abort at 360 seconds
Before
A non-streaming (stream:false) POST /v1/chat/completions (and POST /v1/embeddings) request whose generation took longer than ~2 minutes reliably aborted at ~360 seconds with {"code":"ai_provider_unavailable","message":"This operation was aborted"} — regardless of the client's own timeout. The doomed generation also burned triple the compute.
After
Such a request now runs within a single ~850-second budget (one attempt for the whole budget, no compute tripling). If generation still does not finish in budget, a meaningful 503 with code ai_provider_timeout is returned, with a localized userMessage and a hint (reduce the request size or use streaming mode stream:true) — without a Retry-After header (the timeout is not transient). A client disconnect now immediately cancels the provider-side generation. Streaming mode (stream:true) was never affected by this limit.
FIX-0710-7: requisite-presets/:presetId/fields and requisite-links: list sorting, filtering, and parameter validation
Before
GET /v1/requisite-presets/:presetId/fields ignored sort/order and any filter[...] — it always returned the full list in Bitrix24 order. GET /v1/requisite-links and POST /v1/requisite-links/search accepted only plain equality; comparison operators ($gte, >, etc.), sort/order, and unknown fields were silently dropped, so the whole table came back.
After
Both lists now honour sort/order (including the order[field]=asc|desc form) and filter. requisite-links supports comparison operators ($gt/$gte/$lt/$lte, $in/$nin, >=/> prefixes). An unknown filter or sort field now returns 400 (UNKNOWN_FILTER_FIELD / UNKNOWN_SORT_FIELD), a top-level logical operator ($or/$and) returns 400 INVALID_FILTER_OPERATOR, and filtering by entityId without entityTypeId returns 400 MISSING_ENTITY_TYPE_ID instead of a raw "Access denied".
Impact on integrators
Requests on documented fields keep working and now actually sort/filter. If you relied on an unknown parameter being silently ignored, it now returns 400 — drop the typo or use a field from the response.
FIX-0710-8: Bank details sorting by id honors the direction
Before
Listing bank details (GET /v1/bank-details) sorted by id descending (?sort=-id) returned records in ascending order — the sort direction was silently ignored.
After
?sort=-id (and ?sort=id) sorts by the identifier in the requested direction.
Integrator impact
No code change required — requests that relied on sorting by id now return the expected order.
NEW-0710-9: GET /v1/quotes/fields — ~26 quote fields declared with human-readable labels
The quotes entity schema now declares ~26 fields that Bitrix24 returned but that were undeclared: quoteNumber, updatedBy, lastActivityBy, lastActivityTime, content, terms, leadId, storageTypeId, storageElementIds, personTypeId, webformId, lastCommunicationTime, contactIds, contacts, locationId, taxValue, actualDate, mycompanyId, utmSource/utmMedium/utmCampaign/utmContent/utmTerm, lastCommunicationCallTime/lastCommunicationEmailTime/lastCommunicationImolTime/lastCommunicationWebformTime. They now appear in GET /v1/quotes/fields with readable labels (instead of service names like STORAGE_TYPE_ID/UTM_SOURCE), and their values are type-coerced (numbers, ISO dates) in list/get responses. Labels were also added to the previously label-less stageId, opened, closed.
FIX-0710-10: a PATCH with no writable field is rejected with an explicit error
Before
PATCH /v1/{entity}/:id with an empty body — or a body carrying no recognized writable field (for example because of a typo in a field name) — reached Bitrix24, which silently ignored the request and returned success. The wrapper then replied 200 with the unchanged object, so the client believed the edit had applied while nothing actually changed — a silent-drift risk, especially for AI agents. This affected all three update surfaces: single PATCH /v1/{entity}/:id, per-entity POST /v1/{entity}/batch and global POST /v1/batch.
After
An update with an empty body returns 400 EMPTY_UPDATE_BODY (a per-item error in batch calls) on all three surfaces, before Bitrix24 is called. For /v1/catalog-products/:id a strict check is added: a PATCH that carries no recognized writable field returns 400 NO_RECOGNIZED_UPDATE_FIELDS. A meaningful update always carries at least one field — send a recognized field (custom properties propertyNNN and UF_* fields are accepted too). Entities that already validated their fields behave as before.
Catalog products additionally expose in GET /v1/catalog-products/fields, and now accept for explicit select, filter and sort, the fields from the product-update contract — code, xmlId, sort, vatId, height, length, width, previewText, detailText and others (filtering by them previously returned 400 UNKNOWN_FILTER_FIELD). Filter and sort reliably by the indexable fields (code, xmlId, sort, vatId, dimensions); for the full-text ones (previewText, detailText) Bitrix24 may ignore the filter. List responses without an explicit select are unchanged.
FIX-0710-11: leads, companies, quotes — /fields date keys are now createdTime and updatedTime
Before
GET /v1/leads/fields, GET /v1/companies/fields and GET /v1/quotes/fields advertised createdAt and updatedAt, while list/get/search responses always returned createdTime and updatedTime — the value was never readable under the createdAt/updatedAt name. Filter and sort accepted the createdAt/updatedAt names.
After
These entities' /fields advertise the real keys createdTime and updatedTime — like contacts, invoices and items. The keys in the response body are the same (createdTime/updatedTime were always returned), but the value is now normalized to ISO-8601 in UTC (2026-04-15T07:00:00.000Z) — previously it arrived in the raw Bitrix24 form with the account offset (2026-04-15T08:00:00+01:00). Same instant, only the representation changes.
Impact on integrators
Read dates from createdTime and updatedTime (the keys did not change). If you compare the date string byte-for-byte or cache by it, account for the offset → Z shift (same instant). In filter and sort use createdTime/updatedTime; the former createdAt/updatedAt now return 400 UNKNOWN_FILTER_FIELD (filter) and 400 UNKNOWN_SORT_FIELD (sort). On write, createdAt/updatedAt are no longer rejected as read-only — they are ignored as unknown fields (like contacts/invoices/items); the creation/update timestamp still cannot be set.
FIX-0710-12: sleep settings: flipping to always-on is now rejected while wake windows are active
PATCH /v1/infra/servers/:id/sleep now rejects setting sleepAfterMinutes: null (always-on, 24/7) on a server that still has enabled wake-schedule windows — the call returns 400 with code ALWAYS_ON_CONFLICT. This is the reverse direction of an existing gate: creating a wake window on an always-on server was already rejected, and now the opposite transition is rejected symmetrically — otherwise the server would keep sleeping on schedule, silently breaking the paid always-online guarantee. Delete or disable the wake windows first, or keep a sleep timeout instead of "Never", to turn always-on on.
Affected endpoints: PATCH /v1/infra/servers/:id/sleep.
NEW-0710-13: placement.bind on a self-hosted Bitrix24 returns a clear SESSION_REQUIRES_ADMIN for a non-administrator
On a self-hosted (BOX) Bitrix24, binding a placement via the developer-key path requires the key's user to be an account administrator. Previously a non-administrator request returned an opaque 502 BITRIX_UNAVAILABLE.
Now POST /v1/placements/bind recognises the access-denied signal from Bitrix24 and returns 403 SESSION_REQUIRES_ADMIN with a hint: bind from an account-administrator account, or ask an administrator to grant those rights. The requirement is visible up front in GET /v1/me — the placements.bindPrerequisite block for self-hosted accounts now includes the SESSION_REQUIRES_ADMIN code.
FIX-0710-14: GET /v1/me capabilities now reflect read-only (READONLY) mode
Before
For a READONLY-mode key, GET /v1/me returned capabilities.managedBots.create, agents.create, servers.create and apps.* as available: true, even though every write is blocked with 403 WRITE_BLOCKED_READONLY_KEY. An agent saw "can create" and hit the rejection.
After
For a READONLY key these write capabilities are returned as available: false with reason: "WRITE_BLOCKED_READONLY_KEY" and a hint to switch the key to read+write. Read-only capabilities and the AI Router are unchanged. For READWRITE keys the response is unchanged.
FIX-0710-15: the feedback author can reply to their own ticket again without the vibe:feedback scope
Before
POST /v1/feedback/:id/comments rejected the ticket author with 403 FEEDBACK_SCOPE_REQUIRED when the key lacked the vibe:feedback scope — even though the author branch was documented. The author could not reply to their own AWAITING_USER ticket, and it stalled.
After
The author check now runs before the scope gate: the author replying with the same key that created the ticket reaches the author branch (authorType=USER, ball-court rule AWAITING_USER → NEEDS_REVIEW) even without the vibe:feedback scope. A key that is neither the author nor scoped still gets 403 FEEDBACK_SCOPE_REQUIRED.
NEW-0710-16: AI quota pacing: pacing field in the response and 429 ai_pacing_limited error
The GET /v1/ai/quota response gained a pacing field — the state of monthly AI quota pacing (peak smoothing via a daily and a weekly limit layered on top of the overall monthly limit; enabled by the account administrator from the /ai dashboard page). null when pacing is off at the platform level or not configured for the account; otherwise an object { mode, active, day, week }: mode is the enforcement behavior on a tripped limit (wallet/block/ignore), active signals whether tripping a window will reject a call right now (false in observation mode, and always false in ignore mode — windows are tracked as informational only, 429 is never returned), day and week are each { pctUsed, resetAt } as a percentage of that window's OWN limit. The response is still cached for 30 seconds, so pacing state can lag by that long.
When the daily or weekly limit trips, calls to POST /v1/chat/completions, POST /v1/embeddings, and POST /v1/audio/transcriptions can now return 429 with the body { success: false, error: { code: "ai_pacing_limited", type: "rate_limit_exceeded", message, reason, overageDenied, resetAt, retryAfter } } and a Retry-After header. reason names the tripped window (day_window or week_window); overageDenied names why paid overage was refused (wallet_empty, breaker, wallet_off), or is null under the hard-block mode. Retrying before Retry-After/resetAt will not help — the quota is not any more available in the meantime. Pacing is off by default — the platform enables it.
FIX-0710-17: GET /v1/pages, /v1/sites and POST /v1/{pages,sites}/search now honour offset
Before
Listing pages or sites with an offset (GET /v1/pages?offset=50, POST /v1/pages/search with offset) returned 422 BITRIX_ERROR "Unknown parameter: start". The first page (no offset) worked.
After
offset for pages and sites is handled correctly: the requested window [offset, offset+limit) is returned, with meta.total and meta.hasMore computed from the row count. No client change is needed — calls without offset behave as before.
NEW-0710-18: /fields of documents, catalogs, prices, telephony lines, basket items and leads gain richer metadata
GET /:entity/fields of several entities now carries more complete schema metadata. Documents (GET /v1/documents/fields), catalogs (GET /v1/catalogs/fields), catalog prices (GET /v1/catalog-prices/fields) and telephony lines now expose a human-readable label and description per field, returned in English.
For basket items (GET /v1/basket-items/fields) the fields weight, vatRate, measureCode, measureName, dimensions, productXmlId, catalogXmlId are marked with a nullable flag — they may come back empty. For leads (GET /v1/leads/fields) the same flag marks secondName, sourceDescription, comments, and the previously-undeclared fields originatorId, dateClosed, lastCommunicationTime and the tags utmSource/utmMedium/utmCampaign/utmContent/utmTerm are now declared — filter and sort work on them, and dateClosed is normalized to ISO-8601.
For landing sites the group-by dimensions are declared, so POST /v1/sites/aggregate with groupBy (type, active, deleted, lang, tplId, domainId, createdById, modifiedById) no longer answers Available: .. For documents aggregation is disabled (every numeric field is an identifier): POST /v1/documents/aggregate returns 404.
Existing calls keep working unchanged — this is additional field metadata.
NEW-0710-19: Idempotent server creation — Idempotency-Key header
POST /v1/infra/servers now accepts an optional Idempotency-Key header when creating a standalone server. A retry with the same key — for example after a lost response or a network drop — does not create a second server: it returns the same server the first request created, with status 201 and an Idempotent-Replayed: true response header. The key is a 1–255 character string from [A-Za-z0-9_.:-]; it is scoped to your API key.
On a replay the one-time SSH credentials (ssh.privateKey / ssh.password) are NOT re-exposed — they are null in the response body and a note field explains this. Keep the credentials from the first create response.
New error codes: 400 INVALID_IDEMPOTENCY_KEY (the key fails validation), 400 IDEMPOTENCY_UNSUPPORTED_WITH_GRADUATION (the key together with graduateFrom for a dedicated server is not supported), 409 IDEMPOTENCY_KEY_ALREADY_USED (the key was already used for a server that has since been deleted), 409 IDEMPOTENCY_CONCURRENT_RETRY (a concurrent request with the same key is still in progress — retry shortly).
The header applies to standalone servers only. On galaxy-placement accounts a well-formed key is ignored without an error, and the retry protection does not extend to that path.
Additionally: the create response now returns the canonical server name (suffixed when a name collision is resolved) instead of the name from the request — these previously diverged on a same-name collision.
NEW-0710-20: localized message on OPEN-mode switch denials
Responses from PATCH /v1/infra/servers/:id/mode with codes OPEN_MODE_DISABLED (OPEN mode is disabled platform-wide) and OPEN_MODE_NOT_ALLOWED (OPEN mode is not allowed by portal policy) now additionally carry an error.userMessage field — a localized, human-readable message pointing to the Deploy API as the supported replacement for direct SSH. The field is additive: error.message (English technical string), error.code and the HTTP status are unchanged. It matches the shape of the existing error.userMessage on the OPEN_MODE_REQUIRES_COMMERCIAL denial. The userMessage text depends on the key owner's locale.
BC-0710-21: Open Channels config fields normalized to camelCase and described in /fields
Old format supported until: 10.01.2027
Before
GET /v1/openline-configs, GET /v1/openline-configs/{id} and POST /v1/openline-configs/search returned most configuration fields in the Bitrix24-native form — upper case with underscores (CRM_CREATE, WELCOME_MESSAGE, QUEUE_TIME, and others). The GET /v1/openline-configs/fields reference described only 6 fields, so the rest were invisible to programmatic discovery.
After
All configuration fields are normalized to a single camelCase form (crmCreate, welcomeMessage, queueTime, and so on), and /fields describes the full field set with human label and description values. Filtering and sorting by the new camelCase names work. On write (create/update) both cases are still accepted — existing calls that send upper-case names in the body keep working.
What integrators should do
Read response fields by their camelCase names: config.crmCreate instead of config.CRM_CREATE. The mapping is a direct transliteration from upper case to camelCase (WELCOME_BOT_ID → welcomeBotId, WORKTIME_TO → workTimeTo, LINE_NAME → name). The full list of new names is in the /fields reference.
NEW-0710-22: Added GET /v1/warehouses/fields — warehouse field schema
A new GET /v1/warehouses/fields endpoint returns the schema of the 19 warehouse fields: for each field — its type, a read-only flag (readonly), a human label, and a description. Warehouses are a custom route (no entity schema), so they previously lacked the field reference that auto-generated entities have. The response is { success: true, data: { fields: { … } } }. Requires the catalog scope.
FIX-0710-23: app publish recovers from a placement drift
Before
On publish (POST /v1/apps/:id/publish) or a placement update (PATCH /v1/apps/:id), if a placement was registered on the Bitrix24 side but missing from the app (drift after an unpublish), the bind failed with "Handler already binded" and the placement stayed out of sync.
After
On that error the platform sweeps the stale binding once and retries — the placement syncs automatically. Recovery fires only on the confirmed conflict, so a live placement is never stripped by mistake; placements that need non-portable OPTIONS (chat widgets, the background worker) are excluded from auto-recovery and still surface a warning.
FIX-0710-24: storage: object sha256 is populated on direct upload
Before
The sha256 field in the storage-object upload response was always null for user objects, even though the schema described it as "computed on upload".
After
For direct upload (POST /v1/storage/objects/upload, files up to 10 MB) sha256 now carries the SHA-256 hash of the object content — usable for integrity checks and duplicate detection (identical content yields an identical hash). Presigned and multipart uploads send bytes straight to storage bypassing the platform, so sha256 stays null there for now.
FIX-0710-25: pacing.active in GET /v1/ai/quota no longer reports an active limit when the quota is zero
Before
With pacing enabled on a Bitrix24 account whose monthly quota is zero (a hard stop via the monthlyVibes = 0 override, or usage not yet initialized), data.pacing.active returned true — even though a 429 ai_pacing_limited rejection is structurally impossible in that state: requests are rejected by the monthly limit, not by a pacing window.
After
data.pacing.active returns true only when exceeding the day or week window can actually produce 429 ai_pacing_limited. With a zero quota the field honestly reports false. The response shape is unchanged; clients that built backoff logic on active need no changes — the signal is simply more accurate.