Для AI-агентов: markdown этой страницы — /docs-content/batch.md индекс документации — /llms.txt

Пакетные вызовы

Один HTTP-запрос объединяет до 50 операций над разными сущностями. Каждый вызов идентифицируется собственным id и обрабатывается независимо: ошибка одного вызова не отменяет остальные.

POST /v1/batch

Скоуп: проверяется индивидуально по сущности (crm, task, im, disk и др.) | Базовый URL: https://vibecode.bitrix24.tech/v1 | Авторизация: X-Api-Key (APP-ключ)

Поля запроса (body)

Поле Тип Обяз. Описание
calls array Массив вызовов (от 1 до 50). Каждый элемент — объект, формат описан в таблице ниже.

Поля одного вызова

Поле Тип Обяз. Описание
id string нет Идентификатор вызова в ответе. Если не передан — присваивается порядковый индекс ("0", "1", ...). Длина до 64 символов.
entity string Имя сущности во множественном числе: deals, contacts, companies, tasks, users, files, folders и другие. Полный список сущностей, доступных вашему ключу, возвращает GET /v1/me — см. Ключи и авторизация.
action string Операция: list, get, create, update, delete, fields, search.
entityId number / string для get / update / delete Идентификатор записи.
params object нет Параметры операции в едином entity-формате (имена полей в camelCase, фильтры в синтаксисе фильтрации).

Параметры внутри params совпадают с параметрами одиночного эндпоинта Entity API:

  • list и searchfilter, select, sort, limit. ⚠️ Сортировка передаётся именно под именем sort: order эта дверь в имена полей Битрикс24 не переводит и отправляет их дословно, поэтому порядок молча не применяется. Отдельно withTotal: он действует только у list и только при нулевом offset (см. ниже). Переданный select применяется и к ответу: в записях остаются только перечисленные поля плюс id. Незнакомые имена перечисляются в meta этого вызова как предупреждения UNKNOWN_SELECT_FIELD, а у сущностей со сверенным набором полей отклоняют этот подвызов ошибкой UNKNOWN_SELECT_FIELD — соседние подвызовы при этом выполняются. У реквизитов и банковских реквизитов такое имя вместо предупреждения валит сам подвызов: отказ Битрикс24 приезжает в data.errors под идентификатором подвызова с кодом 100, соседи по пакету выполняются. Значение *UF_*, в любом регистре) означает «вернуть все поля» — отбор не применяется
  • get — поле include, если сущность его поддерживает
  • create и update — поля сущности (имена в camelCase)
  • delete и fields — параметры не нужны
  • смарт-процессы (entity: "items") — внутри params обязательно entityTypeId

Количество записей в списочном вызове. withTotal: false в params означает «количество не нужно»: data.totals.<id> и meta.<id>.total в ответе не приходят. Границы применимости стоит знать: параметр действует на вызовах action: "list" — и при limit не больше 50 с нулевым offset, и при limit больше 50. У action: "search" и у пакета одной сущности POST /v1/{entity}/batch он инертен — подсчёт заказывается как раньше. Инертен он и на list сущностей mail-mailboxes и humanresources-nodes: там количество приходит всегда. Важно: Подсчёт у Битрикс24 параметр отменяет только при limit не больше 50. При limit больше 50 подсчёт нужен платформе, чтобы спланировать обход подзапроса, поэтому там параметр убирает число, а не нагрузку. Правило присутствия total то же, что у одиночных эндпоинтов: на короткой странице точное количество приходит и без заказа, а у вызова с отключённым подсчётом его не будет при offset больше нуля — см. Листание и количество записей. Границей листания в любом случае остаётся meta.<id>.hasMore.

Отмена подсчёта со стороны Битрикс24 — params.start: -1. Помимо withTotal у подкоманды есть второй, более низкоуровневый способ отказаться от счёта: значение -1 в params.start — это прямая инструкция Битрикс24 «коллекцию не считать». Тогда счёта нет и в ответе портала, поэтому платформа его не публикует: ключа total не будет ни в meta.<id>, ни в data.totals.<id>. Числа взяться неоткуда, и withTotal: true его не вернёт — подсчёт отменил сам клиент. Исключение — события календаря: их набор платформа получает целиком одним ответом и считает количество сама, поэтому total придёт и в этом режиме.

Границу выборки в этом режиме определяет meta.<id>.hasMore, и считается она по полноте страницы, которую вернул Битрикс24: заполнена до его размера страницы — 50 записей — → true, короче → false. У action: "list" клиентский limit до Битрикс24 не доезжает, поэтому потолком остаются те же 50. Свой limit уходит на портал только у action: "search", и там полная страница ровно на лимите ({"action":"search","params":{"limit":10,"start":-1}} при десяти записях) — это hasMore: true, а не «всё найдено».

Значение читается так же, как его читает Битрикс24 — дробное усекается, строка разбирается по числовому префиксу, — поэтому -1, "-1", -1.5 и "-1abc" означают одно и то же. Положительное смещение (start: 100) остаётся обычным счётным курсором, total по нему приходит как раньше. Значение, которое числом не является вовсе ("abc", пустая строка, null, объект, массив, true), читается как непереданное: страница будет та же, что и без start, но подкоманда возвращается под общее правило подсчёта записей и может прийти без total.

Важно: start: -1 — не курсор, а отказ от счёта: он всегда отдаёт начало коллекции, и повторный вызов с тем же значением вернёт ту же страницу. Чтобы двигаться дальше, переходите на положительное смещение (start: 100) — по нему total приходит.

Проверяйте наличие ключа (meta.<id>.total !== undefined), а признаком непрочитанного остатка считайте hasMore — так код одинаково работает в обоих режимах.

Идентификатор записи для get / update / delete передаётся в поле entityId на уровне вызова, не в params.id. Вызов { "entity": "users", "action": "get", "params": { "id": 1 } } без entityId отклоняется как MISSING_ENTITY_ID, и вся пачка возвращает 400 с ошибкой валидации. Правильно: { "entity": "users", "action": "get", "entityId": 1 }. Поле params у get служит только для include. Так же строятся update и delete — идентификатор в entityId, изменяемые поля для update в params: { "entity": "deals", "action": "update", "entityId": 575, "params": { "title": "Новое название" } } и { "entity": "contacts", "action": "delete", "entityId": 42 }. Успех update и delete определяется по data.summary.succeeded и отсутствию id в data.errors.

Примеры

curl — личный ключ

Terminal
curl -X POST https://vibecode.bitrix24.tech/v1/batch \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "calls": [
      {
        "id": "deals",
        "entity": "deals",
        "action": "list",
        "params": {
          "filter": { "stageId": "NEW" },
          "select": ["id", "title", "amount"],
          "limit": 50,
          "withTotal": true
        }
      },
      {
        "id": "contacts",
        "entity": "contacts",
        "action": "list",
        "params": {
          "select": ["id", "name", "lastName"],
          "limit": 20,
          "withTotal": true
        }
      },
      {
        "id": "user1",
        "entity": "users",
        "action": "get",
        "entityId": 1
      }
    ]
  }'

curl — OAuth-приложение

Terminal
curl -X POST https://vibecode.bitrix24.tech/v1/batch \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "calls": [
      {
        "id": "deals",
        "entity": "deals",
        "action": "list",
        "params": {
          "filter": { "stageId": "NEW" },
          "select": ["id", "title", "amount"],
          "limit": 50,
          "withTotal": true
        }
      },
      {
        "id": "contacts",
        "entity": "contacts",
        "action": "list",
        "params": {
          "select": ["id", "name", "lastName"],
          "limit": 20,
          "withTotal": true
        }
      },
      {
        "id": "user1",
        "entity": "users",
        "action": "get",
        "entityId": 1
      }
    ]
  }'

JavaScript — личный ключ

javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/batch', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    calls: [
      { id: 'deals', entity: 'deals', action: 'list', params: { filter: { stageId: 'NEW' }, select: ['id', 'title', 'amount'], limit: 50, withTotal: true } },
      { id: 'contacts', entity: 'contacts', action: 'list', params: { select: ['id', 'name', 'lastName'], limit: 20, withTotal: true } },
      { id: 'user1', entity: 'users', action: 'get', entityId: 1 }
    ]
  })
})

const { data } = await res.json()
console.log('Сделки:', data.results.deals)
console.log('Контакты:', data.results.contacts)
console.log('Сводка:', data.summary)

JavaScript — OAuth-приложение

javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/batch', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    calls: [
      { id: 'deals', entity: 'deals', action: 'list', params: { filter: { stageId: 'NEW' }, select: ['id', 'title', 'amount'], limit: 50, withTotal: true } },
      { id: 'contacts', entity: 'contacts', action: 'list', params: { select: ['id', 'name', 'lastName'], limit: 20, withTotal: true } },
      { id: 'user1', entity: 'users', action: 'get', entityId: 1 }
    ]
  })
})

const { data } = await res.json()
console.log('Сделки:', data.results.deals)

Поля ответа

Поле Тип Описание
success boolean true, если запрос принят. Частичные ошибки внутри data.errors не переводят его в false.
data.results object Результаты по id каждого вызова. Значение — то, что вернул бы соответствующий эндпоинт Entity API: массив записей для list / search, объект для get / create, нормализованная запись для update, объект { id, deleted: true } для delete, схема полей для fields.
data.totals object Общее количество записей под фильтр для list / search-вызовов. Ключи — id соответствующих вызовов. У list-вызова присутствие подчиняется тому же правилу, что meta.total одиночного списка: не заказал количество — ключа нет. У search-вызова количество заказывается всегда. Плюс своя ветка: отрицательный params.start отменяет подсчёт на стороне Битрикс24, и тогда числа нет даже при withTotal: true. Оба случая разобраны в абзацах «Количество записей в списочном вызове» и «Отмена подсчёта со стороны Битрикс24» выше. Вызова, завершившегося ошибкой, — тоже нет.
data.errors object Ошибки по id неудачных вызовов. Каждое значение — { "code": "...", "message": "..." }.
data.summary.total number Общее количество вызовов в запросе.
data.summary.succeeded number Количество успешных вызовов.
data.summary.failed number Количество вызовов с ошибкой.
data.meta object Дополнительные сведения по id вызовов с action: "list" или action: "search": total (может отсутствовать — см. выше), returned, hasMore, truncated, а при потере страницы выборки — pageErrorSample.
data.meta.<id>.warnings array Предупреждения по этому вызову. Появляется, только когда они есть. Приходят те же коды, что у одиночного эндпоинта, и в той же форме — code, field, message. Разбирайте массив по code, не по позиции.

Пример ответа

JSON
{
  "success": true,
  "data": {
    "results": {
      "deals": [
        { "id": 575, "title": "Тест валюты", "amount": 0 },
        { "id": 741, "title": "Поставка оборудования", "amount": 250000 }
      ],
      "contacts": [
        { "id": 1, "name": "Иван", "lastName": "Петров" }
      ],
      "user1": [
        { "ID": "1", "NAME": "Мария", "ACTIVE": true }
      ]
    },
    "totals": {
      "deals": 1798,
      "contacts": 305
    },
    "errors": {},
    "summary": {
      "total": 3,
      "succeeded": 3,
      "failed": 0
    },
    "meta": {
      "deals": { "total": 1798, "returned": 2, "hasMore": true, "truncated": false },
      "contacts": { "total": 305, "returned": 1, "hasMore": true, "truncated": false }
    }
  }
}

Частичные ошибки

Если часть вызовов не прошла проверку или вернула ошибку на стороне Битрикс24, успешные результаты остаются в data.results, неудачные — в data.errors под тем же id:

JSON
{
  "success": true,
  "data": {
    "results": {
      "deals": [
        { "id": 575, "title": "Тест валюты" }
      ]
    },
    "totals": {
      "deals": 1798
    },
    "errors": {
      "unknown": {
        "code": "UNKNOWN_ENTITY",
        "message": "Unknown entity \"foobar\". Check GET /v1/guide for available entities."
      }
    },
    "summary": {
      "total": 2,
      "succeeded": 1,
      "failed": 1
    },
    "meta": {
      "deals": { "total": 1798, "returned": 1, "hasMore": true, "truncated": false }
    }
  }
}

Пауза по лимиту приходит по отдельному вызову. Когда метод приостановлен на несколько минут, отказ достаётся не всему пакету, а конкретному вызову — в data.errors.<id> здесь и в data[i].error у пакета одной сущности POST /v1/{entity}/batch. Приостановку описывают два кода: OPERATION_TIME_LIMIT — приостановлена связка «ваш ключ и этот метод», TIMEOUT_QUARANTINE — пауза действует на весь портал. Конверт при этом отвечает 200, поэтому заголовка Retry-After для отдельного вызова у него нет.

Отдельный вызов может отбиться и по другим причинам, и у каждой свой код. Кроме двух кодов приостановки это RATE_LIMITED — превышен темп запросов к порталу; QUEUE_OVERFLOW и QUEUE_TIMEOUT — очередь портала переполнена или вызов не дождался в ней очереди (во втором случае запрос до Битрикс24 не дошёл, повтор безопасен); BITRIX_TIMEOUT — портал принял вызов и не ответил в срок, исход неизвестен; ERROR_LOOP_DETECTED — платформа временно приостановила метод после серии отказов; TOKEN_REFRESH_FAILED — не удалось обновить авторизацию портала, повтор не поможет до переподключения ключа. У всех, кроме последнего, рядом приходит retryAfter в секундах. Отказ без собственного кода приходит как AUTO_PAGINATION_FAILED в общем пакетном вызове и CALL_FAILED в пакете одной сущности.

Если вызов отбила платформа, зная о действующей паузе, рядом с кодом приходят retryAfter в секундах, scope и hint — дождитесь срока и повторите только этот вызов. Так отбиваются вызовы, которые платформа выполняет отдельными запросами: здесь это search, list, чьё окно не умещается в одну страницу Битрикс24, и list у сущностей mail-mailboxes и humanresources-nodes при любом размере окна; а в пакете одной сущности — любой читающий вызов, то есть list, get и fields. Если же паузу применил сам Битрикс24 к вызову внутри общего пакетного запроса, придут только code и message: срок повтора в этом случае не приходит, поэтому повторяйте не раньше чем через пять минут.

Пример ответа при ошибке

Если все вызовы не прошли валидацию, возвращается 400 INVALID_REQUEST с разбивкой по id в data.errors:

JSON
{
  "success": false,
  "error": {
    "code": "INVALID_REQUEST",
    "message": "All calls in the batch failed validation"
  },
  "data": {
    "errors": {
      "x": {
        "code": "MISSING_ENTITY_ID",
        "message": "Action \"get\" requires entityId."
      }
    }
  }
}

Ошибки

HTTP Код Описание
400 INVALID_REQUEST Тело запроса не соответствует схеме или все вызовы провалили валидацию.
400 UNKNOWN_ENTITY В одном из вызовов передано неизвестное имя сущности.
400 ACTION_NOT_SUPPORTED Сущность не поддерживает указанное действие (например, delete для справочной сущности без удаления).
400 MISSING_ENTITY_ID Действия get, update, delete требуют entityId.
400 EMPTY_CREATE_BODY Вызов create без единого поля тела. Возвращается для конкретного вызова в data.errors.
400 EMPTY_UPDATE_BODY Вызов update без единого поля тела. Возвращается для конкретного вызова в data.errors.
400 INVALID_PARAMS В поле, объявленном простым значением, передан объект или массив — в том числе файл парой «имя файла и содержимое в base64», который пакетный вызов не принимает. Возвращается для конкретного вызова в data.errors.
400 BIZPROC_CALLBACK_BATCH_UNSUPPORTED Вызов create или update для bizproc-activities либо bizproc-robots, где handler ведёт на субдомен Black Hole. Такой обработчик регистрируется одиночным запросом. Возвращается для конкретного вызова в data.errors.
400 MISSING_REQUIRED_PARAMS В list-вызове calendar-events не переданы обязательные type и ownerId в params.
400 UNSUPPORTED_FILTER В params.filter вызова переданы поля, которые метод сущности не принимает как фильтр — например, calendar-events.
400 MISSING_DYNAMIC_PARAM Для смарт-процессов (entity: "items") не передан entityTypeId внутри params.
400 INVALID_DYNAMIC_PARAM entityTypeId для смарт-процессов задан некорректно (не положительное целое).
400 USE_DEDICATED_ENTITY Для переданного entityTypeId существует выделенная сущность — использовать её, а не items.
400 ENTITY_CUSTOM_ROUTES Сущность работает только через специализированные маршруты (например, task-comments — через /v1/tasks/:taskId/comments).
400 INVALID_CALL Объект вызова не содержит обязательных полей entity и action.
401 TOKEN_MISSING У ключа нет настроенных OAuth-токенов или для OAuth-приложения не передан Authorization: Bearer ....
401 TOKEN_REFRESH_FAILED Не удалось обновить OAuth-токен портала.
403 MANAGEMENT_KEY_NO_ENTITY_ACCESS Запрос пришёл с management-ключа — пакетные вызовы доступны только APP-ключам.
403 SCOPE_NOT_ALLOWED Все вызовы запросили скоупы, которых нет у ключа. Если хотя бы один вызов проходит — этот код возвращается внутри data.errors для конкретных вызовов, а сам запрос успешен.
422 BITRIX_ERROR Битрикс24 отклонил вызов. Тело ответа содержит bitrixError.error и bitrixError.error_description.
429 RATE_LIMITED Превышен лимит 30 запросов в минуту на портал — общий для всех его API-ключей. Ответ содержит заголовок Retry-After.
429 QUEUE_OVERFLOW На портале накопилось слишком много одновременных вызовов Битрикс24 (по умолчанию 100 и больше в ожидании). Ответ возвращается мгновенно с HTTP-заголовком Retry-After: N (секунды) — клиент должен дождаться его и повторить с экспоненциальным backoff + jitter. Тело: error.code = QUEUE_OVERFLOW, error.retryAfter дублирует заголовок.
429 QUEUE_TIMEOUT Запрос ожидал в очереди портала более 30 секунд. Запрос не был отправлен в Битрикс24 — безопасно повторить (Retry-After). Ответ содержит userMessage и hint.
502 BITRIX_UNAVAILABLE Битрикс24 ответил с ошибкой 5xx.
503 BITRIX_TIMEOUT Битрикс24 принял запрос, но не ответил за 15 секунд — исход неизвестен. Для write-вызовов внутри batch: сначала перечитайте сущность, изменение могло примениться.
500 INTERNAL_ERROR Внутренняя ошибка прокси.

Полный список общих ошибок API — Коды ошибок.

Отказы OPERATION_TIME_LIMIT и TIMEOUT_QUARANTINE приходят по отдельному вызову внутри успешного 200 — они описаны в разделе Частичные ошибки.

Известные особенности

search и list с limit > 50 обходят нативный пакетный вызов Битрикс24. Эти вызовы выполняются как отдельные последовательные запросы со страничной выборкой до 5000 записей — каждый потребляет свою квоту rate-limit Битрикс24 независимо. Остальные вызовы — list с limit ≤ 50, get, create, update, delete, fields — объединяются в один пакетный вызов на стороне Битрикс24 и стоят одну единицу rate-limit суммарно.

list у сущностей mail-mailboxes и humanresources-nodes обходит нативный пакетный вызов при любом limit. Эти сущности обслуживаются семейством методов REST 3.0, а нативный пакетный вызов Битрикс24 умеет отправлять только методы прежнего поколения — раньше такой подвызов возвращал ERROR_METHOD_NOT_FOUND даже при limit в пределах 50. Теперь он выполняется отдельным запросом, как search, и стоит своей квоты rate-limit; соседние вызовы других сущностей по-прежнему уезжают одним пакетом. Исключение действует только на list: get у этих сущностей в пакетном вызове по-прежнему не отвечает данными — читайте запись её собственным маршрутом. Отказ такого подвызова приходит под кодом AUTO_PAGINATION_FAILED в глобальном POST /v1/batch и под CALL_FAILED в POST /v1/{entity}/batch.

Количество записей у этих двух сущностей приходит всегда. withTotal: false на их list не отключает подсчёт: платформа возвращает total и hasMore как обычно. Параметр остаётся допустимым, но нагрузку на портал не снимает.

Стоимость list в единицах rate-limit Битрикс24. Каждые 50 записей = 1 единица. При limit > 50 авто-пагинатор делает несколько вызовов:

limit Единиц Битрикс24
1–50 1 (нативный batch)
51–2 550 2
2 551–5 000 3

У mail-mailboxes и humanresources-nodes строки «1–50» не бывает: их list всегда идёт отдельными запросами по 50 записей, то есть 1 единица на каждые 50 записей начиная с первой. Один вызов в массиве calls — это одна операция для платформы, но несколько запросов к порталу: это разные единицы измерения, не путайте их при планировании квоты. И считайте с запасом: если портал отвечает временной ошибкой, платформа повторяет запрос, а при неудаче повторяет весь обход целиком — вместе с уже успешными страницами. На неудачном обходе фактический расход доходит примерно до восьмикратного от расчётного.

Если действие в одном вызове провалилось, остальные продолжают выполняться. Ошибки попадают в data.errors под тем же id, успешные результаты — в data.results. Поле success остаётся true, проверять нужно data.summary.failed или присутствие нужного id в data.results.

users.get возвращает массив, а не объект. Это особенность Entity API: ответ get для пользователей — [ { ID, NAME, ... } ]. Первый элемент — искомая запись.

Потеря страницы выборки при сбое подзапроса. Если во время авто-пагинации list / search-вызова один из подзапросов страницы упал, результат обрезается до непрерывного префикса, а в data.meta[<id>].pageErrorSample приходит { code, message } первого сбоя. Поле hasMore при этом остаётся true — оставшиеся записи можно дозапросить.

В code приходит либо код ошибки Битрикс24, либо код самого Вайбкод. Кодов Вайбкод сегодня три, и все означают одно: обход прервал не Битрикс24, а сам Вайбкод, и вернул непрерывное начало выборки. KEYSET_DISCONTINUITY — обнаружен разрыв в последовательности страниц, продолжать значило бы отдать дубли. PAGE2_COUNT_FAILED — не удался подсчёт записей: тайм-аут, лимит запросов или ошибка портала. LAZY_COUNT_NO_PROGRESS — метод вернул те же записи вместо следующих. Реакция во всех трёх случаях та же — дозапросить остаток.

У двух последних кодов есть следствие для меты: meta.<id>.total и data.totals.<id> в таком ответе отсутствуют — количество не сосчитано, и число отданных строк его не заменяет. Границей остаётся hasMore.

calendar-events в пакетном вызове. Обязательные type и ownerId передаются в params вызова. Фильтр для этой сущности не поддерживается — оставшиеся поля в params.filter возвращают UNSUPPORTED_FILTER, отсутствие type или ownerIdMISSING_REQUIRED_PARAMS.

Выделенные сущности недоступны в пакетном вызове. Чаты, сообщения, лента, база знаний, звонки и рабочий день обслуживаются выделенными маршрутами — вызов с entity: "chats", "messages", "posts", "note", "calls" или "workday" возвращает ошибку с указателем на нужный маршрут. Пакетный вызов поддерживает только сущности из справочника. Для чтения сообщений многих диалогов одним запросом используйте POST /v1/chats/messages/bulk — до 50 диалогов за вызов.

Обработчик действия или робота на субдомене Black Hole регистрируется одиночным запросом. Вызов create или update для bizproc-activities и bizproc-robots, где handler ведёт на такой субдомен, пакетный вызов отклоняет: BIZPROC_CALLBACK_BATCH_UNSUPPORTED приходит в data.errors под id этого вызова. Обработчик на своём домене проходит пакетным вызовом. Форма отказа на POST /v1/{entity}/batch, поведение на аккаунте без надёжной доставки и что даёт одиночная регистрация — Доставка вызовов действий и роботов.

Пакетный вызов не принимает файл в поле, объявленном простым значением. Так объявлена фотография сотрудника personalPhoto: пара «имя файла и содержимое в base64» в пакете отклоняется, INVALID_PARAMS приходит в data.errors под id этого вызова, а на POST /v1/{entity}/batch весь запрос отвечает 400 BATCH_ITEM_VALIDATION с номером элемента. Причина в размере: подзапрос пакета едет строкой запроса, и пакетные маршруты остались на общем потолке тела, тогда как одиночные подняты до 40 МиБ. За пределом длины подзапроса значение обрезалось бы, а обрезанная запись всё равно отвечает успехом. Отправляйте файл одиночным вызовом — POST /v1/users или PATCH /v1/users/:id. Какие формы поля принимаются и какие отклоняются — раздел «Фотография профиля» на странице Обновить сотрудника.

Проверка формы охватывает только поля, объявленные в схеме сущности. Поля, которого в схеме нет, — например FILES у записей таймлайна, — она не видит: содержимое уедет в подзапросе без предупреждения, и тот же предел длины действует и на него. Такие поля тоже отправляйте одиночным вызовом.

Пакетные операции по одной сущности. Специализированный эндпоинт POST /v1/{entity}/batch работает с одной сущностью. Набор действий у каждой сущности свой: полный список — в operations.batch ответа GET /v1/guide (data.batch в GET /v1/{entity}/fields перечисляет только действия ЗАПИСИ). Действие, которого у сущности нет, отвечает 400 ACTION_NOT_SUPPORTED — в том числе чтение, выключенное у сущности через disabledOperations. Для delete передаётся массив ids, для create и updateitems, для чтений list / get / fields — массив calls. Записи идут внутренними пакетами по 50 штук, до 500 за запрос, ответ — массив результатов с пометкой success для каждого элемента. Для смарт-процессов путь несёт сегмент типа: POST /v1/items/{entityTypeId}/batch. В list-вызове поле filter проходит через транслятор фильтров — работают псевдонимы полей и операторы $gt, $contains, $in. Некорректный фильтр отклоняет ТОЛЬКО свой подвызов: ответ 200, код отказа в data[i].error.code, остальные подвызовы выполняются. Проверяйте наличие error у каждого элемента data — как и в глобальном POST /v1/batch.

Смотрите также