1С Битрикс · 6 мин чтения

crm.deal.add и crm.contact.add: примеры запросов к REST Битрикс24

Метод 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] - это снижает число обращений к серверу и ускоряет обработку заявки, особенно при интеграции с ботами, где важна скорость ответа пользователю.

Есть задача?

Обсудим в мессенджере

Расскажите, что нужно сделать — отвечу в течение 4 часов в рабочее время. Первая консультация бесплатно.

Самозанятый Калинкин Н. А. · работаю с физлицами и юрлицами

Продолжая пользование настоящим сайтом Вы выражаете своё согласие на обработку Ваших персональных данных (файлов куки) с использованием Yandex.Metrika.
Понятно