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

Обзор API

Вайбкод предоставляет единый REST-интерфейс для работы с сущностями Битрикс24: CRM, задачи, пользователи, календарь, диск, каталог, документооборот и другие. Формат запроса, ответа и ошибок у всех сущностей одинаковый.

Базовый URL: https://vibecode.bitrix24.tech Авторизация: заголовок X-Api-Key: ваш_ключ Ключи vibe_app_… дополнительно требуют заголовок Authorization: Bearer с токеном сессии на каждый запрос к сущностям. Для фоновых задач по расписанию без пользователя у экрана берите ключ vibe_api_…. Подробнее — Создание и использование ключа.

Доступные операции

Ниже описан стандартный набор операций. Путь строится по шаблону /v1/{entity}.

Набор операций у каждой сущности свой: у части сущностей отдельные операции недоступны. Какие операции доступны сущности, показывают её страница в Справочнике API и поле operations в ответе GET /v1/guide. Запрос к операции, которой у сущности нет, отвечает 404 ROUTE_NOT_FOUNDКоды ошибок.

Список — `GET /v1/{entity}`

Получить записи с пагинацией, сортировкой и выборкой полей.

Terminal
curl -H "X-Api-Key: $KEY" \
  "https://vibecode.bitrix24.tech/v1/deals?limit=100&offset=0"

Параметры:

Параметр Тип Описание
limit number Количество записей (по умолчанию 50, максимум 5000). Значение 0 размером страницы не является: параметр отбрасывается, применяется значение по умолчанию, а в ответ добавляется предупреждение LIMIT_ZERO_IGNORED в meta.warnings
offset number Пропустить N записей
select string[] Выборка полей: ?select=id,title,amount. Принимаются канонические имена из GET /v1/{entity}/fields, исходные имена Битрикс24 у объявленных полей и взаимозаменяемые имена дат updatedAt / updatedTime, createdAt / createdTime. Незнакомое имя отклоняется ошибкой 400 UNKNOWN_SELECT_FIELD на сущностях, чей набор полей выверен по Битрикс24 — сделки, контакты, компании, лиды, предложения, дела, адреса, смарт-процессы, товары и разделы товаров, каталоги и их товары и разделы, заказы и статусы заказов, статусы, валюты, события и разделы календаря, файлы, папки, хранилища, подразделения, рабочие группы, шаблоны документов. На остальных оно даёт предупреждение UNKNOWN_SELECT_FIELD в meta.warnings. Пользовательские поля (UF_*, ufCrm*) не отклоняются нигде, а свойства товаров принимаются каждое на СВОЕЙ сущности: PROPERTY_295 — на товарах, property295 — на товарах каталога, где их номера назначает портал. Чужое написание метод не понимает, и его гейт отклоняет. На списке и поиске реквизитов и банковских реквизитов имя уходит дальше в Битрикс24, и там аккаунт, не знающий такого поля, отклоняет весь вызов с 422 BITRIX_ERROR. Значение *UF_*, в любом регистре) означает «вернуть все поля» — отбор не применяется, а незнакомое имя рядом с ним даёт предупреждение вместо ошибки
order object Сортировка: ?order[createdAt]=desc
withTotal string Нужно ли количество: true или false, ровно эти два значения. false — единственный способ гарантированно убрать meta.total из ответа. При limit не больше 50 он заодно отменяет подсчёт, при limit больше 50 — убирает число, а не подсчёт. Без параметра значение берётся из настройки ключа, затем из платформенного умолчания, и тогда на короткой странице точное количество приходит и без заказа — см. Листание и количество записей

Авто-пагинация: при limit > 50 Вайбкод автоматически запрашивает несколько страниц у Битрикс24 и возвращает все записи в одном ответе.

Получить по ID — `GET /v1/{entity}/{id}`

Terminal
curl -H "X-Api-Key: $KEY" \
  "https://vibecode.bitrix24.tech/v1/deals/123"

Ответ: { success: true, data: { id: 123, title: "...", ... } }

Параметр select работает и при получении записи по id — ответ содержит только перечисленные поля. Поддерживаются три формы: список через запятую ?select=id,title,stageId, повторяющийся ключ ?select[]=id&select[]=title и индексируемый ?select[0]=id&select[1]=title. Поле id возвращается всегда. Незнакомое имя поля отвечает ошибкой 400 UNKNOWN_SELECT_FIELD на сущностях со сверенным набором полей (список — в таблице параметров выше), а на остальных дополняет ответ предупреждением UNKNOWN_SELECT_FIELD в meta.warnings. Пользовательское поле указывается именем из схемы — Пользовательские поля (UF). Значение *UF_*) — привычная для Битрикс24 запись «вернуть все поля»: отбор не применяется, приходит полная запись. Параметр совместим с include — связанные данные подгружаются до отбора полей.

Создать — `POST /v1/{entity}`

Terminal
curl -X POST -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
  "https://vibecode.bitrix24.tech/v1/deals" \
  -d '{"title": "Новая сделка", "stageId": "NEW", "amount": 150000}'

Ответ: { success: true, data: { id: 456, ... } } (HTTP 201)

Тело без единого поля возвращает 400 EMPTY_CREATE_BODY, а у сущностей с обязательными полями создания — 400 MISSING_REQUIRED_FIELDS с именем первого недостающего поля. Оба кода — Коды ошибок.

Обновить — `PATCH /v1/{entity}/{id}`

Terminal
curl -X PATCH -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
  "https://vibecode.bitrix24.tech/v1/deals/123" \
  -d '{"stageId": "WON", "amount": 200000}'

Тело без единого поля возвращает 400 EMPTY_UPDATE_BODYКоды ошибок.

Удалить — `DELETE /v1/{entity}/{id}`

Terminal
curl -X DELETE -H "X-Api-Key: $KEY" \
  "https://vibecode.bitrix24.tech/v1/deals/123"

Поиск — `POST /v1/{entity}/search`

Поиск с фильтрацией. Подробнее о синтаксисе фильтров: Фильтрация

Terminal
curl -X POST -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
  "https://vibecode.bitrix24.tech/v1/deals/search" \
  -d '{"filter": {"stageId": "NEW", "amount": {"$gte": 100000}}, "sort": {"createdAt": "desc"}, "limit": 200}'

Параметры:

Параметр Тип Описание
filter object Условия фильтрации (три синтаксиса)
sort string | object | array Сортировка. Поддерживаются: "id" / "-amount" / "id,-createdAt" (string), { id: "asc", amount: "desc" } или { id: 1, amount: -1 } (object), ["id", "-amount"] (array). Некорректный тип → 400 INVALID_SORT_TYPE. Подробнее про пагинацию — см. filtering.md.
limit number Количество записей (по умолчанию 50, максимум 5000, авто-пагинация при > 50). Значение 0 отбрасывается — см. предупреждение LIMIT_ZERO_IGNORED
offset number Пропустить N записей
autoWindow boolean false — отключить разбивку по дате для больших выборок
withTotal boolean Нужно ли количество. false — единственный способ гарантированно убрать meta.total из ответа. При limit не больше 50 он заодно отменяет подсчёт, при limit больше 50 — убирает число, а не подсчёт. Без поля значение берётся из настройки ключа, затем из платформенного умолчания, и тогда на короткой странице точное количество приходит и без заказа — см. Листание и количество записей

Поиск по временным окнам: для больших наборов данных поиск автоматически разбивает запрос на окна по дате. Если это вызывает таймауты — отключите через autoWindow: false.

Агрегация — `POST /v1/{entity}/aggregate`

Подсчёт количества и числовые агрегации (sum, avg, min, max) с фильтрацией. Набор операций каждой сущности перечислен на её странице в Справочнике API. У сущностей без агрегации количество записей приходит в meta.total ответа списка.

Terminal
curl -X POST -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
  -d '{
    "aggregate": [
      { "field": "amount", "function": "sum" },
      { "field": "amount", "function": "avg" }
    ],
    "filter": { "stageId": "WON" }
  }' \
  "https://vibecode.bitrix24.tech/v1/deals/aggregate"

Параметры (body):

Параметр Тип Описание
aggregate array Массив агрегаций: { "field": "amount", "function": "sum" }. Функции: count, sum, avg, min, max. Без массива — только count
filter object Фильтрация по полям сущности
groupBy string | string[] Поле или массив полей для группировки (максимум 5). Допустимые значения — из списка aggregatable конкретной сущности. У сущностей без этого списка (например, statuses, currencies, deal-categories) группировка не поддерживается, доступен count с фильтром

Как это работает: count считается одним быстрым вызовом в Битрикс24. Для sum/avg/min/max платформа подгружает записи под фильтр, максимум 5000, и считает на стороне Вайбкод. Прочитано меньше, чем обещал data.count, — ответ помечается meta.truncated: true, и потолок лишь одна из причин такой пометки, а размер нехватки приходит в data.meta.recordsShortfall. Полный разбор — Агрегация POST — потолок 5000 записей. При некорректном поле ответ содержит список доступных.

Пользовательские поля (UF) поддерживаются в sum/avg/min/max для UF-типов integer, double, money. groupBy принимает UF-поля любого типа. Подробно — в разделе Агрегация POST — UF-поля ниже.

Смарт-процессы (items) передают тип в пути: POST /v1/items/:entityTypeId/aggregate. Зарезервированные entityTypeId (1, 2, 3, 4, 7, 31) обслуживаются специализированными API сделок, лидов, контактов, компаний, предложений, счетов.

Агрегация POST — потолок 5000 записей

Потолок действует на запросе, которому для ответа нужны сами записи: это числовые функции и группировка. Простой count без groupBy под него не подпадает — он считается на стороне Битрикс24 и отвечает на выборке любого размера.

Поведение на выборке шире 5000 записей сейчас раскатывается по аккаунтам, поэтому вариантов два:

  • Пока возможность не включена на аккаунте — приходит 200, в ответе meta.truncated: true, результат посчитан по первым 5000 записям. Ту же пометку ставит и нехватка ниже потолка: прочитано меньше, чем обещал data.count, а её размер лежит в data.meta.recordsShortfall.
  • После включения — приходит 422 AGGREGATION_LIMIT_EXCEEDED, и ни одна запись не выгружается. В тексте ошибки перечислено, что делать дальше. Смысл замены: на крупной выборке выгрузка первых 5000 всё равно не успевала, запрос обрывался по таймауту, и усечённый ответ до вызывающего чаще всего не доходил вовсе.

Действие в обоих случаях одно: сузить фильтр либо взять count без группировки. Клиент, который сегодня ветвится по meta.truncated, после включения возможности на его аккаунте начнёт получать 422 — предусмотрите обе ветки.

meta.truncated теперь поднимается ещё в одном случае — и это касается ВСЕХ сущностей, не только дел. Если записей обработано меньше, чем ответ обещал, он ставит truncated: true и добавляет meta.recordsShortfall — сколько записей не хватило. Обещанное — это data.count, а в режиме счётчиков по стадиям (meta.aggregatePath: "fanout") большее из data.count и суммы счётчиков стадий: пробы стадий могут в пределах допуска дать больше воронки, и тогда нехватка меряется от них. Поэтому предикат «неполно, если recordsProcessed < totalRecords» верен не всегда — читайте сам truncated. Раньше в этом случае приходило truncated: false, то есть группы и числовые агрегации считались по части записей, а ответ этого не сообщал. count и meta.totalRecords остаются полными.

Признак усечения приходит рядом с самим числом. Пока он лежал только в meta, клиент, который читает data.aggregates.amount.sum и больше ничего, получал уверенное число без единого намёка на неполноту. Поэтому при усечении каждый объект поля в data.aggregates и в groups[].aggregates дополнительно несёт truncated: true:

JSON
{
  "data": {
    "count": 20000,
    "aggregates": {
      "amount": { "sum": 1234567, "truncated": true }
    },
    "meta": {
      "totalRecords": 20000,
      "recordsProcessed": 5000,
      "truncated": true,
      "warnings": [{ "code": "AGGREGATE_TRUNCATED", "message": "…" }]
    }
  }
}

Значения sum / avg / min / max остаются числами — в объект их никто не оборачивает. Ключ truncated внутри объекта поля появляется ТОЛЬКО при усечении: на полной выборке его нет вовсе, а не false. Это касается условных пометок — data.aggregates.<поле>.truncated, того же ключа внутри groups[].aggregates и groups[].truncated. На data.meta.truncated правило не распространяется: он присутствует всегда и на полной выборке равен false, поэтому проверять там нужно значение, а не наличие ключа.

Признак стоит в том объекте, из которого читается число: при усечении сам объект группы несёт groups[].truncated рядом со своим count. На группировке с одним только count это единственная пометка у числа — у count-выражения объекта поля не возникает вовсе, и оба бэга aggregates приходят пустыми.

Пометка говорит, что ответ — образец. Точен ли при этом сам счётчик группы, зависит от пути, и путь называет meta.aggregatePath: на обычном обходе (поля нет) groups[].count — это размер прочитанного среза, а не всей группы, и как итог по группе его использовать нельзя. В режиме счётчиков по стадиям (meta.aggregatePath: "fanout") счётчик берётся отдельной пробой и точен — образцом там остаются только aggregates рядом с ним. Точные числа его не несут никогда: data.count и meta.totalRecords берутся отдельным быстрым подсчётом и верны на выборке любого размера. Тем же условием добавляется предупреждение AGGREGATE_TRUNCATED в meta.warnings — второй канал для клиента, который проверяет meta.warnings перед тем, как считать выдачу полной.

Что с этим делать — способа ровно два: сузить фильтр так, чтобы выборка уложилась в потолок, либо взять один count — без groupBy и без числовых функций. count точен на выборке любого размера, потому что записи для него не выгружаются вовсе.

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

У сделок есть отдельный режим счётчиков по стадиям, который отвечает точными числами, не читая строк, — но он включается по аккаунтам, и по усечённому ответу нельзя понять, включён ли он у вас. Не угадывайте, а проверьте одним запросом: { "aggregate": [{ "field": "*", "function": "count" }], "groupBy": "stageId" } со скалярным categoryId в фильтре. Сработал режим — в ответе будет meta.aggregatePath: "fanout" и meta.recordsProcessed: 0, а счётчики точны. Не сработал — придёт обычный усечённый ответ, и вы потеряете только этот один вызов.

Само число суммы при усечении — образец по части записей, а не итог, и как итог его использовать нельзя.

У сделок есть отдельный режим счётчиков по стадиям, который отвечает без выгрузки записей, но обходом потолка в общем случае он не является и по умолчанию выключен: его включает администратор платформы по аккаунтам, он работает только для groupBy по stageId или stageSemanticId со скалярным categoryId и только без числовых функций. Пока он выключен — а это состояние по умолчанию, — такой запрос идёт обычным путём и выше потолка приходит усечённым. Условия целиком — Агрегация сделок.

Агрегация POST — сужающий фильтр у дел

У дел ограничение другого рода, и оно касается даже простого count: Битрикс24 не успевает посчитать все дела аккаунта за отведённое на вызов время. Поэтому агрегация дел требует одного сужения — пара ownerTypeId + ownerId, либо responsibleId, либо граница по дате на createdAt / updatedAt / deadline. Без сужения приходит 400 MISSING_REQUIRED_FILTER с перечнем допустимых сужений, а если требование на аккаунте ещё не включено — 422 AGGREGATION_LIMIT_EXCEEDED в тот момент, когда Битрикс24 действительно не ответил. Ни то, ни другое повторять бессмысленно. Подробно — Агрегация дел.

Агрегация POST — UF-поля

Канонический POST-вариант агрегации принимает массив выражений и поддерживает пользовательские поля. Имя поля — то же, что в схеме сущности: на другое написание приходит 400 INVALID_PARAMS со списком доступных полей.

Terminal
curl -X POST https://vibecode.bitrix24.tech/v1/deals/aggregate \
  -H "X-Api-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "aggregate": [
      { "function": "sum", "field": "amount" },
      { "function": "sum", "field": "ufCrmBudget" },
      { "function": "avg", "field": "ufCrmScore" }
    ],
    "groupBy": "ufCrmPriority"
  }'

Правила UF-поддержки:

Функция Принимает UF-типы
sum, avg, min, max только integer, double, money
groupBy любой UF-тип — string, enumeration, date, integer и другие

Поля типа money хранятся в Битрикс24 как строка "сумма|валюта" (например "1500.50|RUB"). Агрегат извлекает числовую часть до | — все арифметические операции корректны.

Ошибки:

HTTP Код Когда
400 INVALID_PARAMS UF-тип не из списка integer/double/money для sum/avg/min/max — в message указан фактический тип
400 INVALID_PARAMS Поле не найдено ни в схеме, ни в UF-кэше — в message перечислены доступные стандартные и UF-поля

Кэш UF-полей: одно обращение к crm.{entity}.fields на комбинацию портал+сущность (плюс entityTypeId для items). TTL 5 минут — повторные агрегаты в пределах окна не вызывают дополнительных запросов.

Схема полей — `GET /v1/{entity}/fields`

Получить описание всех полей сущности с типами.

Terminal
curl -H "X-Api-Key: $KEY" \
  "https://vibecode.bitrix24.tech/v1/deals/fields"

Метаданные каждого поля в ответе содержат отображаемое название label и пояснение назначения description, где они заданы для поля. Названия полей не нужно искать в отдельном справочнике — они приходят вместе со схемой.

Названия и пояснения приходят на русском языке. Заголовками запроса язык не переключается. Названия полей, которые платформа берёт напрямую с портала, включая пользовательские, приходят на языке портала.

Пакетные операции — `POST /v1/{entity}/batch`

Массовое создание, обновление или удаление записей одной сущности.

JSON
{ "action": "create", "items": [{ "title": "Сделка 1" }, { "title": "Сделка 2" }] }

Действие list принимает filter, select и limit, как и одиночный список, и отбор применяется к ответу: в записях остаются только перечисленные поля плюс id. Незнакомое имя приезжает предупреждением UNKNOWN_SELECT_FIELD в meta этого подвызова — соседние подвызовы своё meta не получают. У сущностей, чей набор полей выверен по Битрикс24, такое имя вместо предупреждения отклоняет подвызов ошибкой UNKNOWN_SELECT_FIELD: отказывает только он, соседи по пакету выполняются. Значение *UF_*, в любом регистре) означает «вернуть все поля» — отбор не применяется, и незнакомое имя рядом с ним ничего не отклоняет. Важно: order эта дверь НЕ читает — порядок задаёт сама платформа, и переданный в подвызове order не применяется. Нужна сортировка — берите глобальный POST /v1/batch и передавайте её под именем sort: order он тоже не переводит в имена полей Битрикс24.

Для работы с разными сущностями в одном запросе используйте Batch API.

Формат ответа

Все эндпоинты возвращают единый формат:

JSON
{
  "success": true,
  "data": [ ... ],
  "meta": { "total": 150, "hasMore": true }
}
Поле Описание
success true при успешном выполнении
data Массив записей у list и search, объект у get и create
meta.total Общее количество записей под фильтр у list и search. Необязательное поле: если количество не заказывалось, его в ответе нет — см. Листание и количество записей
meta.hasMore Есть ли ещё записи для загрузки
meta.nextAfterId Идентификатор последней отданной записи, строкой. Приходит у списка и у поиска при сортировке строго по id по возрастанию, пока meta.hasMore равен true. Передаётся обратно как filter[>id]. Сущности с курсором перечислены в Листании и количестве записей
meta.pageErrorSample Приходит у списка и у поиска, когда часть страниц не загрузилась: code и message первой ошибки. Ответ при этом содержит непрерывное начало выборки, а не все записи. code — либо код ошибки Битрикс24, либо код самого Вайбкод. Последних три. KEYSET_DISCONTINUITY означает, что обход прервал сам Вайбкод, обнаружив разрыв в последовательности страниц, и вернул непрерывное начало вместо возможных дублей. PAGE2_COUNT_FAILED — не удался подсчёт записей (тайм-аут, лимит запросов, ошибка портала). LAZY_COUNT_NO_PROGRESS — метод вернул те же записи вместо следующих. meta.total отсутствует только у двух последних: там подсчёт не состоялся. При остальных обрывах количество уже сосчитано, и meta.total приходит рядом
meta.windowErrorSample То же для поиска по окнам: code и message первого сорвавшегося окна. Приходит вместе с meta.windowErrors — числом неудачных окон. При полном отказе поиск возвращает ошибку, а не частичный ответ
meta.warnings Массив предупреждений о выдаче: у списка, поиска, получения записи по id, запроса схемы полей, у товарных позиций сущности и у агрегации. У каждого есть code и message, а field — только когда предупреждение привязано к конкретному полю. Коды LIMIT_ZERO_IGNORED и UNKNOWN_SELECT_FIELD описаны в параметрах выше. WINDOW_TRUNCATED — оконный поиск отдал не всё, разбор в неполной выдаче. OFFSET_BEYOND_FETCHED_PAGE — страница вышла пустой на постраничном обходе, разбор в обходе по страницам вручную. AGGREGATE_TRUNCATED — числовая агрегация посчитана по части записей, разбор в потолке 5000 записей

Форма выше — про сущности этого справочника. Другие семейства маршрутов кладут служебные поля иначе:

  • Глобальный POST /v1/batchresults, errors, summary, totals и meta лежат внутри data, с разбивкой по id каждого вызова.
  • Выделенные маршруты — чаты, почта, лента, база знаний, звонки, рабочий день — несут свою форму data, она описана на страницах этих разделов.

Листание и количество записей

Листайте по meta.hasMore, а не по арифметике от meta.total. Признак «есть ещё» выводится из полноты страницы: пришла полная — возможно, есть ещё, пришла неполная — список закончился. Цикл «читай, пока hasMore» доходит до конца всегда. Если коллекция ровно кратна limit, последний шаг вернёт пустой список — это штатный признак конца, а не ошибка.

При сортировке строго по id по возрастанию ответ дополнительно несёт meta.nextAfterId — идентификатор последней отданной записи. Передайте его обратно как filter[>id], и следующая страница начнётся ровно за ней. Такое листание не зависит от смещения и не дорожает к концу коллекции, поэтому для обходов в десятки тысяч записей оно предпочтительнее растущего offset. Готовый цикл обхода — Постраничный вывод.

Курсор доступен у сделок, лидов, контактов, компаний, предложений и элементов смарт-процессов. У остальных сущностей meta.nextAfterId в ответе нет: страницы там берутся offset, а размер выборки сокращается фильтром.

Количество записей — отдельный вопрос, и стоит он у Битрикс24 несоразмерно дорого: посчитать коллекцию заметно дороже, чем отдать из неё страницу. Поэтому:

  • Нужна цифра — спросите её прямо: POST /v1/{entity}/aggregate с функцией count возвращает количество одним вызовом, без выгрузки записей.
  • Цифра не нужна — уберите её из ответа: withTotal=false в запросе списка или настройка totalDefault на API-ключе, если так работает вся интеграция. Про то, в каких случаях meta.total при этом всё же приходит, — ниже в этом разделе. Важно: Нагрузку это снимает только на одностраничном вызове (limit не больше 50): там подсчёт действительно не заказывается. На многостраничном (limit больше 50) подсчёт платформе нужен, чтобы спланировать обход, поэтому параметр убирает число, а не стоимость. Ставить его ради экономии там незачем: дешевле вызов не станет, а точное количество, которое короткая первая страница отдаёт бесплатно, будет отброшено.
  • Не эмулируйте счётчик обходом. Пролистать коллекцию, чтобы посчитать строки, — это десятки и сотни вызовов вместо одного и самый дорогой способ узнать одну цифру. Для этого есть aggregate с count.

Когда подсчёт не заказан, meta.total в ответе чаще всего отсутствует — но не всегда. Если страница пришла короче запрошенного limit, количество известно из самой страницы, и точное число всё равно приходит. Полная картина:

Что было в запросе meta.total в ответе
Подсчёт заказан — умолчание или withTotal=true приходит
Передан withTotal=false не приходит никогда
Подсчёт отключён настройкой ключа или платформенным умолчанием, offset = 0, страница короче limit приходит, точное число — в том числе 0
Подсчёт отключён настройкой ключа или платформенным умолчанием, страница полная либо offset больше нуля не приходит

Таблица описывает вызов, на котором подсчёт можно пропустить. Там, где пропустить его нельзя, withTotal=false просто игнорируется и meta.total приходит как раньше. Поэтому проверяйте наличие поля в конкретном ответе, а не выводите его из настроек.

Короче: если подсчёт не заказан, total приходит только на вызове с offset = 0 и только если страница пришла короче запрошенного. Если подсчёт заказан — умолчанием или явным withTotal=true — он приходит всегда. Обратите внимание на разницу между параметром и настройкой: явный withTotal=false в запросе убирает и точное число из короткой страницы, а настройка totalDefault на ключе — нет. Поэтому два одинаковых запроса от двух разных ключей могут вернуть ответы разной формы.

Из «подсчёт заказан — приходит всегда» есть одно исключение — частичный ответ, у которого сорвался сам подсчёт. Если в ответе есть meta.pageErrorSample с кодом PAGE2_COUNT_FAILED или LAZY_COUNT_NO_PROGRESS, meta.total отсутствует: количество не сосчитано, а число отданных строк его не заменяет. При остальных обрывах — KEYSET_DISCONTINUITY и любой код Битрикс24 — подсчёт состоялся до обрыва, и meta.total приходит рядом с meta.pageErrorSample. И тот и другой ответ приходит с кодом 200, содержит непрерывное начало выборки и meta.hasMore, равный true.

Сам подсчёт платформа выполняет, только когда без него не обойтись. На форме ответа это не сказывается: на многостраничном вызове (limit больше 50) meta.total приходит так же, как приходил, — просто иногда количество оказывается известно бесплатно.

meta.total — информативное поле. Оно может отставать от текущего состояния коллекции до минуты, поэтому число отданных строк иногда оказывается больше него. Полнота данных от meta.total не зависит: за неё отвечают meta.hasMore и meta.nextAfterId.

Действующее для вашего ключа умолчание по meta.total показывает блок totalDefault в GET /v1/me: key — настройка ключа, platform — платформенное умолчание, effective — что получится, если запрос не передаст withTotal.

Преобразование полей

Вайбкод автоматически преобразует имена полей при отправке запроса. В запросах всегда используйте camelCase:

JSON
{ "title": "Сделка", "stageId": "NEW", "assignedById": 1 }

В ответах объявленные поля сущностей приходят в camelCase — id, title, stageId, responsibleId.

Пользовательские поля портала — отдельный случай. Их состав зависит от портала, заранее они не описаны, а часть полей носит имя, сгенерированное самим Битрикс24. Имя, под которым такое поле принимается в запросе и приходит в ответе, берите из схемы GET /v1/{entity}/fields — написание различается по сущностям. Имена и форматы значений собраны в разделе Пользовательские поля (UF).

Отличается не только регистр — у части полей отличается само имя. Например, сумма сделки в Вайбкод называется amount, а исходное имя этого поля в Битрикс24 — opportunity. Имя Вайбкод стоит в колонке «Поле» схемы GET /v1/{entity}/fields, исходное имя Битрикс24 — в колонке «Битрикс24». В запросах и при чтении ответа используйте имя из колонки «Поле» — под ним поле и приходит в ответе, исходное имя Битрикс24 не возвращается. Список соответствий для каждой сущности — на её странице полей, например Поля сделки.

Часовой пояс на записи

Дата-время, записанное без пояса (2026-07-15T13:00:00), Битрикс24 читает в поясе того аккаунта, через который платформа пишет на портал, — а не в вашем. Клиент из Берлина, отправивший 13:00, получал сохранённые 10:00 по Гринвичу вместо 11:00: разница в час летом и в два зимой.

Чтобы этого не было, объявите свой пояс заголовком запроса:

X-Vibe-Timezone: Europe/Berlin

Значение — имя пояса из базы IANA. В браузере оно берётся из Intl.DateTimeFormat().resolvedOptions().timeZone, серверная интеграция подставляет пояс, в котором ведёт свои данные.

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

Смещение получает только запись вида ГГГГ-ММ-ДДTЧЧ:ММ, где пояс не указан, и только в тех полях, где заголовок действует. Секунды и доли секунды не обязательны и на подстановку не влияют, а перечень полей — ниже. Остальные формы даты-времени уходят на портал без изменений, и заголовок на них не действует:

Что отправлено Что уходит на портал
2026-09-15T13:00:00 2026-09-15T13:00:00+02:00 — смещение подставлено
2026-09-15T13:00, 2026-09-15T13:00:00.500 смещение подставлено так же — без секунд и с долями секунды
2026-09-15T13:00:00Z или со смещением как есть — пояс объявлен вами, платформа его не переписывает
2026-09-15 13:00:00 через пробел как есть — читается в поясе портального аккаунта
15.09.2026 13:00 в местной форме как есть — читается в поясе портального аккаунта

Две последние формы отказа не вызывают: ответ приходит успешным, а сохранённое время отличается на разницу между вашим поясом и поясом портала. Для заголовка Europe/Berlin и портала в поясе UTC+3 запись 2026-09-15T13:00:00 сохраняется как 2026-09-15T11:00:00Z, а те же 13:00 через пробел — как 2026-09-15T10:00:00Z.

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

Пояс подставляется не всем полям подряд, а только проверенным. Битрикс24 объявляет частью полей тип «дата и время», а хранит в них голую дату — подставленное смещение сдвинуло бы у такого поля день. Поэтому список полей открывается по мере живой проверки каждого. Сегодня в нём поля задачи: deadline, startDatePlan, endDatePlan, а также служебные createdDate, changedDate, closedDate — значение вида 2019-05-15T13:47:00 получает у них смещение вашего пояса, а не читается в поясе владельца ключа. Где заголовок действует, видно по машинному описанию API — у операции он объявлен в списке параметров.

Отдельно стоят события календаря: у них пояс задаётся собственными параметрами запроса (timezoneFrom / timezoneTo), поэтому заголовок для них игнорируется — иначе событие сдвинулось бы дважды.

Заголовок влияет только на запись. На фильтр он не действует — значения фильтра читаются в поясе портального аккаунта независимо от заголовка. Что из этого следует для поиска по датам и как пересчитать границы — Часовой пояс в значении фильтра.

Мультиполя (email, телефон, сайт)

Контактные поля email, phone и web хранят несколько значений с типами. Набор зависит от сущности — email и phone есть у контактов, компаний и лидов, web только у компаний. Формат отличается на запись и на чтение.

На запись — массив объектов [{ "value": "petrov@example.com", "typeId": "WORK" }]. Также принимается одиночная строка "petrov@example.com" и массив строк. Значение typeId зависит от поля — WORK / HOME / MOBILE / OTHER для телефона, WORK / HOME / MAILING / OTHER для email. По умолчанию WORK.

JSON
{ "email": [{ "value": "petrov@example.com", "typeId": "WORK" }] }

Форма с ключами VALUE и VALUE_TYPE в верхнем регистре не принимается — 400 INVALID_MULTIFIELD_SHAPE. Используйте camelCase value и typeId.

На чтение — первичное значение приходит строкой ("email": "petrov@example.com"), а полный набор значений с типами лежит в массиве fm. Значения по типам доступны в отдельных полях (emailWork, phoneMobile).

Точный набор typeId и полей чтения для каждой сущности — на её странице, например Контакты и Компании.

Пользовательские поля (UF)

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

Имя поля

Рабочее имя одно — то, под которым поле стоит в схеме GET /v1/{entity}/fields. Под этим именем поле принимается в теле запроса, в filter и в select, под ним же оно приходит в ответе. Написание различается по сущностям, и вывести его из имени, заданного при создании, нельзя:

Сущность Примеры имён в схеме
Сделки, лиды, контакты, компании, предложения ufCrmProjectCode, ufCrm_1729594209
Элементы смарт-процессов ufCrm3_1628508847
Реквизиты UF_CRM_1698325419
Сотрудники UF_USR_1619099890455, UF_PHONE_INNER

Список определений полей и схема сущности называют одно и то же поле по-разному. Поле, созданное на сделках как fieldName: "PROJECT_CODE", стоит в ответе GET /v1/userfields/deals под именем UF_CRM_PROJECT_CODE, а в схеме сделки и во всех запросах — под именем ufCrmProjectCode.

Имя, которого нет в схеме, не даёт ошибки при записи. У сделок, лидов, контактов, компаний, предложений и элементов смарт-процессов значение, отправленное под именем UF_CRM_PROJECT_CODE вместо ufCrmProjectCode, не сохраняется: ответ приходит с кодом 200 или 201, а поле остаётся пустым. В select такое имя не добавляет поле в ответ, в агрегации — на него приходит 400 INVALID_PARAMS со списком доступных полей. Поведение отбора — Неизвестное имя поля в фильтре.

Формат значения по типам

Тип поля задаётся при создании параметром userTypeId и приходит в схеме сущности в поле type.

Примеры ниже сняты на портале в поясе UTC+3.

Тип Что отправлять Что приходит в ответе
string "Договор №17" Та же строка
integer 42 или "15" Число. Дробное значение обрезается до целого — 3.7 сохраняется как 3
double 3.14 или "7.25" Число, округлённое до числа знаков после запятой, заданного настройкой PRECISION этого поля. При PRECISION равном 2 значение 3.14159 приходит как 3.14. Поле, созданное без settings, получает PRECISION равный 0 и хранит целые: 3.14 приходит как 3, а 3.99 — как 4
boolean true, false, "Y", "N". Значения 1, 0, "1" и "0" сохраняются как отрицательное значение — false у сделок, "N" у остальных У сделок — true или false. У контактов, лидов, компаний, предложений и элементов смарт-процессов — строки "Y" и "N"
enumeration Идентификатор варианта — "3821". Варианты поля перечислены в массиве items схемы сущности Идентификатор числом — 3821. Текст варианта вместо идентификатора сохраняется как 0
datetime "2026-08-10T12:30:00" — время читается в поясе портала. Смещение можно указать явно У сделок — момент времени по Гринвичу, "2026-08-10T09:30:00.000Z". У контактов, лидов, компаний, предложений и элементов смарт-процессов — то же время со смещением портала, "2026-08-10T12:30:00+03:00"
date "2026-08-10" Полный момент времени со смещением портала — "2026-08-10T03:00:00+03:00"
money "100|USD" — сумма и код валюты через вертикальную черту. Значение без кода валюты сохраняется в базовой валюте портала "100|USD". Отправленное 100 приходит как "100|RUB", "250.50|RUB" — как "250.5|RUB"
url "https://example.com/contract" Та же строка
address "Москва, Тверская 1|55.7601;37.6055" — адрес и координаты разделяет вертикальная черта, широту и долготу — знак ; Та же строка. Широта и долгота, разделённые вертикальной чертой вместо ;, не сохраняются
employee 1 — идентификатор сотрудника, список: GET /v1/users 1
crm "L_1000739" — буквенный префикс типа и идентификатор записи: L_ лид, C_ контакт Та же строка
crm_status "NEW" — значение statusId, список: GET /v1/statuses Та же строка
file ["contract.pdf", "BASE64_CONTENT"] — имя файла и содержимое в Base64 У поля с одним значением — объект с полями id, url, urlMachine, у множественного — массив таких объектов. Подробнее — Файлы в CRM

Настройка PRECISION задаётся при создании поля — "settings": { "PRECISION": 2 }, см. Создать поле.

Заголовок X-Vibe-Timezone из раздела Часовой пояс на записи на пользовательские поля не действует: значение без смещения читается в поясе портала.

Значения типов employee, crm и crm_status сохраняются без сверки со справочником портала: идентификатор несуществующего сотрудника или значение вне справочника принимаются и возвращаются как есть. Проверяйте их на своей стороне.

Несколько значений в одном поле

Признак множественности приходит в списке определений полей — поле multiple со значением Y или N. Для сделок, лидов, контактов, компаний, предложений и реквизитов это GET /v1/userfields/:entity, для смарт-процессов — GET /v1/items/:entityTypeId/userfields. У пользовательского поля в схеме GET /v1/{entity}/fields этого признака нет.

Значение множественного поля передаётся массивом и заменяет прежний набор целиком:

JSON
{ "ufCrmProjectTags": ["первое", "второе"] }

Одиночное значение вместо массива не сохраняется. Пустой массив и null оставляют прежние значения — очистить множественное поле пустым массивом или null нельзя.

Очистка значения

Поле с одним значением очищается значением null или пустой строкой, а файловое поле с одним значением — пустым массивом. В ответе очищенное поле приходит как null. Множественное поле ничем из перечисленного не очищается.

Особые сущности

Смарт-процессы (`items`)

Смарт-процессы используют entityTypeId в URL: GET /v1/items/{entityTypeId}.

Terminal
# Список записей смарт-процесса с entityTypeId = 1058
curl -H "X-Api-Key: $KEY" \
  "https://vibecode.bitrix24.tech/v1/items/1058?limit=10"

Список доступных смарт-процессов: GET /v1/smart-processes.

События календаря

Требуют обязательные параметры: type со значением user, group или company, и ownerId.

Terminal
curl -H "X-Api-Key: $KEY" \
  "https://vibecode.bitrix24.tech/v1/calendar-events?type=user&ownerId=1"

Файлы

Требуют folderId. Получите его из GET /v1/storages → поле rootFolderId.

Лимиты

10 запросов/секунду — лимит Битрикс24 на уровне портала, общий для всех ключей.

Как укладываться в лимит:

  • Batch API — 50 вызовов за 1 единицу лимита
  • POST /v1/{entity}/batch — до 500 записей CRUD за 10 единиц (а не за 500)
  • POST /v1/{entity}/aggregatecount идёт одним быстрым вызовом, а sum/avg/min/max подгружают до 5000 записей и считаются на стороне Вайбкод
  • Параметр select — загрузка только нужных полей сокращает объём ответа

Подробнее: Оптимизация — паттерны для дашбордов, массовых операций, сканирования больших объёмов

Запись ключом в режиме «только чтение»

Ключ с режимом доступа «только чтение» выполняет чтение и получает 403 WRITE_BLOCKED_READONLY_KEY на любой вызов записи — создание, обновление, удаление, действие над сущностью. Полное описание кода и полей detailsКоды ошибок. Как переключить режим и как работает политика портала — Режим доступа.

Паттерны использования

Задача Подход
Дашборд / аналитика POST /v1/{entity}/aggregate — счётчики и суммы по фильтру
Массовое обновление POST /v1/{entity}/batch с action=update
Данные из нескольких сущностей POST /v1/batchdeals, tasks и contacts в одном запросе
Запись + связанные данные ?include=company,contactсвязанные данные в одном ответе
Сканирование десятков тысяч записей Курсор по idorder[id]=asc плюс filter[>id] из meta.nextAfterId, см. Листание и количество записей. Где курсора нет — POST /v1/{entity}/search с limit до 5000 и сужением фильтра

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