Для 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иsearch—filter,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 — личный ключ
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-приложение
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 — личный ключ
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-приложение
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, не по позиции. |
Пример ответа
{
"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:
{
"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:
{
"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 или ownerId — MISSING_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 и update — items, для чтений 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.