[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"docs-changelog\u002F2026-06-29":3,"docs-tabs-changelog\u002F2026-06-29":6},{"content":4,"lastmod":5},"# API changes: June 29, 2026\n\n[← Changelog](\u002Fdocs\u002Fchangelog) · [June 2026](\u002Fdocs\u002Fchangelog\u002F2026-06)\n\n### FIX-0629-1: org structure node search now searches by name\n\n**Before**\n\n[POST \u002Fv1\u002Fhumanresources\u002Fnodes\u002Fsearch](\u002Fdocs\u002Fhumanresources\u002Fnodes\u002Fsearch) proxied to `humanresources.node.list`: the node type was set inside `filter`, there was no name search at all, and a top-level `{ \"type\": ..., \"name\": ... }` body (or a request with no body) returned `400` or `500`.\n\n**After**\n\nThe endpoint now wraps `humanresources.node.search`. Two fields are required at the top level of the body — `type` (`DEPARTMENT` or `TEAM`) and `name` (a substring of the name). Optional are `parentId` and `pagination.limit` (default 50, maximum 200). It returns nodes whose name contains `name`, in a flat `data` with `meta` (`total`, `hasMore`). The `filter`, `order` and `select` fields are no longer accepted.\n\n**Integrator impact**\n\nSend `{ \"type\": \"TEAM\", \"name\": \"\u003Csubstring>\" }` at the top level instead of the former `{ \"filter\": { \"type\": \"TEAM\" } }`. To enumerate all nodes of a type without name search, use [GET \u002Fv1\u002Fhumanresources\u002Fnodes](\u002Fdocs\u002Fhumanresources\u002Fnodes\u002Flist) with `?type=...`.\n\n### NEW-0629-2: AI quota off-peak hours schedule\n\nAdded the `GET \u002Fv1\u002Foff-peak` endpoint — the off-peak (Time-of-Use) discount schedule for the AI quota. The response carries the price multiplier right now (`currentMultiplier`), the next window when it gets cheaper (`nextWindow`), a 24×7 grid by hour and weekday (`grid`), the current grid cell (`nowCell`), and the schedule timezone (`timezone`). The discount applies to quota-metered usage only — the quota drains slower during these hours; wallet pay-per-token charges are unaffected. The optional `model=\u003Cid>` parameter returns a specific model's schedule instead of the platform default. Requires the `vibe:ai` scope. While off-peak is not enabled, the response is `{ \"enabled\": false }`.\n\n### BC-0629-3: vibe-search provider slug removed\n\n> Old format supported until: 26.12.2026\n\n**Before**\n\nThe `provider` field in [POST \u002Fv1\u002Fsearch](\u002Fdocs\u002Fsearch\u002Frun) and [POST \u002Fv1\u002Fresearch](\u002Fdocs\u002Fsearch\u002Fresearch) accepted the `vibe-search` slug — a separate platform engine added on 2026-06-06. It was also listed among the slugs in [GET \u002Fv1\u002Fsearch\u002Fproviders](\u002Fdocs\u002Fsearch\u002Fproviders).\n\n**After**\n\nThe `vibe-search` slug is removed. The platform search engine on every instance is `bitrix-search` — which upstream backs it is instance-dependent. A request with `provider: \"vibe-search\"` now returns `400 INVALID_REQUEST` (the value fails validation). research support for `bitrix-search` is likewise instance-dependent — see [GET \u002Fv1\u002Fsearch\u002Fproviders](\u002Fdocs\u002Fsearch\u002Fproviders).\n\n**What integrators should do**\n\nIf your request explicitly passed `provider: \"vibe-search\"`, replace it with `bitrix-search` or omit the `provider` field to use the instance default engine (shown by the `defaultProvider` field in [GET \u002Fv1\u002Fme](\u002Fdocs\u002Fkeys-auth)). The `vibe-search` slug was not the default engine on any production instance, so only integrations that hardcoded it are affected.\n\n### FIX-0629-4: A null field value via POST \u002Fv1\u002Fbatch no longer writes the string \"null\" into the field\n\n**Before**\n\nIn a composite `POST \u002Fv1\u002Fbatch`, a create or update with a field value of `null` (for example `{\"entity\":\"deals\",\"action\":\"update\",\"entityId\":123,\"params\":{\"comments\":null}}`) wrote the **literal string `\"null\"`** into the field.\n\n**After**\n\nThe field receives an empty value, which Bitrix24 interprets by field type: text is cleared, numeric becomes `0`, a date is left unchanged. The literal string `\"null\"` is no longer written and no error is raised. This matches the behavior of a single `PATCH \u002Fv1\u002F{entity}\u002F:id` with `null`. The per-entity `\u002Fv1\u002F{entity}\u002Fbatch` path still skips a `null` field entirely (leaves the value unchanged for every type).\n\n### FIX-0629-5: Search and list with a null filter now return more than 50 rows\n\n**Before**\n\nA `POST \u002Fv1\u002F{entity}\u002Fsearch` or `GET \u002Fv1\u002F{entity}` request with a filter on an empty value (for example `{\"filter\": {\"closedDate\": null}}`) and a `limit` above 50 returned at most 50 records, even though `meta.total` reported the real match count and `meta.hasMore` was `true`. Auto-pagination silently stopped after the first page, so the common \"read while rows equal `limit`\" loop got an incomplete result with no error.\n\n**After**\n\nSuch a request now returns up to `limit` records, the same as with any other filter. A `null` filter value is treated as \"field is empty\" consistently across every page of the result set.\n\n**Impact on integrators**\n\nClients that paged manually via `offset` in steps of 50 to work around the truncation no longer need to — up to 5000 records can be fetched in a single call.\n\n### FIX-0629-6: port change and deploy auto-routing now work out of the box on new app servers\n\n**Before**\n\nA regular \"Publish app\" server booted its agent with a fixed port, so `PATCH \u002Fv1\u002Finfra\u002Fservers\u002F:id\u002Fport` and the deploy auto-routing step returned `409 PORT_NOT_APPLIED` (`NO_SCANNER`), and the public URL served the Black Hole service page while the app listened on a non-default port.\n\n**After**\n\nNew regular app servers boot with port auto-detection: a service on any port is reachable through the tunnel immediately, and port change plus deploy auto-routing succeed. Agent and galaxy-host servers are unchanged.\n\n### FIX-0629-7: app install via \u002Fv1\u002Fapps returns a precise error code instead of the generic BOX_APP_INSTALL_FAILED\n\n**Before**\n\nOn an OAuth application install failure, [POST \u002Fv1\u002Fapps](\u002Fdocs\u002Fapps\u002Fcreate) always returned `502 BOX_APP_INSTALL_FAILED`, with the full raw Bitrix24 response echoed into `error.message`.\n\n**After**\n\nThe failure response is now classified: `403 B24_INSUFFICIENT_SCOPE` (the service integration lost its rights on the portal), `410 STALE_DEVELOPER_KEY` (access was changed or removed and cannot be auto-recovered), `502 RECOVERY_FAILED` (transient, retryable) or `502 DEVKEY_MINT_FAILED` (other). `error.message` no longer carries the raw Bitrix24 body — the diagnostic moves to the redacted `error.details.b24Body` field.\n\n**Impact**\n\nExisting \"non-201 means install failed\" handling keeps working unchanged. If your code branched specifically on `BOX_APP_INSTALL_FAILED`, add handling for the new codes above.\n\n### FIX-0629-8: date-range search no longer returns empty for wide ranges\n\n**Before**\n\n`POST \u002Fv1\u002Fdeals\u002Fsearch` (and likewise for leads, contacts, companies, quotes, invoices, items) with a date filter and a lower bound (`>=` \u002F `>`) spanning more than 14 days returned `200` with an empty `data` and `meta.total: 0`, even when records existed in that range.\n\n**After**\n\nThe request returns all matching records. `GET \u002Fv1\u002Fdeals`, narrow ranges (≤ 14 days), and the `autoWindow: false` parameter were unaffected.\n\n### NEW-0629-9: New error code CONNECTOR_APP_INSTALL_FORBIDDEN on app install\n\n[POST \u002Fv1\u002Fapps](\u002Fdocs\u002Fapps) on a self-hosted portal now returns `403` with code `CONNECTOR_APP_INSTALL_FORBIDDEN` when the Bitrix24 account administrator has forbidden the user from installing applications. The `error.message` field carries a clear localized explanation pointing the user to ask their Bitrix24 account administrator. Previously this denial surfaced as a generic `502 CONNECTOR_APP_INSTALL_FAILED` with no cause; that code is still used for other install failures.\n\n### NEW-0629-10: Transfer bot ownership to another key\n\nAdded [POST \u002Fv1\u002Fbots\u002F:botId\u002Ftransfer](\u002Fdocs\u002Fbots\u002Fmanagement\u002Ftransfer) — moves a bot's ownership to another API key of the same Bitrix24 account and the same user (or an account admin). Resolves the case where a bot is orphaned after the app is recreated: the owning key is revoked and the bot's runtime stops working. Body: `{ \"targetApiKeyId\": \"\u003Cid>\" }`. The target key must be active, in the same account, with the `imbot` scope. After the transfer, verify the new key's B24 binding via `POST \u002Fv1\u002Fbots\u002F:botId\u002Freauth`.\n",null,{},1794739467935]