Добавить дело в новый лид или сделку в зависимости от режима CRM
Scope:
crmКто может выполнять методы: чтобы пройти сценарий целиком, нужно самое строгое из перечисленных прав — «право на изменение лидов и сделок»
- crm.lead.add — пользователь с правом на создание лидов
- crm.lead.get — пользователь с правом на чтение лидов
- crm.deal.list — пользователь с правом на чтение сделок
- crm.activity.todo.add — пользователь с правом на редактирование элемента CRM, в который добавляется дело
Выберите инструмент для разработки с AI-агентом:
- используйте Битрикс24 Вайбкод, чтобы создать приложение для Битрикс24 по описанию задачи без знания языков программирования. Агент напишет код и разместит приложение на сервере без ручной настройки хостинга
- используйте MCP-сервер, чтобы разрабатывать интеграцию через REST API в своем проекте. Агент будет обращаться к официальной REST-документации
На сайте можно разместить форму для сбора данных потенциальных клиентов. Когда клиент заполнит форму, его данные попадут в CRM. Вы сможете обработать заявку и позвонить клиенту.
В результате сценария в CRM появится новый лид, а в таймлайне — дело с напоминанием о звонке. К какому объекту привязано дело, зависит от режима работы CRM: в простом режиме — к сделке, которая получилась из лида, в классическом — к самому лиду.
Настройка состоит из двух этапов:
-
Подготавливаем поля и размещаем форму на странице
-
Создаем файл-обработчик, который вызывает последовательно методы crm.lead.add, crm.lead.get, crm.deal.list и crm.activity.todo.add
Режимы CRM
В Битрикс24 есть два режима работы CRM.
-
Простой режим — работает без лидов. Система автоматически конвертирует новый лид в сделку, а лид получает статус
CONVERTED -
Классический режим — разделяет потенциальных и действующих клиентов. Лид остается в системе со статусом
NEW
Дело нужно привязать к тому объекту, который в CRM появился на самом деле. Поэтому в обработчике после создания лида проверяем его статус и по нему выбираем, куда добавить напоминание о звонке.
Узнать, какой режим настроен в Битрикс24, можно методом crm.settings.mode.get. Он возвращает 1 для классического режима и 2 для простого. Но в сценарии мы опираемся не на эту настройку, а на статус конкретного лида: в классическом режиме лид тоже может сконвертироваться, если это делают роботы или другие инструменты автоматизации.
Пользовательская документация
Что нужно до начала
-
вебхук создан от имени пользователя с правом на создание лидов, на чтение сделок и на редактирование лидов и сделок
-
есть сервер, который отдает страницу с формой и принимает данные формы методом
POST. В примерах это Express для JS, PHP-скрипт и Flask для Python -
путь вебхука хранится в окружении, а не в коде страницы. Форма находится на публичной странице, и попадать в нее секрет не должен
-
поле
NAMEв форме обязательное. В простом режиме система конвертирует в сделку лид с заполненным именем
1. Создаем веб-форму
В Битрикс24 из лида можно автоматически создать контакт и компанию. Чтобы форма подходила для разных случаев, сделаем ее универсальной. Для контакта нужно указать имя и фамилию, а для компании — название. Создадим на странице сайта веб-форму с пятью полями:
-
NAME— имя клиента, обязательное поле -
LAST_NAME— фамилия -
COMPANY_TITLE— название компании -
PHONE— телефон -
EMAIL— электронная почта
Форма передает данные методом POST в обработчик.
Как использовать примеры в документации
<form id="form_to_crm">
<!-- Имя (обязательное поле) -->
<input type="text" name="NAME" placeholder="Имя" required>
<!-- Фамилия -->
<input type="text" name="LAST_NAME" placeholder="Фамилия">
<!-- Название компании -->
<input type="text" name="COMPANY_TITLE" placeholder="Название компании">
<!-- Email -->
<input type="text" name="EMAIL" placeholder="Почта">
<!-- Телефон -->
<input type="text" name="PHONE" placeholder="Телефон">
<!-- Кнопка отправки -->
<input type="submit" value="Отправить">
</form>
<script>
document.getElementById('form_to_crm').addEventListener('submit', async (el) => {
el.preventDefault(); // Отменяем стандартную отправку формы
// Собираем данные формы в JSON
const formData = Object.fromEntries(new FormData(el.currentTarget).entries());
// Отправляем данные на сервер (эндпоинт обработчика на Node.js)
const response = await fetch('/form', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData),
});
const data = await response.json();
alert(data.message); // Показываем результат
});
</script>
<form id="form_to_crm">
<!-- Имя (обязательное поле) -->
<input type="text" name="NAME" placeholder="Имя" required>
<!-- Фамилия -->
<input type="text" name="LAST_NAME" placeholder="Фамилия">
<!-- Название компании -->
<input type="text" name="COMPANY_TITLE" placeholder="Название компании">
<!-- Email -->
<input type="text" name="EMAIL" placeholder="Почта">
<!-- Телефон -->
<input type="text" name="PHONE" placeholder="Телефон">
<!-- Кнопка отправки -->
<input type="submit" value="Отправить">
</form>
<!-- Подключаем jQuery для AJAX-запроса -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
$('#form_to_crm').on('submit', function(el) {
el.preventDefault(); // Отменяем стандартную отправку формы
var formData = $(this).serialize(); // Собираем данные формы
// Отправляем данные на сервер (маршрут обработчика на Flask)
$.ajax({
'method': 'POST',
'dataType': 'json',
'url': '/form', // Маршрут-обработчик
'data': formData,
success: function(data) {
alert(data.message); // Показываем результат
}
});
});
});
</script>
<form id="form_to_crm" method="POST" action="form.php">
<!-- Имя (обязательное поле) -->
<input type="text" name="NAME" placeholder="Имя" required>
<!-- Фамилия -->
<input type="text" name="LAST_NAME" placeholder="Фамилия">
<!-- Название компании -->
<input type="text" name="COMPANY_TITLE" placeholder="Название компании">
<!-- Email -->
<input type="text" name="EMAIL" placeholder="Почта">
<!-- Телефон -->
<input type="text" name="PHONE" placeholder="Телефон">
<!-- Кнопка отправки -->
<input type="submit" value="Отправить">
</form>
<!-- Подключаем jQuery для AJAX-запроса -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
$('#form_to_crm').on('submit', function(el) {
el.preventDefault(); // Отменяем стандартную отправку формы
var formData = $(this).serialize(); // Собираем данные формы
// Отправляем данные на сервер
$.ajax({
'method': 'POST',
'dataType': 'json',
'url': 'form.php', // Файл-обработчик
'data': formData,
success: function(data) {
alert(data.message); // Показываем результат
}
});
});
});
</script>
2. Создаем обработчик формы
Обработчик принимает значения полей формы, создает лид, проверяет его статус и добавляет дело с напоминанием о звонке в лид или в сделку.
Подготавливаем данные из формы
Читаем поля NAME, LAST_NAME, COMPANY_TITLE, PHONE, EMAIL и приводим их к строке. Если поле не заполнено, получаем пустую строку, а не undefined или None.
Форму заполняет посетитель сайта, поэтому значения нельзя считать безопасными. В примере на PHP их дополнительно пропускаем через htmlspecialchars. Если вы возвращаете эти значения обратно на страницу, экранируйте их и в остальных примерах.
// Получаем данные из формы
const sName = String(req.body.NAME ?? '')
const sLastName = String(req.body.LAST_NAME ?? '')
const sCompanyTitle = String(req.body.COMPANY_TITLE ?? '')
const sPhone = String(req.body.PHONE ?? '')
const sEmail = String(req.body.EMAIL ?? '')
# Получаем данные из формы
s_name = request.form.get("NAME", "")
s_last_name = request.form.get("LAST_NAME", "")
s_company_title = request.form.get("COMPANY_TITLE", "")
s_phone = request.form.get("PHONE", "")
s_email = request.form.get("EMAIL", "")
// Получаем и очищаем данные из формы
$sName = htmlspecialchars($_POST["NAME"]);
$sLastName = htmlspecialchars($_POST["LAST_NAME"]);
$sCompanyTitle = htmlspecialchars($_POST["COMPANY_TITLE"]);
$sPhone = htmlspecialchars($_POST["PHONE"]);
$sEmail = htmlspecialchars($_POST["EMAIL"]);
Телефон и почту система хранит как массив объектов crm_multifield, поэтому их нужно привести к формату массива.
-
Если значение есть, записываем его в поле
VALUE, а в полеVALUE_TYPEпередаем тип, напримерWORKдля телефона иHOMEдля почты -
Если значения нет, передаем пустой массив
// Форматируем телефон и почту для Битрикс24 в формат crm_multifield
const arPhone = sPhone ? [{ VALUE: sPhone, VALUE_TYPE: 'WORK' }] : []
const arEmail = sEmail ? [{ VALUE: sEmail, VALUE_TYPE: 'HOME' }] : []
# Форматируем телефон и почту для Битрикс24 в формат crm_multifield
ar_phone = [{"VALUE": s_phone, "VALUE_TYPE": "WORK"}] if s_phone else []
ar_email = [{"VALUE": s_email, "VALUE_TYPE": "HOME"}] if s_email else []
// Форматируем телефон и почту для Битрикс24 в формат crm_multifield
$arPhone = (!empty($sPhone)) ? array(array('VALUE' => $sPhone, 'VALUE_TYPE' => 'WORK')) : array();
$arEmail = (!empty($sEmail)) ? array(array('VALUE' => $sEmail, 'VALUE_TYPE' => 'HOME')) : array();
Заголовок лида сформируем из имени и фамилии. Для компаний добавим в заголовок название компании.
// Формируем заголовок лида из имени и фамилии
let sTitle = 'С сайта: ' + `${sName} ${sLastName}`.trim()
// Если есть название компании — добавляем его через тире после имени и фамилии
if (sCompanyTitle) {
sTitle += ' — ' + sCompanyTitle
}
# Формируем заголовок лида из имени и фамилии
s_title = "С сайта: " + f"{s_name} {s_last_name}".strip()
# Если есть название компании — добавляем его через тире после имени и фамилии
if s_company_title:
s_title += " — " + s_company_title
// Формируем заголовок лида из имени и фамилии
$sTitle = 'С сайта: ' . trim($sName . ' ' . $sLastName);
// Если есть название компании — добавляем его через тире после имени и фамилии
if (!empty($sCompanyTitle)) {
$sTitle .= ' — ' . $sCompanyTitle;
}
Создаем лид и получаем данные лида
Последовательно выполним два метода: создадим лид и получим его данные.
Для добавления лида используем метод crm.lead.add. В объекте fields передаем поля:
-
TITLE— заголовок лида из переменной$sTitle -
NAME— имя из поля формыNAME -
LAST_NAME— фамилия из поля формыLAST_NAME -
COMPANY_TITLE— название компании из поля формыCOMPANY_TITLE -
PHONE— телефон в форматеcrm_multifieldиз переменной$arPhone -
EMAIL— почта в форматеcrm_multifieldиз переменной$arEmail
Метод вернет идентификатор нового лида — сохраняем его в переменную $leadId. Он нужен на следующих шагах: чтобы получить статус лида и чтобы найти сделку, если лид сконвертирован.
Проверьте, какие обязательные поля настроены для лидов в вашем Битрикс24. Все обязательные поля нужно передать в метод crm.lead.add.
const addLead = await $b24.actions.v2.call.make({
method: 'crm.lead.add',
params: {
fields: {
TITLE: sTitle, // Заголовок лида
NAME: sName, // Имя
LAST_NAME: sLastName, // Фамилия
COMPANY_TITLE: sCompanyTitle, // Название компании
PHONE: arPhone, // Телефон
EMAIL: arEmail, // Email
}
},
requestId: 'add-lead'
})
const leadId = addLead.getData().result
lead_id = client.crm.lead.add(fields={
"TITLE": s_title, # Заголовок лида
"NAME": s_name, # Имя
"LAST_NAME": s_last_name, # Фамилия
"COMPANY_TITLE": s_company_title, # Название компании
"PHONE": ar_phone, # Телефон
"EMAIL": ar_email, # Email
}).result
$leadId = $sb->getCRMScope()->lead()->add([
'TITLE' => $sTitle, // Заголовок лида
'NAME' => $sName, // Имя
'LAST_NAME' => $sLastName, // Фамилия
'COMPANY_TITLE' => $sCompanyTitle, // Название компании
'PHONE' => $arPhone, // Телефон
'EMAIL' => $arEmail, // Email
])->getId();
Для получения данных лида используем метод crm.lead.get. В параметр ID передаем идентификатор лида, полученный из результата метода crm.lead.add.
const getLead = await $b24.actions.v2.call.make({
method: 'crm.lead.get',
params: { id: leadId }, // ID из результата выполнения метода crm.lead.add
requestId: 'get-lead'
})
const leadStatus = getLead.getData().result.STATUS_ID
lead = client.crm.lead.get(bitrix_id=lead_id).result # ID из результата crm.lead.add
lead_status = lead["STATUS_ID"]
$lead = $sb->getCRMScope()->lead()->get($leadId)->lead(); // ID из результата crm.lead.add
$leadStatus = $lead->STATUS_ID;
В результате метод crm.lead.add вернет идентификатор нового лида, а метод crm.lead.get — данные лида, включая поле STATUS_ID.
{
"result": {
"ID": "22",
"TITLE": "С сайта: Иван Иванов",
"HONORIFIC": null,
"NAME": "Иван",
"SECOND_NAME": null,
"LAST_NAME": "Иванов",
"COMPANY_TITLE": null,
"STATUS_ID": "CONVERTED"
}
}
Ответ сокращен: метод возвращает все поля лида. Для сценария важно только STATUS_ID.
Определяем, куда добавить дело
По значению переменной $leadStatus выбираем ветку сценария.
-
CONVERTED— лид уже сконвертирован в сделку. Находим сделку и добавляем дело в нее -
любое другое значение, например
NEW— лид остался лидом. Добавляем дело прямо в него
В обеих ветках дело добавляет метод crm.activity.todo.add. Передаем поля:
-
ownerTypeId— идентификатор типа объекта CRM. Получить идентификаторы можно методом crm.enum.ownertype. Значение зависит от ветки:1— лид,2— сделка -
ownerId— идентификатор элемента CRM. Зависит от ветки: идентификатор лида или сделки -
deadline— крайний срок дела. Передаем время в формате2026-08-19 15:00:00или2026-08-19T15:00:00, метод принимает оба -
title— название дела -
description— описание дела
Простой режим
Сам лид уже не нужен — дело добавим в сделку. Чтобы получить ее идентификатор, используем метод crm.deal.list. Укажем в select поле ID, а в фильтр filter передадим поле LEAD_ID с идентификатором лида из переменной $leadId.
// Простой режим: ищем сделку, созданную из лида
const resultDeal = await $b24.actions.v2.callList.make({
method: 'crm.deal.list',
params: { select: ['ID'], filter: { LEAD_ID: leadId } },
requestId: 'deal-list'
})
const deals = resultDeal.getData().result
# Простой режим: ищем сделку, созданную из лида
deals = client.crm.deal.list(
filter={"LEAD_ID": lead_id}, select=["ID"],
).as_list().result
// Простой режим: ищем сделку, созданную из лида
$deals = $sb->getCRMScope()->deal()->list(
order: [],
filter: ['LEAD_ID' => $leadId],
select: ['ID']
)->getDeals();
В результате получим массив сделок. Из одного лида получается одна сделка, поэтому берем первый элемент.
{
"result": [
{
"ID": "1811"
}
],
"total": 1
}
Дело добавляем в сделку: в ownerTypeId передаем 2, в ownerId — идентификатор сделки из переменной $deals.
if (deals.length && deals[0].ID) {
const dealId = deals[0].ID
// Привязываем дело к сделке
await $b24.actions.v2.call.make({
method: 'crm.activity.todo.add',
params: {
ownerTypeId: 2, // тип объекта — сделка
ownerId: dealId, // идентификатор сделки
deadline: new Date(Date.now() + 3600 * 1000).toISOString(), // текущее время + 1 час
title: 'Позвонить клиенту',
description: 'Заполнил заявку на сайте',
},
requestId: 'todo-deal'
})
}
from datetime import datetime, timedelta
if deals:
deal_id = deals[0]["ID"]
deadline = (datetime.now() + timedelta(hours=1)).strftime("%Y-%m-%d %H:%M:%S") # +1 час
# Привязываем дело к сделке — метод crm.activity.todo.add вызываем напрямую
token.call_method("crm.activity.todo.add", {
"ownerTypeId": 2, # тип объекта — сделка
"ownerId": int(deal_id), # идентификатор сделки
"deadline": deadline,
"title": "Позвонить клиенту",
"description": "Заполнил заявку на сайте",
})
if (!empty($deals)) {
$dealId = $deals[0]->ID;
// Привязываем дело к сделке — для crm.activity.todo.add в SDK нет обертки, вызываем напрямую
$sb->core->call('crm.activity.todo.add', [
'ownerTypeId' => 2, // тип объекта — сделка
'ownerId' => $dealId, // идентификатор сделки
'deadline' => date("Y-m-d H:i:s", time() + 3600), // текущее время + 1 час
'title' => 'Позвонить клиенту',
'description' => 'Заполнил заявку на сайте',
]);
}
Классический режим
Сделки нет, поэтому дело привязываем к самому лиду. Дополнительный запрос не нужен: идентификатор лида уже лежит в переменной $leadId.
Дело добавляем в лид: в ownerTypeId передаем 1, в ownerId — идентификатор нового лида из переменной $leadId.
// Классический режим: привязываем дело к лиду
await $b24.actions.v2.call.make({
method: 'crm.activity.todo.add',
params: {
ownerTypeId: 1, // тип объекта — лид
ownerId: leadId, // идентификатор лида
deadline: new Date(Date.now() + 3600 * 1000).toISOString(), // текущее время + 1 час
title: 'Позвонить клиенту',
description: 'Заполнил заявку на сайте',
},
requestId: 'todo-lead'
})
from datetime import datetime, timedelta
deadline = (datetime.now() + timedelta(hours=1)).strftime("%Y-%m-%d %H:%M:%S") # +1 час
# Классический режим: привязываем дело к лиду — метод crm.activity.todo.add вызываем напрямую
token.call_method("crm.activity.todo.add", {
"ownerTypeId": 1, # тип объекта — лид
"ownerId": lead_id, # идентификатор лида
"deadline": deadline,
"title": "Позвонить клиенту",
"description": "Заполнил заявку на сайте",
})
// Классический режим: привязываем дело к лиду
$sb->core->call('crm.activity.todo.add', [
'ownerTypeId' => 1, // тип объекта — лид
'ownerId' => $leadId, // идентификатор лида
'deadline' => date("Y-m-d H:i:s", time() + 3600), // текущее время + 1 час
'title' => 'Позвонить клиенту',
'description' => 'Заполнил заявку на сайте',
]);
Метод вернет идентификатор созданного дела.
{
"result": {
"id": 999
}
}
Полный пример кода обработчика
import express from 'express'
import { B24Hook } from '@bitrix24/b24jssdk'
const $b24 = B24Hook.fromWebhookUrl(process.env.B24_HOOK)
// B24_HOOK = 'https://your-domain.bitrix24.ru/rest/USER_ID/TOKEN/'
const app = express()
app.use(express.json())
// Обработчик принимает данные формы по маршруту /form
app.post('/form', async (req, res) => {
// Получаем и очищаем данные из формы
const sName = String(req.body.NAME ?? '')
const sLastName = String(req.body.LAST_NAME ?? '')
const sCompanyTitle = String(req.body.COMPANY_TITLE ?? '')
const sPhone = String(req.body.PHONE ?? '')
const sEmail = String(req.body.EMAIL ?? '')
// Форматируем телефон и почту для Битрикс24 в формат crm_multifield
const arPhone = sPhone ? [{ VALUE: sPhone, VALUE_TYPE: 'WORK' }] : []
const arEmail = sEmail ? [{ VALUE: sEmail, VALUE_TYPE: 'HOME' }] : []
// Формируем заголовок лида из имени и фамилии
let sTitle = 'С сайта: ' + `${sName} ${sLastName}`.trim()
if (sCompanyTitle) {
sTitle += ' — ' + sCompanyTitle
}
try {
// Создаем лид
const addLead = await $b24.actions.v2.call.make({
method: 'crm.lead.add',
params: {
fields: {
TITLE: sTitle, NAME: sName, LAST_NAME: sLastName,
COMPANY_TITLE: sCompanyTitle, PHONE: arPhone, EMAIL: arEmail,
}
},
requestId: 'add-lead'
})
const leadId = addLead.getData().result
// Получаем данные лида
const getLead = await $b24.actions.v2.call.make({
method: 'crm.lead.get', params: { id: leadId }, requestId: 'get-lead'
})
const leadStatus = getLead.getData().result.STATUS_ID
const deadline = new Date(Date.now() + 3600 * 1000).toISOString() // текущее время + 1 час
if (leadStatus === 'CONVERTED') {
// Простой режим: ищем сделку, созданную из лида
const resultDeal = await $b24.actions.v2.callList.make({
method: 'crm.deal.list',
params: { select: ['ID'], filter: { LEAD_ID: leadId } },
requestId: 'deal-list'
})
const deals = resultDeal.getData().result
if (deals.length && deals[0].ID) {
// Добавляем дело в сделку
await $b24.actions.v2.call.make({
method: 'crm.activity.todo.add',
params: {
ownerTypeId: 2, ownerId: deals[0].ID, deadline,
title: 'Позвонить клиенту', description: 'Заполнил заявку на сайте',
},
requestId: 'todo-deal'
})
}
} else {
// Классический режим: добавляем дело в новый лид
await $b24.actions.v2.call.make({
method: 'crm.activity.todo.add',
params: {
ownerTypeId: 1, ownerId: leadId, deadline,
title: 'Позвонить клиенту', description: 'Заполнил заявку на сайте',
},
requestId: 'todo-lead'
})
}
res.json({ message: 'Дело добавлено в лид или сделку' })
} catch (e) {
res.json({ message: e.message })
}
})
app.listen(3000)
# pip install b24pysdk flask
import os
from datetime import datetime, timedelta
from flask import Flask, request, jsonify
from b24pysdk import BitrixWebhook, Client
app = Flask(__name__)
token = BitrixWebhook(
domain="your-domain.bitrix24.ru",
webhook_token=os.environ["B24_HOOK_TOKEN"],
)
# B24_HOOK_TOKEN = 'USER_ID/TOKEN' — только user_id и токен, без https://
client = Client(token)
@app.route("/form", methods=["POST"])
def handle_form():
# Получаем и очищаем данные из формы
s_name = request.form.get("NAME", "")
s_last_name = request.form.get("LAST_NAME", "")
s_company_title = request.form.get("COMPANY_TITLE", "")
s_phone = request.form.get("PHONE", "")
s_email = request.form.get("EMAIL", "")
# Форматируем телефон и почту для Битрикс24 в формат crm_multifield
ar_phone = [{"VALUE": s_phone, "VALUE_TYPE": "WORK"}] if s_phone else []
ar_email = [{"VALUE": s_email, "VALUE_TYPE": "HOME"}] if s_email else []
# Формируем заголовок лида из имени и фамилии
s_title = "С сайта: " + f"{s_name} {s_last_name}".strip()
if s_company_title:
s_title += " — " + s_company_title
try:
# Создаем лид
lead_id = client.crm.lead.add(fields={
"TITLE": s_title, "NAME": s_name, "LAST_NAME": s_last_name,
"COMPANY_TITLE": s_company_title, "PHONE": ar_phone, "EMAIL": ar_email,
}).result
# Получаем данные лида
lead_status = client.crm.lead.get(bitrix_id=lead_id).result["STATUS_ID"]
deadline = (datetime.now() + timedelta(hours=1)).strftime("%Y-%m-%d %H:%M:%S") # +1 час
if lead_status == "CONVERTED":
# Простой режим: ищем сделку, созданную из лида
deals = client.crm.deal.list(filter={"LEAD_ID": lead_id}, select=["ID"]).as_list().result
if deals:
# Добавляем дело в сделку (crm.activity.todo.add — вызываем напрямую)
token.call_method("crm.activity.todo.add", {
"ownerTypeId": 2, "ownerId": int(deals[0]["ID"]), "deadline": deadline,
"title": "Позвонить клиенту", "description": "Заполнил заявку на сайте",
})
else:
# Классический режим: добавляем дело в новый лид
token.call_method("crm.activity.todo.add", {
"ownerTypeId": 1, "ownerId": lead_id, "deadline": deadline,
"title": "Позвонить клиенту", "description": "Заполнил заявку на сайте",
})
return jsonify({"message": "Дело добавлено в лид или сделку"})
except Exception as e:
return jsonify({"message": str(e)})
<?php
// composer require bitrix24/b24phpsdk:"^3.0"
require_once 'vendor/autoload.php';
use Bitrix24\SDK\Services\ServiceBuilderFactory;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Psr\Log\NullLogger;
$sb = (new ServiceBuilderFactory(new EventDispatcher(), new NullLogger()))
->initFromWebhook(getenv('B24_HOOK'));
// B24_HOOK = 'https://your-domain.bitrix24.ru/rest/USER_ID/TOKEN/'
$crm = $sb->getCRMScope();
// Получаем и очищаем данные из формы
$sName = htmlspecialchars($_POST["NAME"]);
$sLastName = htmlspecialchars($_POST["LAST_NAME"]);
$sCompanyTitle = htmlspecialchars($_POST["COMPANY_TITLE"]);
$sPhone = htmlspecialchars($_POST["PHONE"]);
$sEmail = htmlspecialchars($_POST["EMAIL"]);
// Форматируем телефон и почту для Битрикс24 в формат crm_multifield
$arPhone = (!empty($sPhone)) ? array(array('VALUE' => $sPhone, 'VALUE_TYPE' => 'WORK')) : array();
$arEmail = (!empty($sEmail)) ? array(array('VALUE' => $sEmail, 'VALUE_TYPE' => 'HOME')) : array();
// Формируем заголовок лида из имени и фамилии
$sTitle = 'С сайта: ' . trim($sName . ' ' . $sLastName);
if (!empty($sCompanyTitle)) {
$sTitle .= ' — ' . $sCompanyTitle;
}
try {
// Создаем лид
$leadId = $crm->lead()->add([
'TITLE' => $sTitle, 'NAME' => $sName, 'LAST_NAME' => $sLastName,
'COMPANY_TITLE' => $sCompanyTitle, 'PHONE' => $arPhone, 'EMAIL' => $arEmail,
])->getId();
// Получаем данные лида
$leadStatus = $crm->lead()->get($leadId)->lead()->STATUS_ID;
$deadline = date("Y-m-d H:i:s", time() + 3600); // текущее время + 1 час
if ($leadStatus == 'CONVERTED') {
// Простой режим: ищем сделку, созданную из лида
$deals = $crm->deal()->list(order: [], filter: ['LEAD_ID' => $leadId], select: ['ID'])->getDeals();
if (!empty($deals)) {
// Добавляем дело в сделку — у crm.activity.todo.add нет обертки, вызываем напрямую
$sb->core->call('crm.activity.todo.add', [
'ownerTypeId' => 2, 'ownerId' => $deals[0]->ID, 'deadline' => $deadline,
'title' => 'Позвонить клиенту', 'description' => 'Заполнил заявку на сайте',
]);
}
} else {
// Классический режим: добавляем дело в новый лид
$sb->core->call('crm.activity.todo.add', [
'ownerTypeId' => 1, 'ownerId' => $leadId, 'deadline' => $deadline,
'title' => 'Позвонить клиенту', 'description' => 'Заполнил заявку на сайте',
]);
}
echo json_encode(['message' => 'Дело добавлено в лид или сделку']);
} catch (\Throwable $e) {
echo json_encode(['message' => $e->getMessage()]);
}
Проверим результат
Откройте созданный лид в Битрикс24. Если CRM работает в классическом режиме, в таймлайне лида появится дело «Позвонить клиенту» с крайним сроком через час. В простом режиме лид будет сконвертирован, а дело окажется в таймлайне сделки.
Через REST дела объекта проверяет метод crm.activity.list с фильтром по владельцу: OWNER_TYPE_ID — 1 для лида и 2 для сделки, OWNER_ID — идентификатор объекта.
const checkResponse = await $b24.actions.v2.callList.make({
method: 'crm.activity.list',
params: {
filter: { OWNER_TYPE_ID: 1, OWNER_ID: leadId },
select: ['ID', 'SUBJECT', 'OWNER_TYPE_ID', 'OWNER_ID']
},
requestId: 'activity-list'
})
console.dir(checkResponse.getData().result)
activities = client.crm.activity.list(
filter={"OWNER_TYPE_ID": 1, "OWNER_ID": lead_id},
select=["ID", "SUBJECT", "OWNER_TYPE_ID", "OWNER_ID"],
).response.result
$activities = $sb->getCRMScope()->activity()->list(
[],
['OWNER_TYPE_ID' => 1, 'OWNER_ID' => $leadId],
['ID', 'SUBJECT', 'OWNER_TYPE_ID', 'OWNER_ID'],
0
)->getActivities();
Сценарий выполнен, если:
-
метод crm.lead.add вернул идентификатор лида
-
метод crm.activity.todo.add вернул объект с идентификатором дела
id -
в списке дел объекта есть дело с темой «Позвонить клиенту», а его
OWNER_TYPE_IDиOWNER_IDуказывают на лид или на сделку — в зависимости от того, по какой ветке пошел сценарий
Ошибки и диагностика
Если метод вернул ошибку, проверьте данные запроса.
|
Код |
Причина и действие |
|
Пустое значение |
У пользователя нет прав на создание лидов. Проверьте, от имени какого пользователя создан вебхук |
|
|
У пользователя нет прав на редактирование объекта, в который добавляется дело. Право нужно и на лид, и на сделку |
|
|
В crm.activity.todo.add не переданы обязательные поля |
|
|
Объект, указанный в |
|
|
Значение |
Лид может создаться, а дело — не появиться там, где вы его ждете. Проверьте по порядку:
-
crm.lead.get вернул
STATUS_IDсо значениемNEW, хотя CRM работает в простом режиме. Лид не сконвертирован — проверьте, что полеNAMEзаполнено -
статус равен
CONVERTED, но crm.deal.list вернул пустой список. У пользователя вебхука нет прав на чтение сделок. Сделка создана, но в выборку не попала -
дело добавилось в лид, хотя вы ожидали его в сделке. Значит, на момент проверки лид еще не был сконвертирован
Повторяйте сценарий с того шага, который вернул ошибку. Получение лида и списка сделок ничего не создают, их можно выполнять сколько угодно раз. Если ошибку вернул crm.lead.add, лид не создан: исправьте fields и повторите только этот вызов. Если ошибку вернул crm.activity.todo.add, лид уже существует — повторяйте только добавление дела, иначе получите дубликат лида.
Что важно учитывать
-
вебхуку нужны права сразу на два типа объектов. Ветку выбирает система, и заранее неизвестно, попадет дело в лид или в сделку
-
у метода crm.activity.todo.add нет типизированной обертки в B24PhpSDK и b24pysdk, поэтому его вызываем через ядро SDK
-
повторная отправка формы с теми же данными каждый раз создает новый лид. Дубликаты не отсеиваются. Чтобы связывать повторные обращения, используйте сценарий Добавить повторный лид
-
напоминание о звонке можно настроить точнее: передайте в crm.activity.todo.add параметр
pingOffsets, например[0, 15]— уведомления придут за 15 минут до крайнего срока и в момент его наступления