# Изменения API: 10 сентября 2026

[← Журнал изменений](/docs/changelog) · [Сентябрь 2026](/docs/changelog/2026-09)

### BC-0910-1: сетевой сбой на пути к провайдеру приходит своим кодом ai_provider_network

> Поддержка старого формата до: не предусмотрена

**Было**

Когда платформа не могла соединиться с кластером моделей или соединение обрывалось до его ответа, [POST /v1/chat/completions](/docs/ai/chat/completions), [POST /v1/embeddings](/docs/ai/embeddings) и [POST /v1/audio/transcriptions](/docs/ai/audio/transcriptions) отвечали `502` с общим кодом `ai_provider_unavailable` — тем же, что и при ошибке, которой ответил сам кластер. По коду ответа два случая не различались.

**Стало**

Тот же ответ `502` несёт код `ai_provider_network`. Тип `server_error` и повторяемость не изменились. Поля `providerStatusCode` у этого ответа нет: кластер не ответил вовсе. Код `ai_provider_unavailable` остаётся за случаями, когда кластер ответил ошибкой. Исходный статус приходит в `providerStatusCode` всегда, когда кластер ответил HTTP-статусом, — в том числе когда поток уже открыт, а кластер отказал на его установке. Поля нет там, где статуса не было: ошибка пришла кадром из тела уже открытого потока (такой кадр несёт поле только с кодом `rate_limit_exceeded`) или сбой случился при обработке ответа кластера. В потоковом режиме тот же код приходит в кадре ошибки перед `data: [DONE]` вместе с полями `retryable` и `retryAfter`. Распознавание речи сообщает сетевой сбой тем же кодом `ai_provider_network`.

**Что делать интеграторам**

Клиент, который решает о повторе по статусу `502`, полю `retryable` или типу `server_error`, ничего не меняет. Клиент, который распознаёт сетевой сбой по коду `ai_provider_unavailable`, добавляет к проверке код `ai_provider_network`. Прежний код в этом сценарии с релиза не возвращается: переходного периода нет, потому что один и тот же ответ не может нести два кода.

### FIX-0910-2: ошибки AI-провайдера приходят с паузой из его заголовка и в одном формате для всех потоков

**Было**

Когда кластер моделей отвечал `429`, пауза для клиента бралась из текста ответа кластера, а без числа в тексте ставилась фиксированная в десять секунд. Заголовок `Retry-After` самого кластера при этом терялся. В потоковом режиме резервной модели Cowork/Code ошибка провайдера приходила кадром `{ "error": { "message": "fallback stream error", "type": "server_error" } }` без полей `code`, `retryable`, `retryAfter` и `providerStatusCode`, поэтому отказ кластера по лимиту был неотличим от его внутренней ошибки. Кадр `{ "error": … }`, который кластер присылает внутри уже открытого потока, уходил клиенту как обычный фрагмент ответа, а вызов записывался в журнал как успешный.

**Стало**

Пауза берётся из заголовка `Retry-After` кластера. Синхронный ответ `rate_limit_exceeded` несёт её в своём заголовке `Retry-After`, потоковый — в поле `retryAfter` кадра ошибки перед `data: [DONE]`. Число из текста ответа и значение по умолчанию используются только когда заголовка нет. Кадр ошибки потока резервной модели Cowork/Code собирается так же, как кадр основного потока: поля `code`, `type`, `retryable`, `retryAfter` и `providerStatusCode` присутствуют по тем же правилам. Кадр ошибки, пришедший внутри открытого потока, превращается в стандартный кадр ошибки платформы: ответ остаётся потоком, после уже переданных фрагментов приходит кадр с `code`, `type`, `retryable` и `retryAfter`, затем `data: [DONE]`, а вызов записывается в журнал как ошибка. Кадр, поднятый из тела открытого потока, несёт `providerStatusCode` только при коде `rate_limit_exceeded`: из тела платформа принимает лишь статус `429`, остальные ошибки из тела приходят кодом `ai_provider_unavailable` без этого поля. Отказ кластера HTTP-статусом на установке потока приходит кадром `ai_provider_unavailable` с `providerStatusCode`, как и синхронный ответ.

**Влияние на интеграторов**

Менять ничего не нужно. Клиент, который уже читает заголовок `Retry-After` и поле `retryAfter`, получает более точную паузу. Клиент потока резервной модели Cowork/Code может теперь различать отказы по `code` и `providerStatusCode` так же, как в основном потоке.

**Затронутые эндпоинты:** [POST /v1/chat/completions](/docs/ai/chat/completions), [POST /v1/embeddings](/docs/ai/embeddings), [POST /v1/audio/transcriptions](/docs/ai/audio/transcriptions).

### FIX-0910-3: MCP-инструменты отмечают отказы API флагом ошибки

**Было**

За пределами entity-инструментов ответ API с `success: false` мог возвращаться как MCP-результат без флага ошибки.

**Стало**

Такие ответы отмечаются `isError: true`, включая локальные отказы и сетевые ошибки с `success: false`. Текст JSON с диагностикой сохраняется. Успешные ответы не меняются, поле `isError` в них отсутствует.

**Влияние на интеграторов**

MCP-клиенты, учитывающие `isError`, теперь распознают эти ответы как ошибки инструментов, а не успешные вызовы. Интеграции, которые игнорируют этот флаг и проверяют `success` в тексте JSON, продолжают работать как прежде: формат и содержимое JSON не изменились.

### FIX-0910-4: сообщение об отказе QUEUE_TIMEOUT называет фактическое ожидание

**Было**

В отказе [429 QUEUE_TIMEOUT](/docs/errors/limits) поле `userMessage` называло 30 секунд ожидания в очереди к Битрикс24. Число было вписано в текст сообщения и не совпадало с ожиданием, которое платформа Вайбкод применяла на самом деле.

**Стало**

`userMessage` называет то ожидание, которое применилось к этому вызову. Ответ по-прежнему приходит с кодом `QUEUE_TIMEOUT`, полем `retryAfter` и заголовком `Retry-After` — срок повтора берётся из них, а не из текста.

**Влияние на интеграторов**

Менять в коде ничего не нужно: форма ответа не изменилась. Клиенту, который подбирал свой таймаут по этому сообщению, число теперь показывает реальный предел ожидания в очереди.

### NEW-0910-5: внешнее членство видно в V1: значение external у access.via и блок externalServers в /v1/me

Поле `access.via` в ответе `GET /v1/infra/servers/{id}` получило новое значение `external`
(наряду с `owner`, `collaborator` и `galaxy-reference`). Оно означает, что вызывающего
пригласили на этот сервер из ДРУГОГО аккаунта
Битрикс24: поверхность кода (деплой, exec, загрузка файлов, логи, чтение и скачивание
исходников, пробуждение) открыта и перечислена в `access.allowedEndpoints`, а кабинета
аккаунта-владельца у него нет вовсе. Текст `access._note` у такой строки свой: он говорит про
границу приглашения, а не про команду разработки, — прежний общий текст отправлял агента
искать интерфейс, которого ему не положено. Остальные значения — `owner`, `collaborator` и
`galaxy-reference` — не изменились.

`GET /v1/me` получил в блоке `data.infra` новую секцию `externalServers` рядом с
`collaboratorServers`. В ней перечислены серверы на чужих аккаунтах Битрикс24, куда владельца
ключа позвали внешним участником: `total`, `count` и `items` со строками `id`, `name`, `role`,
`companyPortalDomain` (аккаунт, которому принадлежит сервер) и `expiresAt` (`null` — срока нет);
`items` ограничен сотней строк, и `total` больше `count` означает, что список усечён. Секции
две, а не одна, потому что различается credential: обычный ключ до чужой машины не
дотягивается, у каждого внешнего членства свой ключ, который человек ведёт в собственном
аккаунте, в разделе «Внешний доступ». Ни та, ни другая секция в `infra.limits` не входит —
машины чужие.

Секция НЕОБЯЗАТЕЛЬНАЯ: ключ появляется в ответе, только если для аккаунта Битрикс24 компании,
которой принадлежит сервер, включена кросс-аккаунтная коллаборация и по нему есть хотя бы одно
живое внешнее членство. Отсутствие секции означает «ни одного не видно», а не «ни одного не
существует»: строка выпадает из ответа и тогда, когда коллаборацию выключили на стороне
компании-владельца, хотя членство физически ещё живо. Прямой запрос `GET /v1/infra/servers/{id}`
подтверждает доступ ИМЕННО СЕЙЧАС: ответ 200 означает, что членство живо И кросс-аккаунтная
коллаборация у компании-владельца включена. Ответ 403 `EXTERNAL_COLLABORATOR_NOT_A_MEMBER`
означает «доступа сейчас нет» и не различает две разные причины — членства нет или владелец
выключил коллаборацию: средствами клиента сегодня их не разделить.

Оба изменения аддитивные: прежние поля ответов не тронуты, и клиент, который о них не знает,
продолжает работать как раньше.

### FIX-0910-6: имя в X-Vibe-User-Name при открытии из каталога — из карточки сотрудника

**Было**

При открытии приложения из каталога платформы Вайбкод заголовок нёс имя учётной записи на платформе: у части сотрудников это логин, адрес почты или `Unknown`. При открытии того же приложения плиткой из Битрикс24 приходило имя из карточки сотрудника.

**Стало**

На обоих путях заголовок несёт имя из карточки сотрудника, сохранённое при авторизации приложения. Имени в сохранённых данных нет — цепочка прежняя: имя учётной записи, затем адрес почты, затем `Unknown`.

**Влияние на интеграторов**

Менять код не нужно. У части приложений значение заголовка изменится в сторону имени сотрудника. Имя остаётся отображаемым значением: как идентификатор учётной записи и основание для проверок доступа используйте `X-Vibe-User-Id`.

### FIX-0910-7: ответ модели, размещённой у стороннего провайдера, называет публичный id модели

**Было**

Для модели каталога, размещённой у стороннего OpenAI-совместимого провайдера под публичным id, поле `model` в ответе `POST /v1/chat/completions` и в каждом событии потока повторяло внутреннее имя модели у провайдера, а не запрошенный публичный id. Текст ошибки провайдера тоже называл внутреннее имя.

**Стало**

Поле `model` в ответе и в каждом событии потока равно публичному id модели, который клиент передал в запросе, — как у остальных моделей каталога. Текст ошибки провайдера называет тот же публичный id. Успешный ответ остаётся успешным, форма конверта не меняется.

**Влияние на интеграторов**

Клиент, который сверяет `model` в ответе с запрошенным id, теперь получает совпадение и для таких моделей. Интеграции, которая разбирала внутреннее имя модели из поля `model` или из текста ошибки, нужно перейти на публичный id: внутреннее имя больше не отдаётся.

### BC-0910-8: GET /v1/me отвечает 401 на удалённый ключ вместо 200

> Поддержка старого формата до: не предусмотрена

**Было**

[GET /v1/me](/docs/keys-auth/me) отдавал `KEY_NOT_FOUND` телом `{"success": false, "error": {"code": "KEY_NOT_FOUND", "message": "Key not found"}}` под статусом `200 OK`. Так отвечал вызов по ключу, удалённому во время обработки запроса. Отозванный ключ этой ошибки не получает — у него `KEY_INACTIVE`.

**Стало**

Тот же отказ приходит под статусом `401` с тем же телом. Форма тела не изменилась.

**Что делать интеграторам**

Клиенту, чья HTTP-библиотека сама бросает исключение на 4xx (`axios` с дефолтным `validateStatus`, `ky`, `got`, `ofetch`, Guzzle с `http_errors`), править код обязательно: до тела ответа он больше не доходит, вместо ветки по полю `success` срабатывает исключение. Обработайте `401` и выпустите новый ключ по нему.

Клиент, определявший успех по HTTP-статусу (`res.ok`, `response.raise_for_status()`), раньше принимал удалённый ключ за рабочий и разбирал тело отказа как ответ `/v1/me`. Теперь он получает `401` — проверьте, что ветка ошибки ведёт к выпуску нового ключа, а не к повтору того же запроса.

Клиент, который читает поле `success` из тела и на 4xx исключения не бросает (`fetch` без проверки статуса), работает без изменений.

### BC-0910-9: отказ Битрикс24 по учётным данным ключа приходит как 401, а не 422

> Поддержка старого формата до: не предусмотрена

**Было**

Когда Битрикс24 отклонял сами учётные данные, с которыми ключ обращается к порталу
(отозванный или переставший действовать вебхук), вызов отвечал `422 BITRIX_ERROR`, а код
портала приходил дополнительным полем `error.b24Code`. На этом статусе отказ выглядел как
ошибка данных запроса, поэтому клиенты повторяли вызов впустую.

**Стало**

Тот же отказ приходит как `401 PORTAL_CREDENTIALS_REJECTED` с подсказкой, что повтор не
поможет, пока ключ не переподключён. Код приходит на любом маршруте, читающем данные
портала, и в подошибке `POST /v1/batch`. Описание — [Авторизация, ключи и права](/docs/errors/auth).

**Что делать интеграторам**

Разбирающим `422 BITRIX_ERROR` с `error.b24Code` равным `authorization_error` или
`INVALID_CREDENTIALS` — перевести эту ветку на `401 PORTAL_CREDENTIALS_REJECTED` и вместо
повтора переподключать ключ в кабинете. Остальные значения
`error.b24Code` в `422 BITRIX_ERROR` не изменились.

### FIX-0910-10: операции перемещения и копирования на Диске появились в OpenAPI

**Было**

Пять живых операций Диска отсутствовали в `/v1/openapi.json` и в карточках справочника API. Четыре из них — перемещение и копирование файла и папки — были описаны на страницах документации, а получение содержимого корня хранилища не было описано нигде. Агент, который знакомится с платформой по машинной схеме, делал из этого вывод, что таких методов нет. Отдельно врала карточка скачивания файла: её заголовок и описание обещали возврат ссылки, тогда как метод отдаёт байты.

**Стало**

В OpenAPI и в карточках справочника описаны [POST /v1/storages/{id}/children](/docs/entities/storages), [POST /v1/files/{id}/moveto](/docs/entities/files/moveto), [POST /v1/files/{id}/copyto](/docs/entities/files/copyto), [POST /v1/folders/{id}/moveto](/docs/entities/folders/moveto) и [POST /v1/folders/{id}/copyto](/docs/entities/folders/copyto) — со скоупом `disk`, телом запроса и полным набором кодов отказа. Операция [GET /v1/files/{fileId}/download](/docs/entities/files/download) объявляет двоичный ответ и описана как поток байтов, а не как ссылка. Поведение методов не менялось, успешный ответ остаётся HTTP 200, прежние статусы и форматы ошибок сохранены.

### BC-0910-11: пользовательский AI-провайдер больше не проходит по редиректу

> Поддержка старого формата до: не предусмотрена

**Было**

Если базовый адрес пользовательского провайдера `custom-openai-compat` отвечал HTTP-редиректом (301, 302, 303, 307 или 308), платформа переходила по нему и выполняла запрос по новому адресу. Так работали проверка ключа, загрузка каталога моделей и вызовы моделей.

**Стало**

Платформа по редиректу не переходит и возвращает отказ.

- [POST /v1/ai/credentials](/docs/ai/credentials/create) и [PATCH /v1/ai/credentials/:id](/docs/ai/credentials/update) отвечают `400 base_url_redirect`.
- [POST /v1/ai/credentials/:id/test](/docs/ai/credentials/test) по-прежнему отвечает HTTP 200 с `valid: false`, а в `data` появляется поле `code` со значением `base_url_redirect`.
- [POST /v1/ai/credentials/:id/fetch-models](/docs/ai/credentials/fetch-models) отвечает `400 base_url_redirect` вместо ответа с `PROVIDER_LIST_MODELS_UNAVAILABLE`.
- [POST /v1/chat/completions](/docs/ai/chat/completions) с моделью такого провайдера отвечает `400 ai_provider_redirect_blocked`, в потоковом режиме приходит кадр ошибки с тем же кодом. Повтор запроса не поможет.

**Что делать интеграторам**

Укажите в `baseUrl` конечный адрес API, на который ведёт редирект. Частый случай — `https://` вместо `http://`. Уже сохранённый ключ обновите через [PATCH /v1/ai/credentials/:id](/docs/ai/credentials/update).

### BC-0910-12: списочные ручки задач, телефонии и универсальных списков больше не игнорируют filter молча

> Поддержка старого формата до: не предусмотрена

**Было**

Часть списочных ручек отвечала `200` и выборкой шире запрошенной, когда в запросе был параметр
`filter`. Отличить применённый фильтр от отброшенного по ответу было нельзя.

Первая причина — две формы записи `filter` пишутся разборщиком строки запроса в одно и то же место,
поэтому вторая замещает первую. Запрос `?filter[AUTHOR_ID]=1&filter=` оставлял пустое значение, оно
читалось как «фильтра нет», и в Битрикс24 уезжал вызов без фильтра:
[GET /v1/tasks/{taskId}/comments](/docs/entities/task-comments/list),
[GET /v1/calls/statistics](/docs/telephony/analytics/statistics),
[GET /v1/lists/{iblockId}/sections](/docs/lists/sections/list) и
[GET /v1/lists/{iblockId}/elements](/docs/lists/elements/list). По той же причине терялось условие,
записанное скобками глубже двух уровней (`?filter[a][b][c]=1`): до параметра `filter` оно не
доходило вовсе.

Вторая причина — ручки, у которых фильтра нет вовсе, параметр не читали и не отклоняли:
[GET /v1/tasks/{taskId}/history](/docs/task-history),
[GET /v1/tasks/stages/{entityId}](/docs/task-stages),
[GET /v1/tasks/{taskId}/checklist](/docs/entities/tasks/checklist/list),
[GET /v1/lists](/docs/lists/lists/list) и
[GET /v1/voximplant-lines](/docs/telephony/lines/voximplant).

**Стало**

Конверт `filter`, из которого хотя бы одно написанное вами условие не доехало до параметра,
отклоняется с `400` и кодом `INVALID_FILTER` — до обращения к Битрикс24. Условие теряется, когда
две формы смешаны в одном запросе (в любом порядке), когда два написания адресуют одно и то же
условие (`?filter[a]=1&filter[a][b]=2`) и когда скобок больше двух уровней — разборщик строки
запроса такое не собирает. Текст ошибки называет, что именно произошло. Собирайте фильтр в одной
форме, каждое условие пишите один раз и не глубже двух уровней.

Ручки, у которых фильтра нет, отвечают `400` с кодом `UNSUPPORTED_FILTER` и называют, чем
пользоваться вместо него: `field` для истории задачи, `sort` и `offset` для списка универсальных
списков.

Пустое значение `?filter=` по-прежнему означает «без фильтра», и ответ остаётся `200`.
Не затронуты **на ручках, которые фильтр поддерживают**: фильтр одной формы, у которого все
скобочные условия дошли до параметра — целиком скобочный или один объект JSON, — именованные
параметры `field`, `sort`, `start`, а также повторный `?filter=a&filter=b`, у которого разборщик
оставляет последнее значение — при условии, что оно НЕ пустое. Если последним стоит пустой
`?filter=`, он затирает предыдущее условие, и такой запрос отклоняется.

Две оговорки, чтобы список читался честно. На ручках, у которых фильтра нет вовсе, отклоняется
**любой** значимый `filter`, включая одноформенный. И запрос, где скобочная форма стоит ПОСЛЕ
пустого `?filter=` (`?filter=&filter[ID]=1`), раньше отвечал `200` с корректно применённым
фильтром, а теперь тоже отклоняется: имена параметров в строке запроса не несут значений, отличить
пустую запись от непустой нельзя, поэтому отклоняются обе — как это давно сделано для списочных
запросов сущностей.

**Что делать интеграторам**

1. Соберите фильтр в ОДНОЙ форме: либо целиком скобочной (`?filter[ID]=1&filter[STAGE]=NEW`), либо
   одним объектом JSON (`?filter={"ID":1,"STAGE":"NEW"}`) — там, где ручка обе формы принимает.
   ⚠️ [GET /v1/calls/statistics](/docs/telephony/analytics/statistics) принимает ТОЛЬКО скобочную
   форму: объект JSON уходит в Битрикс24 строкой и фильтром не становится. Не отправляйте пустой
   `?filter=` рядом со скобочными условиями — даже если раньше такой запрос работал.
2. Уберите скобочные условия глубже двух уровней: разборщик строки запроса их не собирает, и до
   Битрикс24 они не доходили и раньше.
3. Для ручек, у которых фильтра нет вовсе, перейдите на именованные параметры — их называет текст
   ошибки: `field` для истории задачи, `sort` и `offset` для списка универсальных списков.

Поддержка старого поведения не предусмотрена: половину потерянных условий восстановить нельзя, а
отвечать `200` на запрос, фильтр которого не применён, — это и есть исправляемый дефект.

### FIX-0910-13: два написания одного поля в фильтре больше не теряют условие молча

**Было**

У поля есть несколько принимаемых написаний: имя из схемы и родное имя Битрикс24 (`amount` и
`OPPORTUNITY` у сделок), разный регистр. Если два условия в одном фильтре сводились к одному
имени фильтра Битрикс24, запрос **из-за ошибки** отвечал `200` с потерянным условием:
применялось только одно из двух, а какое именно — решал порядок ключей в запросе. Выборка
выглядела отфильтрованной, хотя половина отбора не применялась. Полагаться на этот ответ было
нельзя: он и не был обещан документацией. То же происходило с двумя написаниями одного
оператора, с парными именами полей даты (`updatedAt`/`updatedTime`, `createdAt`/`createdTime`, где
у сущности объявлено одно из двух, а второе принимается как псевдоним) и с парами синонимов,
объявленных у сущности отдельными именами: у лидов это `amount`/`opportunity`, `stageId`/`statusId`,
`currency`/`currencyId`.

**Стало**

Такой запрос отклоняется с `400 INVALID_DUPLICATE_FILTER_FIELD`, и в `message` названы оба
условия и общее имя, к которому они свелись. Запрос с одним написанием поля отвечает HTTP 200
как раньше — успешный ответ сохранён. Операторы, дающие РАЗНЫЕ имена, тоже не изменились:
`{ "amount": { "$gte": 1000, "$lte": 5000 } }` — это диапазон, а не дубль.

Решает результат, а не написание, и у двух пар границы РАЗНЫЕ. `UF_`-форма вместе с
camelCase-написанием того же пользовательского поля складывается в одно имя только у сущностей
со старым стилем именования И только для того написания, которое платформа переводит:
чисто буквенное (`ufCrmProjectCode`) — на всех таких сущностях, а с цифровым хвостом
(`ufCrm_1698325419`) — только у реквизитов. На остальных сущностях цифровая пара уезжает двумя
именами, отказа не даёт, и второе условие по-прежнему молча отбрасывает Битрикс24. У сущностей с camelCase-именами
и у элементов CRM это разные имена на проводе, и отказа тоже нет. `$ne` вместе с `$nin` на одном поле дают один префикс `!` у ВСЕХ
сущностей, кроме элементов CRM, — включая сущности с camelCase-именами (товары каталога,
почтовые ящики, смарт-процессы); у элементов CRM `$nin` получает отдельный префикс `!@`, и пара
проходит.

Правило действует на всех списках, поиске, агрегации и в обоих пакетных вызовах; в пакетном
вызове отклоняется только сам вызов, соседние выполняются.

**Затронутые эндпоинты:** `GET /v1/{entity}` и [POST /v1/{entity}/search](/docs/filtering#эндпоинт-поиска)
сущностей со стандартным разбором фильтра, `POST /v1/{entity}/aggregate` и его legacy-двойник
`GET /v1/{entity}/aggregate`, [POST /v1/batch](/docs/batch) и подвызовы `POST /v1/{entity}/batch`,
а также `GET /v1/addresses` с `POST /v1/addresses/search`. На этих дверях фильтр разбирает один и
тот же код, поэтому отказ приходит одинаково.

Отдельные маршруты со своим форматом фильтра разбирают его собственным кодом и в это изменение не
входят: там пара написаний по-прежнему отвечает `200` с потерянным условием. Среди прочих —
`GET /v1/requisite-links` с `POST /v1/requisite-links/search`, статистика звонков, списки,
комментарии задач и открытые линии; перечень не закрытый.

Почему это исправление, а не смена контракта: прежний `200` на таком запросе не был обещан
документацией и не был воспроизводимым — он применял одно из двух условий, а какое именно,
решал порядок ключей. Полагаться на такой ответ было нельзя, поэтому окна поддержки прежнего
поведения нет: поддерживать нечего. Запрос с одним написанием поля не затронут.

### NEW-0910-15: компании отдают связанные контакты через `include=contact`

Справочник компаний объявляет связь `contact`, и параметр `include=contact` теперь принимается на `GET /v1/companies/{id}`, `GET /v1/companies` и `POST /v1/companies/search`. В каждой записи появляется `_included.contacts` — массив полных карточек контактов, к каждой из которых добавлены поля привязки `sort`, `isPrimary`, `roleId`; у компании без привязанных контактов это пустой массив. Раньше такой запрос отвечал `400 INVALID_INCLUDE`, потому что у компаний не было объявлено ни одной связи и список доступных значений `include` был пуст.

Вместе со связью открылись подмаршруты привязок: `GET /v1/companies/{id}/contacts` — список привязок, `POST /v1/companies/{id}/contacts` — добавить одну, `PUT /v1/companies/{id}/contacts` — заменить весь набор, `DELETE /v1/companies/{id}/contacts/{contactId}` — снять одну. Запросы без `include` отвечают как прежде.

### FIX-0910-16: булев флаг пользовательского поля больше не теряется молча

**Было**

Свойства пользовательского поля `multiple`, `mandatory`, `showFilter`, `showInList`,
`editInList`, `isSearchable` Битрикс24 хранит символами `Y` и `N`. Присланные булевыми, они
применялись через раз — и это в пределах ОДНОГО тела запроса: `showInList: true` и
`editInList: true` включались, а `showFilter: true` и `isSearchable: true` молча
сохранялись выключенными. Ответ при этом приходил успешный: `201` на создании,
`{"updated": true}` на изменении. Повторная отправка тех же флагов булевыми ничего не меняла:
выключенные оставались выключенными.

Ни один из шести флагов не был объявлен в описании API: у создания перечислялись только
`fieldName`, `userTypeId` и `label`, у изменения тело описывалось пустым объектом. Узнать
рабочую форму значения было неоткуда.

**Стало**

Все шесть флагов принимаются и булевыми, и строками `"Y"` / `"N"` — платформа приводит обе
формы перед вызовом портала. Каноническими формами остаются булев и `"Y"` / `"N"`; вдобавок
платформа принимает снисходительные написания: `"y"`, `1`, `"1"` включают, а `"n"`, `0`, `"0"`
выключают. Прежде все они доезжали до портала как есть — и **включающие** флаг не включали, а
теперь включат; выключающие как выключали, так и выключают, для них не меняется ничего.
Нераспознанные значения проходят как прежде, без изменений.

**Обратите внимание, если ваша интеграция уже шлёт эти флаги булевыми.** Прежде часть из них
не срабатывала, и вы могли к этому привыкнуть — теперь сработают все. Замерены два
перещёлкивания: `showFilter: true` и `isSearchable: true` прежде оставляли флаг выключенным,
а теперь включат его. Включение `isSearchable` не косметика: значения поля уезжают в
полнотекстовый поиск портала и начинают всплывать в общем поиске.

Следите и за `mandatory: true` — включённым он блокирует создание записей без этого поля, в
том числе через другие интеграции портала. Срабатывал ли он от булевого прежде, мы не
замеряли, поэтому исходите из того, что теперь сработает.

Если ваша интеграция шлёт `showFilter: "E"` — прежняя таблица страниц по фиксированным
типам CRM называла это «маской» —
поведение изменится: раньше такая запись оставляла фильтр ВЫКЛЮЧЕННЫМ, теперь она его
включает. Среди ЗАМЕРЕННЫХ строковых значений это единственное перещёлкивание, и сделано оно нарочно:
`"E"` — форма, в которой фильтр приходит на чтении, и возвращать её обратно должно быть
безопасно.

Если ваша интеграция шлёт `"I"` или `"S"` по нашей прежней таблице — такая запись фильтр не
включает ни прежде, ни теперь. Отправьте `true`, чтобы его включить.

Проверьте, что шлёте, до обновления.

Флаги объявлены в описании API у создания и у изменения. Отдельно описана особенность
чтения `showFilter`: включённый фильтр возвращается как `E` — это форма хранения Битрикс24,
а не ошибка. Значения `"I"` и `"S"` при записи не принимаются и фильтр выключают,
поэтому включать его нужно значением `true`, `"Y"` или прочитанным `"E"`. Справочник по фиксированным типам CRM
прежде описывал эти буквы как режимы фильтра и приводил их в примерах — замер на четырёх
типах поля этого не подтвердил, страницы исправлены.

Отдельно закрыт круговой рейс: прочитанное `"E"` теперь можно отправить обратно как есть —
платформа понимает его как «включён». Прежде обычный цикл «прочитал поле → поменял одно
свойство → отправил объект целиком» возвращал порталу прочитанное `"E"`, и фильтр молча
выключался под успешным ответом.

Правка касается фиксированных типов CRM — сделок, лидов, контактов, компаний, предложений и
реквизитов. Пользовательские поля смарт-процессов идут другим контрактом Битрикс24 и этой
правкой не затронуты.

### FIX-0910-17: Ответы V1 возвращают X-Request-Id

**Было**

Документация просила приложить `X-Request-Id` к обращению в поддержку, но ответы API не содержали этот заголовок.

**Стало**

Каждый ответ `/v1`, сформированный backend, содержит серверный `X-Request-Id`. Значение из проблемного ответа можно приложить к обращению вместе со временем запроса.

**Влияние на интеграторов**

Существующие запросы менять не нужно. Клиент может сохранять новый заголовок для диагностики, но не обязан его обрабатывать.
