[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"docs-changelog\u002F2026-07-10":3,"docs-tabs-changelog\u002F2026-07-10":6},{"content":4,"lastmod":5},"# API changes: July 10, 2026\n\n[← Changelog](\u002Fdocs\u002Fchangelog) · [July 2026](\u002Fdocs\u002Fchangelog\u002F2026-07)\n\n### NEW-0710-1: wake-schedule window management (wake-schedules)\n\nNew CRUD contract on Black Hole servers: `GET`\u002F`POST \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fwake-schedules`, `PATCH`\u002F`DELETE \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fwake-schedules\u002F:scheduleId`. Lets you declare one or more recurring wake windows (`cronExpr` + a required IANA `timezone`, optional `label`\u002F`lead`\u002F`enabled`) — the platform wakes a sleeping server ahead of the window, and the app's own in-VM cron runs the task from there.\n\nRolling 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\u002Fupdate 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`\u002F`lead`→null.** Send the full window object on update.\n\nAffected endpoints: `GET|POST \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fwake-schedules`, `PATCH|DELETE \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fwake-schedules\u002F:scheduleId`.\n\n### NEW-0710-2: machine-readable tz-warning code in wake-schedules responses (`tzWarningCode`)\n\nThe wake-schedule create\u002Fupdate response (`POST`\u002F`PATCH \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fwake-schedules[\u002F:scheduleId]`) now additionally carries a `tzWarningCode` field alongside the existing text `tzWarning` — `\"SINGLE_ZONE\"` \u002F `\"MULTI_ZONE\"` \u002F `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.\n\nAffected endpoints: `POST|PATCH \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fwake-schedules[\u002F:scheduleId]`.\n\n### FIX-0710-3: wake-schedules: schedules rejected on always-on servers\n\n`POST`\u002F`PATCH \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fwake-schedules` now reject creating or updating a wake window on an always-on (24\u002F7) 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\u002Fbots are unaffected. Turn off always-on to declare wake windows.\n\nAffected endpoints: `POST \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fwake-schedules`, `PATCH \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fwake-schedules\u002F:scheduleId`.\n\n### FIX-0710-4: wake-schedules can now be declared on nested galaxy apps\n\n**Before**\n\n`POST`\u002F`PATCH \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fwake-schedules` returned `400 GALAXY_NOT_SUPPORTED` for any galaxy-family server — both the host itself and nested apps (`kind=GALAXY_APP`).\n\n**After**\n\nNested 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.\n\nAffected endpoints: `POST|GET \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fwake-schedules`, `PATCH|DELETE \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fwake-schedules\u002F:scheduleId`.\n\n### FIX-0710-5: sleep-now declines to sleep when a scheduled wake is imminent\n\n**Before**\n\n[POST \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fsleep-now](\u002Fdocs\u002Finfra\u002Flifecycle\u002Fsleep-now) always put the server to sleep immediately, even when the next scheduled wake was a minute away — the server woke straight back up.\n\n**After**\n\nWhen 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.\n\n**Impact on integrators**\n\nThe `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.\n\n### FIX-0710-6: non-streaming chat\u002Fcompletions and embeddings no longer abort at 360 seconds\n\n**Before**\n\nA non-streaming (`stream:false`) `POST \u002Fv1\u002Fchat\u002Fcompletions` (and `POST \u002Fv1\u002Fembeddings`) 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.\n\n**After**\n\nSuch a request now runs within a single ~850-second budget. 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.\n\n### FIX-0710-7: requisite-presets\u002F:presetId\u002Ffields and requisite-links: list sorting, filtering, and parameter validation\n\n**Before**\n\n[GET \u002Fv1\u002Frequisite-presets\u002F:presetId\u002Ffields](\u002Fdocs\u002Fentities\u002Frequisite-presets) ignored `sort`\u002F`order` and any `filter[...]` — it always returned the full list in Bitrix24 order. [GET \u002Fv1\u002Frequisite-links](\u002Fdocs\u002Fentities\u002Frequisite-links) and [POST \u002Fv1\u002Frequisite-links\u002Fsearch](\u002Fdocs\u002Fentities\u002Frequisite-links) accepted only plain equality; comparison operators (`$gte`, `>`, etc.), `sort`\u002F`order`, and unknown fields were silently dropped, so the whole table came back.\n\n**After**\n\nBoth lists now honour `sort`\u002F`order` (including the `order[field]=asc|desc` form) and `filter`. requisite-links supports comparison operators (`$gt`\u002F`$gte`\u002F`$lt`\u002F`$lte`, `$in`\u002F`$nin`, `>=`\u002F`>` prefixes). An unknown filter or sort field now returns `400` (`UNKNOWN_FILTER_FIELD` \u002F `UNKNOWN_SORT_FIELD`), a top-level logical operator (`$or`\u002F`$and`) returns `400 INVALID_FILTER_OPERATOR`, and filtering by `entityId` without `entityTypeId` returns `400 MISSING_ENTITY_TYPE_ID` instead of a raw \"Access denied\".\n\n**Impact on integrators**\n\nRequests on documented fields keep working and now actually sort\u002Ffilter. If you relied on an unknown parameter being silently ignored, it now returns `400` — drop the typo or use a field from the response.\n\n### FIX-0710-8: Bank details sorting by id honors the direction\n\n**Before**\n\nListing bank details ([GET \u002Fv1\u002Fbank-details](\u002Fdocs\u002Fentities\u002Fbank-details\u002Flist)) sorted by `id` descending (`?sort=-id`) returned records in ascending order — the sort direction was silently ignored.\n\n**After**\n\n`?sort=-id` (and `?sort=id`) sorts by the identifier in the requested direction.\n\n**Integrator impact**\n\nNo code change required — requests that relied on sorting by `id` now return the expected order.\n\n### NEW-0710-9: GET \u002Fv1\u002Fquotes\u002Ffields — ~26 quote fields declared with human-readable labels\n\nThe 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`\u002F`utmMedium`\u002F`utmCampaign`\u002F`utmContent`\u002F`utmTerm`, `lastCommunicationCallTime`\u002F`lastCommunicationEmailTime`\u002F`lastCommunicationImolTime`\u002F`lastCommunicationWebformTime`. They now appear in [GET \u002Fv1\u002Fquotes\u002Ffields](\u002Fdocs\u002Fentities\u002Fquotes\u002Ffields) with readable labels (instead of service names like `STORAGE_TYPE_ID`\u002F`UTM_SOURCE`), and their values are type-coerced (numbers, ISO dates) in list\u002Fget responses. Labels were also added to the previously label-less `stageId`, `opened`, `closed`.\n\n### FIX-0710-10: a PATCH with no writable field is rejected with an explicit error\n\n**Before**\n\n`PATCH \u002Fv1\u002F{entity}\u002F: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 \u002Fv1\u002F{entity}\u002F:id`, per-entity `POST \u002Fv1\u002F{entity}\u002Fbatch` and global `POST \u002Fv1\u002Fbatch`.\n\n**After**\n\nAn 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 `\u002Fv1\u002Fcatalog-products\u002F: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.\n\nCatalog products additionally expose in `GET \u002Fv1\u002Fcatalog-products\u002Ffields`, 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.\n\n### FIX-0710-11: leads, companies, quotes — \u002Ffields date keys are now createdTime and updatedTime\n\n**Before**\n\n`GET \u002Fv1\u002Fleads\u002Ffields`, `GET \u002Fv1\u002Fcompanies\u002Ffields` and `GET \u002Fv1\u002Fquotes\u002Ffields` advertised `createdAt` and `updatedAt`, while list\u002Fget\u002Fsearch responses always returned `createdTime` and `updatedTime` — the value was never readable under the `createdAt`\u002F`updatedAt` name. Filter and sort accepted the `createdAt`\u002F`updatedAt` names.\n\n**After**\n\nThese entities' `\u002Ffields` advertise the real keys `createdTime` and `updatedTime` — like contacts, invoices and items. The keys in the response body are the same (`createdTime`\u002F`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.\n\n**Impact on integrators**\n\nRead 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`\u002F`updatedTime`; the former `createdAt`\u002F`updatedAt` now return `400 UNKNOWN_FILTER_FIELD` (filter) and `400 UNKNOWN_SORT_FIELD` (sort). On write, `createdAt`\u002F`updatedAt` are no longer rejected as read-only — they are ignored as unknown fields (like contacts\u002Finvoices\u002Fitems); the creation\u002Fupdate timestamp still cannot be set.\n\n### FIX-0710-12: sleep settings: flipping to always-on is now rejected while wake windows are active\n\n`PATCH \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fsleep` now rejects setting `sleepAfterMinutes: null` (always-on, 24\u002F7) 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.\n\nAffected endpoints: `PATCH \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fsleep`.\n\n### NEW-0710-13: placement.bind on a self-hosted Bitrix24 returns a clear SESSION_REQUIRES_ADMIN for a non-administrator\n\nOn 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`.\n\nNow [POST \u002Fv1\u002Fplacements\u002Fbind](\u002Fdocs\u002Fkeys-auth) 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 \u002Fv1\u002Fme](\u002Fdocs\u002Fkeys-auth) — the `placements.bindPrerequisite` block for self-hosted accounts now includes the `SESSION_REQUIRES_ADMIN` code.\n\n### FIX-0710-14: GET \u002Fv1\u002Fme capabilities now reflect read-only (READONLY) mode\n\n**Before**\n\nFor a READONLY-mode key, [GET \u002Fv1\u002Fme](\u002Fdocs\u002Fkeys-auth) 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.\n\n**After**\n\nFor 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.\n\n### FIX-0710-15: the feedback author can reply to their own ticket again without the vibe:feedback scope\n\n**Before**\n\n[POST \u002Fv1\u002Ffeedback\u002F:id\u002Fcomments](\u002Fdocs\u002Fkeys-auth) 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.\n\n**After**\n\nThe 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`.\n\n### NEW-0710-16: AI quota pacing: pacing field in the response and 429 ai_pacing_limited error\n\nThe [GET \u002Fv1\u002Fai\u002Fquota](\u002Fdocs\u002Fai\u002Fconsumption\u002Fquota) 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 `\u002Fai` 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`\u002F`block`\u002F`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.\n\nWhen the daily or weekly limit trips, calls to [POST \u002Fv1\u002Fchat\u002Fcompletions](\u002Fdocs\u002Fai\u002Fchat\u002Fcompletions), [POST \u002Fv1\u002Fembeddings](\u002Fdocs\u002Fai\u002Fembeddings), and [POST \u002Fv1\u002Faudio\u002Ftranscriptions](\u002Fdocs\u002Fai\u002Faudio\u002Ftranscriptions) 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`\u002F`resetAt` will not help — the quota is not any more available in the meantime. Pacing is off by default — the platform enables it.\n\n### FIX-0710-17: GET \u002Fv1\u002Fpages, \u002Fv1\u002Fsites and POST \u002Fv1\u002F{pages,sites}\u002Fsearch now honour offset\n\n**Before**\n\nListing pages or sites with an offset (`GET \u002Fv1\u002Fpages?offset=50`, `POST \u002Fv1\u002Fpages\u002Fsearch` with `offset`) returned `422 BITRIX_ERROR \"Unknown parameter: start\"`. The first page (no `offset`) worked.\n\n**After**\n\n`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.\n\n### NEW-0710-18: \u002Ffields of documents, catalogs, prices, telephony lines, basket items and leads gain richer metadata\n\n`GET \u002F:entity\u002Ffields` of several entities now carries more complete schema metadata. Documents ([GET \u002Fv1\u002Fdocuments\u002Ffields](\u002Fdocs\u002Fentities\u002Fdocuments\u002Ffields)), catalogs ([GET \u002Fv1\u002Fcatalogs\u002Ffields](\u002Fdocs\u002Fentities\u002Fcatalogs\u002Ffields)), catalog prices ([GET \u002Fv1\u002Fcatalog-prices\u002Ffields](\u002Fdocs\u002Fentities\u002Fcatalog-prices\u002Ffields)) and telephony lines now expose a human-readable `label` and `description` per field, returned in English.\n\nFor basket items ([GET \u002Fv1\u002Fbasket-items\u002Ffields](\u002Fdocs\u002Fentities\u002Fbasket-items\u002Ffields)) the fields `weight`, `vatRate`, `measureCode`, `measureName`, `dimensions`, `productXmlId`, `catalogXmlId` are marked with a `nullable` flag — they may come back empty. For leads ([GET \u002Fv1\u002Fleads\u002Ffields](\u002Fdocs\u002Fentities\u002Fleads\u002Ffields)) the same flag marks `secondName`, `sourceDescription`, `comments`, and the previously-undeclared fields `originatorId`, `dateClosed`, `lastCommunicationTime` and the tags `utmSource`\u002F`utmMedium`\u002F`utmCampaign`\u002F`utmContent`\u002F`utmTerm` are now declared — filter and sort work on them, and `dateClosed` is normalized to ISO-8601.\n\nFor landing sites the group-by dimensions are declared, so [POST \u002Fv1\u002Fsites\u002Faggregate](\u002Fdocs\u002Fentities\u002Fsites\u002Faggregate) 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 \u002Fv1\u002Fdocuments\u002Faggregate](\u002Fdocs\u002Fentities\u002Fdocuments\u002Flist) returns `404`.\n\nExisting calls keep working unchanged — this is additional field metadata.\n\n### NEW-0710-19: Idempotent server creation — Idempotency-Key header\n\n[POST \u002Fv1\u002Finfra\u002Fservers](\u002Fdocs\u002Finfra\u002Fservers\u002Fcreate) 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.\n\nOn a replay the one-time SSH credentials (`ssh.privateKey` \u002F `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.\n\nNew 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).\n\nThe 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.\n\nAdditionally: 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.\n\n### NEW-0710-20: localized message on OPEN-mode switch denials\n\nResponses from [PATCH \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fmode](\u002Fdocs\u002Finfra\u002Faccess\u002Fmode) 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](\u002Fdocs\u002Finfra\u002Fdeploy) 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.\n\n### BC-0710-21: Open Channels config fields normalized to camelCase and described in \u002Ffields\n\n> Old format supported until: 10.01.2027\n\n**Before**\n\n[GET \u002Fv1\u002Fopenline-configs](\u002Fdocs\u002Fopenlines\u002Fconfig\u002Flist), [GET \u002Fv1\u002Fopenline-configs\u002F{id}](\u002Fdocs\u002Fopenlines\u002Fconfig\u002Fget) and [POST \u002Fv1\u002Fopenline-configs\u002Fsearch](\u002Fdocs\u002Fopenlines\u002Fconfig\u002Fsearch) returned most configuration fields in the Bitrix24-native form — upper case with underscores (`CRM_CREATE`, `WELCOME_MESSAGE`, `QUEUE_TIME`, and others). The [GET \u002Fv1\u002Fopenline-configs\u002Ffields](\u002Fdocs\u002Fopenlines\u002Fconfig\u002Ffields) reference described only 6 fields, so the rest were invisible to programmatic discovery.\n\n**After**\n\nAll configuration fields are normalized to a single camelCase form (`crmCreate`, `welcomeMessage`, `queueTime`, and so on), and `\u002Ffields` describes the full field set with human `label` and `description` values. Filtering and sorting by the new camelCase names work. On write (`create`\u002F`update`) both cases are still accepted — existing calls that send upper-case names in the body keep working.\n\n**What integrators should do**\n\nRead 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 `\u002Ffields` reference.\n\n### NEW-0710-22: Added GET \u002Fv1\u002Fwarehouses\u002Ffields — warehouse field schema\n\nA new [GET \u002Fv1\u002Fwarehouses\u002Ffields](\u002Fdocs\u002Fentities\u002Fwarehouses\u002Flist) 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.\n\n### FIX-0710-23: app publish recovers from a placement drift\n\n**Before**\n\nOn publish ([POST \u002Fv1\u002Fapps\u002F:id\u002Fpublish](\u002Fdocs\u002Fapps)) or a placement update ([PATCH \u002Fv1\u002Fapps\u002F:id](\u002Fdocs\u002Fapps)), 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.\n\n**After**\n\nOn 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.\n\n### FIX-0710-24: storage: object sha256 is populated on direct upload\n\n**Before**\n\nThe `sha256` field in the storage-object upload response was always `null` for user objects, even though the schema described it as \"computed on upload\".\n\n**After**\n\nFor direct upload ([POST \u002Fv1\u002Fstorage\u002Fobjects\u002Fupload](\u002Fdocs\u002Fstorage), 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.\n\n### FIX-0710-25: pacing.active in GET \u002Fv1\u002Fai\u002Fquota no longer reports an active limit when the quota is zero\n\n**Before**\n\nWith 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.\n\n**After**\n\n`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.\n",null,{},1794739470232]