Метод crm deal add в Битрикс24 REST API - это запрос, который я пишу почти в каждой интеграции сайта с CRM: клиент оставляет заявку на лендинге или в боте, скрипт создаёт контакт через crm.contact.add, затем сделку через crm.deal.add и привязывает её к этому контакту. За последние пару лет собирал такую связку для магазинов на WooCommerce, лендингов на Tilda, ботов на aiogram и сценариев в n8n. Ниже - рабочие примеры запросов и грабли, на которые я наступал.
Когда нужны crm.contact.add и crm.deal.add в связке
Сценарий почти всегда один: на сайте или в мессенджере пользователь оставляет контактные данные, и это нужно превратить в сделку с привязанным клиентом, а не просто в запись в таблице. Порядок действий такой:
- Ищу контакт по телефону или email методом crm.duplicate.findbycomm, чтобы не плодить дубли при повторных заявках.
- Если контакта нет - создаю его через crm.contact.add и получаю CONTACT_ID.
- Создаю сделку crm.deal.add, передавая полученный CONTACT_ID в поле CONTACT_IDS.
- При необходимости обновляю сделку после оплаты или доставки - например, когда приходит вебхук от эквайринга или трек-номер от СДЭК.
Если пропустить шаг с поиском дублей, через месяц в CRM накопится сотня одинаковых контактов с разными ID - это я видел на проектах, где форма Tilda дергала crm.contact.add напрямую без проверки.
Авторизация: вебхук или OAuth-приложение для запроса к CRM
Для большинства интеграций хватает входящего вебхука - токен создаётся в разделе «Разработчикам» в пару кликов и не требует публикации приложения. OAuth-приложение нужно только если код будет работать от имени нескольких порталов клиентов или публиковаться в маркетплейсе Битрикс24.
| Критерий | Входящий вебхук | OAuth-приложение |
|---|---|---|
| Скорость подключения | 5 минут | от 1-2 дней на регистрацию |
| Подходит для | одного портала, скриптов и ботов | решений для многих клиентов |
| Обновление токена | не требуется | refresh_token каждые сутки |
| Лимит запросов | 2 в секунду | 2 в секунду (можно поднять по заявке в поддержку Битрикс24) |
URL для вебхука выглядит так: https://ваш_портал.bitrix24.ru/rest/1/webhook_код/crm.contact.add.json - цифра после rest/ это ID пользователя, от имени которого выполняются действия, а не версия API.
crm.contact.add: создаём контакт первым запросом
Поля PHONE и EMAIL передаются массивом объектов с VALUE и VALUE_TYPE - это отличается от плоских полей вроде NAME и часто ломает интеграции, если копировать код без внимания к структуре.
curl -X POST
"https://your-portal.bitrix24.ru/rest/1/webhook_code/crm.contact.add.json"
-H "Content-Type: application/json"
-d '{
"fields": {
"NAME": "Иван",
"LAST_NAME": "Петров",
"PHONE": [{"VALUE": "+79261234567", "VALUE_TYPE": "MOBILE"}],
"EMAIL": [{"VALUE": "ivan@example.com", "VALUE_TYPE": "WORK"}],
"SOURCE_ID": "WEB"
},
"params": {"REGISTER_SONET_EVENT": "Y"}
}'
В ответ приходит {"result": 128} - это и есть CONTACT_ID, который пойдёт в следующий запрос. Перед созданием стоит вызвать crm.duplicate.findbycomm с типом PHONE или EMAIL: если совпадение найдено, использую существующий ID вместо нового контакта.
crm.deal.add: создаём сделку и привязываем к контакту
Сделка без привязки к контакту в отчётах выглядит бесхозной - менеджер видит сумму и название, но не понимает, кому звонить. Поэтому CONTACT_IDS всегда передаю вместе с созданием сделки, а не отдельным запросом на обновление.
const response = await fetch(
'https://your-portal.bitrix24.ru/rest/1/webhook_code/crm.deal.add.json',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
fields: {
TITLE: 'Заявка с сайта: Иван Петров',
CONTACT_IDS: [128],
STAGE_ID: 'NEW',
CURRENCY_ID: 'RUB',
OPPORTUNITY: 15000,
ASSIGNED_BY_ID: 1,
SOURCE_ID: 'WEB'
},
params: { REGISTER_SONET_EVENT: 'Y' }
})
}
);
const data = await response.json();
console.log(data.result); // ID новой сделки
STAGE_ID зависит от воронки - значения по умолчанию (NEW, PREPARATION, WON, LOSE) действуют только для основной воронки. Для дополнительных направлений продаж стадии имеют вид C2:NEW, где C2 - ID воронки, и его нужно смотреть через crm.dealcategory.stage.list, а не угадывать.
Бесплатный материал
🎁 Полезный скрипт в подарок
Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.
Без спама. Отписка в 1 клик.
Типичные ошибки при добавлении сделок и контактов
Большую часть багов в таких интеграциях я разбирал уже постфактум, когда клиент жаловался на «пропавшие» заявки. Список того, на что смотреть в первую очередь:
- ERROR_FIELDS_LACK - не передано обязательное поле, чаще всего TITLE в сделке или PHONE в контакте без VALUE_TYPE.
- Дубли контактов - форма создаёт новый контакт при каждой отправке вместо проверки по телефону. Решается вызовом crm.duplicate.findbycomm перед crm.contact.add.
- Лимит 2 запроса в секунду - при массовом импорте или интеграции с ботом, который шлёт заявки пачками, запросы нужно ставить в очередь с задержкой, иначе сервер вернёт код 503.
- Несовпадение CURRENCY_ID - если валюта портала EUR, а в запросе передан RUB без пересчёта, суммы в отчётах разъезжаются с фактом.
- Молчаливая потеря вебхука - если Битрикс24 недоступен дольше пары секунд, а скрипт не сохраняет заявку локально до ответа CRM, данные клиента теряются безвозвратно.
Последний пункт - самый обидный. На проекте с эквайрингом T‑Bank я в итоге завёл промежуточную таблицу в базе: заявка сначала пишется туда, и только потом уходит в Битрикс24 с отметкой об успехе. Если CRM недоступна, повторная отправка идёт по расписанию, а не теряется в логах.
Автоматизация связки в n8n, на Tilda и в Telegram-ботах
В n8n цепочка обычно строится из двух HTTP Request нод подряд: первая дергает crm.contact.add и возвращает result, вторая берёт это значение через expression {{$json.result}} и подставляет в CONTACT_IDS сделки - без единой строчки кода, только настройка нод.
На Tilda прямой вызов REST Битрикс24 из браузерного скрипта светит webhook-токеном в открытом виде, поэтому я всегда ставлю прокси-эндпоинт на своём сервере: Tilda отправляет данные формы туда, а сервер уже обращается к CRM с приватным токеном. Для готовых сценариев такого рода у меня есть подборка в библиотеке готовых скриптов - можно взять за основу и адаптировать под конкретную воронку.
В ботах на aiogram связка та же: пользователь заполняет анкету в диалоге, бот вызывает crm.contact.add по последнему сообщению с телефоном, затем crm.deal.add с указанием источника SOURCE_ID = ‘CALLBACK’ или отдельный ID для Telegram-канала - так в отчётах сразу видно, откуда пришла заявка.
| Канал | Где создаётся контакт | Особенность |
|---|---|---|
| Tilda-форма | на прокси-сервере, не в браузере | токен не светится в исходном коде страницы |
| n8n-сценарий | в HTTP Request ноде | без кода, но сложнее отлаживать ошибки полей |
| Telegram-бот на aiogram | в хендлере после сбора анкеты | легко добавить валидацию телефона до отправки в CRM |
Стоимость такой интеграции у меня начинается от 40 000 ₽ для комплексной связки Tilda с CRM и эквайрингом, автоматизация в n8n - от 25 000 ₽, разработка Telegram-бота с записью лидов в CRM - от 30 000 ₽. У других разработчиков на фрилансе цены на похожие задачи гуляют в диапазоне 15 000-100 000 ₽ в зависимости от того, входит ли в скоуп обработка ошибок и повторная отправка при сбоях CRM - это стоит уточнять до старта, иначе интеграция окажется рабочей только «в среднем».
crm.item.add как альтернатива для новых воронок
В актуальных версиях Битрикс24 сделки в смарт-процессах и универсальных списках создаются не через crm.deal.add, а через crm.item.add с параметром entityTypeId. Для классических сделок entityTypeId равен 2, и по факту это тот же набор полей, обёрнутый в новый метод:
import requests
url = "https://your-portal.bitrix24.ru/rest/1/webhook_code/crm.item.add.json"
payload = {
"entityTypeId": 2,
"fields": {
"title": "Заявка с сайта",
"contactId": 128,
"opportunity": 15000,
"currencyId": "RUB"
}
}
response = requests.post(url, json=payload)
print(response.json())
На практике crm.deal.add продолжает работать и никуда не девается даже на свежих порталах, но если проект использует смарт-процессы для нетиповых сущностей (например, заявки на ремонт или бронирования), логичнее сразу писать под crm.item.add - переучивать интеграцию потом дороже, чем заложить это на старте.
Чтобы сайт работал без сбоев
Техподдержка
от 15 000 ₽/мес
Подробнее →Частые вопросы
Можно ли создать сделку без предварительного создания контакта?
Можно передать данные клиента прямо в поле сделки без отдельного CONTACT_ID, но тогда в CRM появится сделка без карточки клиента, и при повторном обращении система не свяжет её с историей. На практике я всегда создаю контакт первым запросом, даже если это на один вызов API больше.
Как избежать дублей контактов при интеграции?
Перед crm.contact.add вызываю crm.duplicate.findbycomm с телефоном или email клиента. Если метод возвращает ID существующего контакта, использую его вместо создания нового - это экономит часы ручной чистки базы у менеджеров.
Сколько запросов в секунду выдерживает REST Битрикс24?
Официальный лимит - 2 запроса в секунду на один вебхук или приложение. При превышении сервер отвечает кодом 503 с пометкой QUERY_LIMIT_EXCEEDED, поэтому массовые операции нужно ставить в очередь с задержкой, а не слать пачкой.
Чем batch-запрос лучше отдельных вызовов crm.contact.add и crm.deal.add?
Метод batch позволяет объединить оба вызова в один HTTP-запрос и даже подставить результат первого метода в параметры второго через ссылку вида $result[contact][0] - это снижает число обращений к серверу и ускоряет обработку заявки, особенно при интеграции с ботами, где важна скорость ответа пользователю.