Бронирование ресурсов в Битрикс24: как создать бронь через REST
В Битрикс24 два разных механизма бронирования с почти одинаковыми названиями. Из-за этого попытка создать бронь для сделки заканчивается ошибкой «Some resources were not found», а метод, который напрашивается по логике, в списке REST просто отсутствует. Разбираем, где проходит граница и как записать бронь в поле сделки.
Почему booking.v1.booking.add отвечает «Some resources were not found»?
Потому что вы передаёте в него идентификатор ресурса из другой подсистемы. В портале есть два независимых механизма бронирования, и реестры ресурсов у них разные. Метод ищет ваш ID в своём реестре, не находит и возвращает ошибку — хотя ресурс на портале есть и в интерфейсе виден.
| Бронирование ресурсов | Бронирование и онлайн-запись | |
|---|---|---|
| Модуль | calendar | booking |
| Ресурсы | calendar.resource.* | booking.v1.resource.* |
| Брони | calendar.resource.booking.list — только чтение | booking.v1.booking.* — полный набор |
| Связь с CRM | поле сделки или лида типа resourcebooking | booking.v1.booking.externalData.* |
Реестры не пересекаются. Новый модуль бронирования не заполняет пользовательское поле сделки, а старый ничего не знает про ресурсы нового. Выбор механизма — это выбор один раз и навсегда.
Каким методом создать бронь, если calendar.resource.booking.add не существует?
Никаким: отдельного метода создания нет и не планировалось. В этой подсистеме бронь — побочный эффект сохранения пользовательского поля. Вы обновляете сделку методом crm.deal.update, а обработчик типа поля сам создаёт событие календаря и запись в реестре броней.
Поэтому единственный рабочий путь — записать значение в поле типа resourcebooking. Найти такое поле у сделок можно через crm.deal.userfield.list с фильтром по USER_TYPE_ID, список ресурсов и их идентификаторы — через calendar.resource.list.
В каком формате передавать значение в поле?
Поле множественное, каждый элемент — строка из пяти частей, разделённых вертикальной чертой.
тип|ID|дата_начала|длительность_в_секундах|название_услуги
resource|586|10.09.2026 15:00:00|3600|Консультация- тип — resource для ресурса из календаря или user для брони времени сотрудника
- ID — идентификатор ресурса либо идентификатор пользователя портала
- дата начала — в формате сайта, то есть 10.09.2026 15:00:00
- длительность — в секундах: 3600 равно часу
- название услуги — необязательно, попадёт в описание события строкой «Услуга: …»
curl -X POST -H "Content-Type: application/json" \
-d '{
"id": 1234,
"fields": {
"UF_CRM_1712345678": [
"resource|586|10.09.2026 15:00:00|3600|Консультация"
]
}
}' \
https://portal.bitrix24.ru/rest/1/WEBHOOK/crm.deal.updateПочему в поле лежит [5], а не то, что вы отправили?
Потому что при сохранении строка разбирается и заменяется. Обработчик поля создаёт событие календаря, добавляет строку в реестр броней и кладёт в поле идентификатор этой строки. То, что вы прочитаете через crm.deal.get, — не ваши данные, а ссылка на созданную бронь.
По этому же идентификатору бронь потом читается. В фильтре метода чтения два взаимоисключающих ключа: resourceIdList — это значения из поля сделки, resourceTypeIdList — идентификаторы самих ресурсов. Передать оба сразу нельзя.
# читаем бронь по значению из поля сделки
{"filter": {"resourceIdList": [5], "from": "2026-09-01", "to": "2026-09-30"}}
# читаем всю загрузку ресурса за период
{"filter": {"resourceTypeIdList": [586], "from": "2026-09-01", "to": "2026-09-30"}}Как обновить или снять бронь, не потеряв остальные?
Передавать полный набор значений. Обработчик сравнивает то, что пришло, с тем, что уже есть у сделки, и всё, чего в новом массиве нет, освобождает: удаляет событие календаря и строку реестра. Отправите одну новую бронь вместо двух существующих — вторая исчезнет молча.
- Чтобы снять все брони — передайте в поле пустой массив
- Чтобы добавить бронь, сохранив прежние, — передайте прежние числовыми идентификаторами вместе с новой строкой
- Чтобы сдвинуть время существующей брони — передайте её заново полной строкой с новыми датой и длительностью
# 5 — уже существующая бронь, её оставляем как есть;
# вторым элементом добавляем новую
"UF_CRM_1712345678": ["5", "resource|590|11.09.2026 09:00:00|1800|Осмотр"]На чём это ломается в реальной интеграции?
На трёх вещах, ни одна из которых не видна из справки REST.
Часовой пояс в строке не передаётся вообще. Он берётся из настроек самого поля: либо это фиксированный пояс из настроек, либо пояс текущего пользователя — то есть того, чьим ключом работает вебхук. Если вебхук создан от одного сотрудника, а брони ставятся для филиала в другом часовом поясе, время уедет, и никакой ошибки не будет.
Режим «весь день» меняет смысл длительности. Если у поля включена соответствующая настройка, время в дате игнорируется, а длительность считается сутками — та же строка даст совсем другую бронь.
Пересечения не проверяются. В коде обработчика есть метод с говорящим названием, который должен проверять доступность ресурса, — и он пустой. При сохранении он не вызывается ни разу. Портал спокойно поставит две брони на один ресурс и одно время, если вы этого не проверите сами.
Практический вывод: перед записью брони запрашивайте занятость ресурса за нужный период и решайте конфликт на своей стороне. Рассчитывать на то, что портал откажет, нельзя.
Что выбрать, если бронирование только внедряется?
Если бронь должна жить внутри сделки и быть её полем — берите механизм на календаре: он собран именно вокруг карточки CRM, но управляется только через сохранение поля и почти не документирован.
Если нужен полноценный API с созданием, изменением, удалением и листом ожидания — берите модуль бронирования. Тогда ресурсы заводятся в нём заново, а сделка привязывается отдельным методом. Поле resourcebooking в карточке при этом останется пустым: это два параллельных мира, и смешать их не получится.
Интеграция упирается в недокументированное поведение
Мы читаем исходники модулей портала, а не только справку REST: находим фактический формат данных и делаем интеграцию, которая не разваливается на первом же нестандартном поле.