# Изменения API: 4 августа 2026

[← Журнал изменений](/docs/changelog) · [Август 2026](/docs/changelog/2026-08)

### BC-0804-1: V1 /wake и /start будят galaxy-приложение через хост

> Поддержка старого формата до: 22.08.2026

**Было**

`POST /v1/infra/servers/:id/wake` и `/start` для `kind=GALAXY_APP` отвечали **422 `VM_MISSING`** (у приложения нет cloud VM) и советовали редеплой, даже когда контейнер просто спал после idle. Периодические задачи после первого сна не могли подняться через API.

**Стало**

Для размещённого galaxy-приложения оба verb'а будят приложение через хост: будят общий хост при необходимости и стартуют контейнер. Успех — **HTTP 200**. `?wait=true` на galaxy **не** ждёт RUNNING до таймаута / `WAKE_TIMEOUT` — после cold wake приложение может остаться SLEEPING; опрашивайте GET. Хост с `preventWake` блокирует **и** `/wake`, **и** `/start` (**403 `SERVER_WAKE_BLOCKED`** или **402** paywall-коды `MARKETPLACE_REQUIRED` / … — не маркеры `TRIAL_EXPIRED`). Слот без host/`galaxyId` → **404 `GALAXY_HOST_NOT_FOUND`** (не 422 `VM_MISSING`). Для STANDALONE `VM_MISSING` / override `/start` без изменений.

### BC-0804-2: тип архива при сохранении версии сверяется с его первыми байтами

> Поддержка старого формата до: 03.08.2026

**Было**

Сохранение версии исходников — [POST /v1/infra/servers/:id/sources](/docs/source-storage) и [POST /v1/apps/:id/sources](/docs/source-storage) — верило заголовку `Content-Type` на слово. Архив zip, отправленный с `Content-Type: application/gzip`, принимался, сохранялся как gzip и получал имя с расширением `.tar.gz`; выкладка такой версии на сервер падала на распаковке с невнятной ошибкой чтения архива. Автосохранение версии после выкладки записывало формат как gzip всегда, независимо от того, что было в теле запроса.

**Стало**

При приёме читаются первые байты архива. Прямое противоречие между заявленным типом и содержимым — заявлен `application/gzip`, а байты zip, или наоборот — отвергается с кодом `415 UNSUPPORTED_ARCHIVE_FORMAT`; тело ответа содержит `error.hint.declared` и `error.hint.detected`. Типы `application/x-tar` и `application/octet-stream` под этот отказ не попадают никогда: их сигнатура не читается в первых восьми байтах, поэтому противоречие с ними установить нечем. Нераспознанное содержимое принимается как раньше.

Автосохранение после выкладки отказа не даёт вообще: там тип архива клиент не заявляет, и распознанные байты просто записываются честно — zip сохраняется как zip и при повторной выкладке уходит в нужный распаковщик.

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

Отправляйте `Content-Type`, соответствующий архиву (`application/gzip` для tar.gz, `application/zip` для zip), либо `application/octet-stream`, если тип неизвестен. Клиенты, которые уже отправляют корректный заголовок, ничего не меняют.

### NEW-0804-3: названия и описания полей у разделов товаров, шаблонов реквизитов и банковских реквизитов

**Что нового**

`GET /v1/product-sections/fields`, `GET /v1/requisite-presets/fields` и `GET /v1/bank-details/fields` возвращали только `type` и `readonly` — ни одно поле не имело человекочитаемого названия, поэтому по справочнику нельзя было понять, что означает, например, `rqAccNum`. Теперь названия объявлены у всех полей: 7 у разделов товаров, 11 у шаблонов реквизитов, 34 у банковских реквизитов. У шаблонов реквизитов и банковских реквизитов вместе с названием приходит и описание.

Ответ дополняется ключами `label` и `description` рядом с уже существующими `type` и `readonly` — прежние поля ответа не изменились.

Динамический справочник Битрикс24 эти названия дать не мог: он дополняет только те поля, которых нет в статической схеме, поэтому у объявленных полей его подписи отбрасывались. Операция `aggregate` у банковских реквизитов остаётся отключённой.

### NEW-0804-4: `limit=0` больше не игнорируется молча

**Что нового**

`limit=0` не является размером страницы: параметр отбрасывается, и применяется значение по умолчанию. Раньше это происходило без единого признака — ответ приходил с кодом 200 и полной страницей записей, как будто параметр учли. Теперь в такой ответ добавляется предупреждение в `meta.warnings`:

```json
{
  "meta": {
    "warnings": [
      {
        "code": "LIMIT_ZERO_IGNORED",
        "field": "limit",
        "message": "limit=0 is not a page size and was ignored. Valid range: 1..5000; pass an explicit limit (e.g. 5000) to read the whole collection."
      }
    ]
  }
}
```

Работает на `GET /v1/{entity}` и `POST /v1/{entity}/search` для всех сущностей. Само значение не изменилось — прежние вызовы продолжают возвращать столько же записей, сколько и раньше; чтобы прочитать всю коллекцию, передайте явный `limit` (максимум 5000).

### FIX-0804-5: банковские реквизиты: `entityTypeId` помечен как невозвращаемое поле

**Было**

`GET /v1/bank-details/fields` описывал `entityTypeId` обычным числовым полем — читаемым и записываемым. Про то, что Битрикс24 это значение принимает при создании, но никогда не возвращает при чтении, было сказано только в тексте описания поля. Клиент, который строит модель по машиночитаемому справочнику, а не по прозе, добавлял поле в тип чтения, а на месте числа получал пустоту.

**Стало**

У поля появился признак `notReturned: true` — в `GET /v1/bank-details/fields`, в `GET /v1/guide` и в OpenAPI-схеме (там это аннотация `x-notReturned` и фраза в описании). Тот же признак этот выпуск ввёл для `serverName` у телефонных линий.

Поле остаётся доступным для записи: `POST /v1/bank-details` по-прежнему принимает `entityTypeId` (всегда 8 — владелец-реквизит). Признак говорит только про чтение.

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

Ничего менять не нужно — признак аддитивен. Если вы генерируете типы по справочнику, `entityTypeId` можно исключить из модели чтения и оставить в модели создания.

### FIX-0804-6: discover называет настоящий идентификатор в пути

**Было**

`GET /v1/guide` и OpenAPI-схема показывали путь одиночной записи как `/:id` у всех сущностей. У четырёх это неправда: `smart-processes` адресуется публичным `entityTypeId` (1030+), `telephony-lines` — номером линии, а `bizproc-robots` и `bizproc-activities` — кодом (`code`), причём собственного `id` у них нет вовсе. Клиент, прочитавший `{id}`, подставлял собственное поле `id` записи и получал `404 SMART_PROCESS_NOT_FOUND`, где то же значение названо `entityTypeId` — притом что опубликованная документация уже писала `:entityTypeId` и `:code`.

**Стало**

`GET /v1/guide` показывает `/v1/smart-processes/:entityTypeId`, `/v1/telephony-lines/:number`, `/v1/bizproc-robots/:code` и `/v1/bizproc-activities/:code`; у остальных сущностей путь остался `/:id`. В OpenAPI имя параметра осталось `id`: имя path-параметра обязано совпадать с плейсхолдером в шаблоне пути, а сам адрес не менялся — вместо этого описание параметра теперь называет настоящий идентификатор. Там, где у сущности нет собственного поля `id` (телефонные линии и оба bizproc-справочника), описание так и говорит, а не отправляет сверяться с полем, которого не существует. Описание поля `id` у `smart-processes` тоже уточнено.

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

Ничего менять не нужно: маршруты и коды ответов не изменились, изменились только описания. Если вы подставляли в путь `smart-processes` внутренний `id` записи, теперь понятно, почему приходил 404 — используйте `entityTypeId`; для роботов и действий бизнес-процессов — `code`.

### FIX-0804-7: телефонные линии: `name` объявлен nullable, `serverName` помечен как невозвращаемый

**Было**

`GET /v1/telephony-lines/fields` объявлял `name` обычной строкой, хотя у линии без названия приходит `null` — модель, построенная по справочнику, ломалась на первом же таком значении. Поле `serverName` выглядело в справочнике обычным читаемым полем, хотя Битрикс24 его не хранит и никогда не возвращает.

**Стало**

У `name` появился признак `nullable: true` — и в `GET /v1/telephony-lines/fields`, и в `GET /v1/guide`, и в OpenAPI-схеме (там это форма `type: ["string","null"]`). У `serverName` появился признак `notReturned: true` в тех же местах, а в OpenAPI — аннотация `x-notReturned` и фраза в описании. Поле осталось в справочнике намеренно: запись в него по-прежнему отклоняется с `400 READONLY_FIELD`, и клиент должен иметь возможность найти поле и прочитать причину.

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

Ничего менять не нужно — признаки аддитивны. Если вы генерируете типы по справочнику, `name` станет `string | null`, а `serverName` можно исключить из модели чтения.

### FIX-0804-8: рабочие группы: работают `limit > 50` и точное смещение

**Было**

`GET /v1/workgroups?limit=500` возвращал первые 50 записей независимо от того, сколько групп доступно, и `meta.hasMore` не помогал дочитать остальное. Причина — метод списка у этой сущности называется не `.list`, а `sonet_group.get`, и признак «это список» у неё не был объявлен, поэтому ни `limit`, ни авто-пагинация до Битрикс24 не доходили. Смещение при этом округлялось вниз до границы страницы: `offset=30` отдавал записи с первой, а не с тридцать первой.

**Стало**

`limit` доходит до Битрикс24, и при `limit > 50` платформа сама читает нужное число страниц — как это давно работает у пользователей, отделов и хранилищ. Смещение стало точным: `offset=30` начинает выдачу с 31-й записи. То же поведение на `POST /v1/workgroups/search` и в списочном подзапросе `POST /v1/batch`.

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

Если вы листали рабочие группы вручную и компенсировали округление смещения на своей стороне (например, отбрасывали первые записи страницы), эту компенсацию нужно убрать — иначе записи будут пропускаться дважды. Для глубокого листания надёжнее курсор: фильтр `{">id": последнийId}` с сортировкой по `id`.

### FIX-0804-9: удаление приложения доводит снятие регистрации с портала до конца

**Было**

[DELETE /v1/apps/:id](/docs/apps/delete) снимал приложение с портала одной попыткой.
Если портал был недоступен, права отозвали или у автора приложения не оказалось ключа
разработчика, попытка молча пропадала: приложение удалялось у нас, но оставалось
установленным на портале Битрикс24 — его пункт оставался в меню. Встройки при этом
отвязывались только у опубликованных в каталоге приложений и только на облачных
порталах.

**Стало**

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

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

Ответ эндпоинта не изменился — по-прежнему `204` сразу после удаления на нашей стороне.
Изменился результат: пункт приложения на портале теперь исчезает и в тех случаях, когда
раньше оставался навсегда.

### NEW-0804-10: Отказ по таймауту шлюза на выполнении команды несёт подсказку по восстановлению

Отказ с кодом `GATEWAY_TIMEOUT` у [POST /v1/infra/servers/:id/exec](/docs/infra/deploy/exec) теперь дополнен объектом `hint` с полями `reason`, `recovery` и `recoveryAction` — так же, как это уже сделано для `EXEC_TIMEOUT` и агентского `EXEC_BUSY`. Подсказка говорит главное: код возврата не получен, поэтому исход команды неизвестен и она может продолжать выполняться на сервере. Повторный запуск вслепую способен создать вторую копию поверх первой, поэтому сначала стоит выяснить фактическое состояние — прочитать логи или выполнить короткую read-only команду. Для работы, которая заведомо не укладывается в предельное время, подсказка направляет на фоновую задачу.

Поля `code` и `message` не изменились — подсказка добавлена аддитивно, и прежние вызовы продолжают работать. Приходит в обоих режимах ответа: в JSON-конверте и SSE-событием `error`.

### FIX-0804-11: отказ при недоступной модели — 429 с выдержкой вместо 502

**Было**

Когда доступ к моделям временно закрывался из-за череды неудачных вызовов, `POST /v1/chat/completions` и `POST /v1/embeddings` отвечали **502** с кодом `AI_PROVIDER_UNAVAILABLE`, а в `error.message` приходила внутренняя служебная строка вместо объяснения. Заголовка `Retry-After` не было, поэтому клиенту нечего было ждать — типовая реакция библиотеки на 5xx — повторить сразу, что продлевало недоступность.

**Стало**

Тот же отказ приходит как **429**, `error.type: "rate_limit_exceeded"`, `error.code: "ai_provider_cooldown"` (в потоковом кадре — то же имя в верхнем регистре, как у остальных кодов этого семейства), с заголовком **`Retry-After`** в секундах; в потоковом ответе то же значение приходит полем `retryAfter` внутри терминального кадра ошибки. Значение — остаток окна ожидания, не меньше секунды. `error.message` больше не содержит служебных строк и адресов. Отказ временный: дождитесь `Retry-After` и повторите тот же запрос.

### FIX-0804-12: деплой архива формой multipart сохраняет версию исходников даже при неудачном деплое

**Было**

[POST /v1/infra/servers/:id/deploy](/docs/infra/deploy/deploy) с телом `multipart/form-data` сохранял архив в хранилище исходников только после успешного деплоя. Если деплой падал, версия не появлялась вовсе — разбирать было нечего, а повторная выкладка требовала заново отправить те же байты.

**Стало**

Архив помещается в хранилище исходников до старта деплоя, и деплой продолжается по ссылке на эту версию. Версия остаётся в истории при любом исходе: при успехе она получает `deployStatus: "success"`, при неудаче — `"failed"`. Блок `data.source` в ответе не изменился: `autoSaved`, `savedVersionId`, `sha256` и `newVersion` заполняются как раньше, повторная отправка тех же байт по-прежнему дедуплицируется и новой версии не создаёт.

Появился новый код отказа `SOURCE_DEPOT_UNAVAILABLE` (502): хранилище исходников не приняло архив, деплой не стартовал, запрос можно повторить без изменений. Раньше такой сбой приходил как `VALIDATION_ERROR` (400), то есть выглядел ошибкой запроса.

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

Действий не требуется. Список версий ([GET /v1/infra/servers/:id/sources](/docs/source-storage)) у клиентов, деплоящих формой multipart, теперь может содержать версии неудавшихся выкладок — они помечены `deployStatus: "failed"`.

### NEW-0804-13: поля адресов приходят с человекочитаемыми подписями и описаниями

Раньше [GET /v1/addresses/fields](/docs/entities/addresses/fields) у шести полей из четырнадцати отдавал в `title` имя поля Битрикс24 — `TYPE_ID`, `ENTITY_TYPE_ID`, `ENTITY_ID`, `COUNTRY_CODE`, `ANCHOR_TYPE_ID`, `ANCHOR_ID`. Показать такую подпись пользователю нельзя, а значения кодов приходилось искать в документации.

Теперь у этих шести полей в `title` приходит подпись на языке портала, а у полей, которым есть что добавить к подписи, появился ключ `description` с назначением поля и расшифровкой кодов: типы адреса (все двенадцать, с 1 по 12 — какие из них доступны, зависит от страновой зоны портала) и типы владельца (1 — лид, 3 — контакт, 4 — компания, 8 — реквизит). У поля `countryCode` описание не обещает формат: в документации REST Битрикс24 это поле помечено как неиспользуемое и оставленное для обратной совместимости. У кода 1 расшифровка дополнена подписью из англоязычного интерфейса Битрикс24 — Street address: сам Битрикс24 называет этот тип по-разному в русской и английской версиях, и без оговорки словарь расходился бы с подписью, которую пользователь видит в интерфейсе.

Подписи, которые Битрикс24 отдаёт сам, не изменились. Рядом с `title` та же подпись теперь приходит и в ключе `label` — так же, как у остальных сущностей, поэтому читать подписи можно одним способом на любой сущности. Ключи `description` и `label` добавляются к описанию поля, прежние ключи остаются на месте, поэтому менять ничего не нужно.

Заодно исправлены расшифровки кодов типа адреса в документации операций создания, чтения, изменения и удаления адреса: там были указаны неверные значения, а код 13 не существует вовсе — набор типов заканчивается на 12, и какие из них доступны, зависит от страновой зоны портала.

### NEW-0804-14: поля товаров приходят с подписями, описаниями и словарём формата описания

[GET /v1/products/fields](/docs/entities/products/fields) описывал двадцать одно поле товара только типом и признаком «только для чтения» — ни подписи, ни описания. Понять по такому ответу, что `measure` это единица измерения, а `vatId` — ставка НДС, было нельзя.

Теперь каждое из двадцати одного поля несёт `label` на языке портала, а поля, у которых есть что добавить к подписи, — ещё и `description`: где взять список допустимых значений (`GET /v1/currencies`, `GET /v1/product-sections`, `GET /v1/catalogs`, `GET /v1/users`), как работает сортировка и что задаёт формат описания. У поля `descriptionType` появился словарь `enum` со значениями `text` и `html`.

Ключи добавляются к описанию поля, прежние `type` и `readonly` не меняются, поэтому менять ничего не нужно. Свойства каталога `PROPERTY_<N>` по-прежнему приходят с подписью из настроек портала.

### NEW-0804-15: все 45 полей предложения приходят с подписью и описанием

[GET /v1/quotes/fields](/docs/entities/quotes/fields) отдавал подпись у тридцати полей из сорока пяти. Пятнадцать оставшихся — ровно базовые: `id`, `title`, `dealId`, `contactId`, `companyId`, `amount`, `currency`, `assignedById`, `createdBy`, `comments`, `isManualOpportunity`, `beginDate`, `closeDate`, `createdTime`, `updatedTime` — описывались только типом и признаком «только для чтения». Описания (`description`) не было ни у одного поля.

Теперь подпись есть у всех сорока пяти полей, и у каждого появилось описание: назначение поля, где взять список допустимых значений (`GET /v1/currencies`, `GET /v1/users`, `GET /v1/deals` и другие), поведение при записи. Отдельно названы расхождения имён, на которых легко ошибиться: сумма в Битрикс24 называется `opportunity`, валюта — `currencyId`, а даты начала и закрытия — `begindate` и `closedate` целиком в нижнем регистре.

У поля `stageId` словаря значений нет намеренно: набор стадий настраивается на портале, поэтому в описании стоит ссылка на справочник `GET /v1/statuses?filter[entityId]=QUOTE_STATUS` — статический словарь устарел бы.

Ключи добавляются к описанию поля, прежние `type` и `readonly` не меняются, поэтому менять ничего не нужно.

### NEW-0804-16: поля товаров каталога приходят с подписями и описаниями

[GET /v1/catalog-products/fields](/docs/entities/catalog-products/fields) описывал все сорок два поля товара каталога только типом и служебными признаками — ни подписи, ни описания. Понять по такому ответу, чем `purchasingCurrency` отличается от валюты цены, а `quantityTrace` — от `canBuyZero`, было нельзя.

Теперь каждое из сорока двух полей несёт `label` на языке портала, а тридцать восемь полей — ещё и `description`. В описаниях названо то, что раньше приходилось выяснять на практике: `iblockId` задаётся только при создании и не даёт перенести товар между каталогами; `iblockSection` принимается только при записи, а на чтении основной раздел приходит скаляром `iblockSectionId`; `available` и `bundle` вычисляет Битрикс24; `recurSchemeLength`, `recurSchemeType` и `trialPriceId` работают только в коробочной версии Битрикс24 при продаже контента. Где значение берётся из справочника, указан эндпоинт — `GET /v1/catalogs`, `GET /v1/catalog-sections`, `GET /v1/currencies`, `GET /v1/users`.

У полей `previewTextType` и `detailTextType` набор значений приходит машиночитаемым словарём `enum` (`text` и `html`) — так же, как у формата описания товара, а не прозой внутри описания.

Ключи добавляются к описанию поля, прежние `type`, `readonly`, `createOnly` и `nullable` не меняются, поэтому менять ничего не нужно.

### FIX-0804-17: схема полей шаблона реквизитов объявляет inShortList булевым

**Было**

[GET /v1/requisite-presets/:presetId/fields/schema](/docs/entities/requisite-presets/preset-fields/schema) описывал поле `inShortList` типом `char`, хотя чтение строк того же шаблона отдаёт `true`/`false`, а запись принимает `true`/`false`. Схема противоречила данным, которые она описывает, и клиент, полагавшийся на объявленный тип, готовился разбирать односимвольную строку.

**Стало**

В том же ответе `inShortList.type` приходит как `boolean`. Остальные ключи описания поля (`isRequired`, `isReadOnly`, `title` и прочие) не изменились, типы остальных полей — тоже.

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

Менять ничего не нужно: данные и раньше приходили булевыми. Проверка, сравнивающая `inShortList.type` со строкой `char`, перестанет совпадать — сверяйте с `boolean`.

### NEW-0804-18: скачивание записей звонков и вложений таймлайна

Появились два эндпоинта для файлов CRM, до которых нельзя было добраться через скачивание файла Диска.

[GET /v1/activities/:activityId/files/:fileId/download](/docs/entities/activities/file-download) отдаёт файл дела, в том числе запись звонка. До этого сделать это через API было нельзя: файл дела не является объектом Диска, его идентификатор живёт в отдельном пространстве, пересекающемся с идентификаторами Диска, а ссылка из ответа дела приходит с пустым параметром авторизации — запрос по ней возвращает страницу входа с кодом `200`. Эндпоинт добавляет авторизацию сам, проверяет, что файл действительно принадлежит названному делу, и отдаёт поток байтов.

[GET /v1/timelines/:commentId/files/:fileRef/download](/docs/entities/timelines/file-download) отдаёт вложение комментария таймлайна. В `fileRef` принимается любой из двух идентификаторов: ID привязки, который показывает интерфейс портала, и ID объекта Диска — ключ объекта в поле `files` ответа комментария. Первый считает доступ через сам комментарий, поэтому дотягивается до вложений, которые скачивание файла Диска отдавать отказывается; второй идёт через личные права на Диске. Сначала читается сам комментарий, затем пробуется ID объекта Диска, и только если его нет в списке файлов комментария — ID привязки; указывать выбор не нужно.

Оба эндпоинта требуют скоуп `crm` и никогда не возвращают адрес для скачивания: в нём содержится код авторизации, поэтому наружу уходит только содержимое. Файл, принадлежность которого названному делу или комментарию не подтверждается, получает `404` — это касается и вложения, которое висит на записи другого типа, например на задаче с тем же номером, — без этой проверки эндпоинт позволял бы перебирать файлы портала, поскольку сам Битрикс24 в таком случае отвечает страницей под кодом `200`, а не отказом.

Проверка адреса распространяется и на перенаправления: эндпоинт проходит их сам, сверяя каждый шаг, ограничивает цепочку по длине и по времени и не идёт по перенаправлению на внутренний адрес — такой ответ становится `502`. Это же касается [скачивания файла Диска](/docs/entities/files/download), которое пользуется той же обвязкой.

`404` на скачивании вложения означает именно неверную ссылку. Временная причина — лимит запросов Битрикс24, недоступность портала, сработавшая защита от повторяющихся ошибок — приходит как она есть: `429` либо `502`/`503` с заголовком `Retry-After`. Разница практическая: по `404` повторять запрос бессмысленно, по `429`/`5xx` — нужно, с задержкой. Срок в `Retry-After` считается по тому вызову Битрикс24, который действительно упёрся в лимит.

Контракт [скачивания файла Диска](/docs/entities/files/download) не менялся: те же параметры, те же ответы. Общей обвязкой оно унаследовало только проверку адреса и перенаправлений, описанную выше.

### FIX-0804-19: сообщение бота без текста отбивается понятной ошибкой, а не мнимым успехом

**Было**

[POST /v1/bots/:botId/messages](/docs/bots/messages/send) с текстом в неузнанном поле — например `{"dialogId": "…", "text": "привет"}` — уходил в Битрикс24 без содержимого, и тот отвечал `422` с кодом `EMPTY_MESSAGE` и текстом «Message can't be empty». Понять из такого ответа, что дело в имени поля, было нельзя: текст-то передан.

Хуже вело себя обновление. [PATCH /v1/bots/:botId/messages/:messageId](/docs/bots/messages/update) в той же ситуации отвечал `200 {"result": true}`, а текст сообщения не менялся — мнимый успех, после которого интегратор считал правку применённой.

**Стало**

Оба запроса проверяют содержимое до обращения к Битрикс24 и при его отсутствии отвечают `400` с кодом `MESSAGE_REQUIRED`. В тексте ошибки перечислены нераспознанные ключи тела и указано, что текст сообщения кладётся в поле `message`. Пустой массив в `attach` и пустая строка в `message` содержимым не считаются; блок `attach` без текста — считается, как и число (`0` это текст «0»).

Обёртка `fields` — способ передать тело в родном виде Битрикс24, и «обёртка передана» теперь понимается одинаково на всех шагах обработки. Значение, обёрткой не являющееся (`false`, `0`, пустой массив), обёрткой и не считается: `{"dialogId": "…", "fields": false}` получает тот же `400` с кодом `MESSAGE_REQUIRED`, а `{"message": "привет", "fields": []}` отправляет текст, а не теряет его.

Это тот же код и та же формулировка, что у соседнего [POST /v1/chats/:dialogId/messages](/docs/chats/messages/send): один контракт на одинаковую ошибку в двух родственных эндпоинтах.

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

Запрос с текстом в поле `message` работает как раньше. Запрос, который раньше получал `422 EMPTY_MESSAGE`, теперь получает `400 MESSAGE_REQUIRED` с указанием, что исправить. Поле `text` синонимом `message` не становится: на чтении содержимое сообщения действительно называется `text`, но принимать оба имени молча значило бы развести контракт с эндпоинтом чатов.
