Сделка переходит на стадию «Оплата счёта» — и в этот момент внешняя система (например, платёжный шлюз или служба доставки) должна об этом узнать. Без интеграции менеджер вручную копирует номер сделки и сумму в другой интерфейс, а это минуты простоя и посторонние ошибки при копипасте. Бизнес-процессы Bitrix24 умеют вызывать внешний URL сами, без участия человека — именно про это и пойдёт речь: интеграция бизнес-процессов Bitrix24 с внешними системами через webhook, от простого одностороннего вызова до действия, которое дожидается ответа и продолжает процесс дальше. Статья для разработчиков, которые уже настраивали бизнес-процессы в конструкторе и хотят подключить к ним внешний API.
Сразу оговорка по терминологии, чтобы не путаться в шагах ниже: в конструкторе бизнес-процессов есть готовое действие с названием «WebHook», и оно умеет только одно — отправить GET-запрос и забыть о нём. Если нужно передать данные POST-запросом, дождаться ответа и повести процесс по разным веткам в зависимости от результата — потребуется своё REST-действие. Обе схемы разберём по шагам, с кодом.
Что понадобится перед началом
- Права администратора на портале Bitrix24 — без них нельзя ни создать локальное приложение, ни зарегистрировать новое действие для конструктора бизнес-процессов
- Локальное REST-приложение Bitrix24 (создаётся в разделе «Разработчикам» → «Другое» → «Локальное приложение») — через него регистрируется собственное действие
- Публично доступный HTTPS-адрес для обработчика (хендлера) действия — по HTTP запросы Bitrix24 не отправляет
- PHP 7.4+ (в примерах используется библиотека CRest — стандартный клиент для локальных приложений Bitrix24) или любой другой стек, который умеет принимать POST и отдавать JSON
- Тестовый портал — учитывая, что бизнес-процесс, который зависает в статусе ожидания, тяжело отлаживать на реальных сделках

Основная часть: интеграция бизнес-процессов Bitrix24 с внешней системой через webhook
Шаг 1. Определяем, какой вариант вызова нужен
Открываем конструктор бизнес-процессов и смотрим действие категории «Прочее» с названием «WebHook». Оно отправляет GET-запрос на указанный URL и всё — данных обратно в процесс не возвращает, ответ внешней системы никак не используется. Для уведомлений в духе «просто дёрнуть URL, а дальше неважно» этого достаточно: настройка занимает минуту, ничего программировать не нужно.
Если же нужно передать тело запроса методом POST, свои заголовки (например, авторизационный токен внешней системы), а главное — дождаться ответа и в зависимости от него направить процесс по разным веткам (успех / отказ / повтор), встроенного действия не хватит. Для этого регистрируется собственное действие через REST — им и займёмся дальше. По ощущениям, для 80% реальных интеграций (передача заказа в 1С, запрос в скоринг, создание отгрузки в службе доставки) нужен именно второй вариант — простого WebHook хватает редко.
Тут же стоит развести два похожих метода регистрации собственного действия. bizproc.activity.add — более старый механизм, который в документации Bitrix24 сейчас прямо помечен как предназначенный «в первую очередь для поддержки уже существующих интеграций». bizproc.robot.add — актуальный способ, рекомендованный для новой разработки: он работает и в CRM-автоматизации (роботы на стадии воронки), и в обычных бизнес-процессах. Разница в основном в параметре FILTER / DOCUMENT_TYPE, которым робот привязывается к типу документа (сделка, лид, смарт-процесс). Логика вокруг асинхронного вызова — EVENT_TOKEN, ожидание и bizproc.event.send — у обоих методов одинаковая. Дальше в статье используется bizproc.robot.add как текущая рекомендация.
Шаг 2. Создаём локальное приложение и регистрируем REST-действие
В «Разработчикам» → «Другое» → «Локальное приложение» создаём приложение с правами bizproc (доступ к бизнес-процессам) и crm (если действие будет читать поля сделки напрямую, а не только то, что передано в параметрах). Получаем client_id и client_secret — они понадобятся библиотеке CRest.
Дальше регистрируем действие методом bizproc.robot.add. Один вызов делается один раз при установке приложения — не при каждом использовании действия.
<?php
// Регистрируем собственное действие (робота) для бизнес-процессов и CRM-автоматизации
// Вызывается один раз — например, из index.php при установке локального приложения
require_once('crest.php');
$result = CRest::call('bizproc.robot.add', [
'CODE' => 'send_deal_to_external',
'HANDLER' => 'https://example.com/bizproc/handler.php', // публичный HTTPS-адрес обработчика
'AUTH_USER_ID' => 1,
'USE_SUBSCRIPTION' => 'Y', // действие асинхронное: ждём bizproc.event.send с результатом
'NAME' => ['ru' => 'Отправить сделку во внешнюю систему', 'en' => 'Send deal to external system'], // локализация задаётся прямо в NAME, отдельного параметра для этого нет
'PROPERTIES' => [
'deal_id' => ['Name' => 'ID сделки', 'Type' => 'string'],
'amount' => ['Name' => 'Сумма сделки', 'Type' => 'string'],
'stage' => ['Name' => 'Стадия', 'Type' => 'string'],
],
'RETURN_PROPERTIES' => [
'status' => ['Name' => 'Статус ответа', 'Type' => 'string'],
'external_id' => ['Name' => 'ID во внешней системе', 'Type' => 'string'],
],
'FILTER' => [
'INCLUDE' => [['crm', 'CCrmDocumentDeal', 'DEAL']], // ограничиваем действие сделками CRM
],
]);
USE_SUBSCRIPTION => 'Y' — ключевой параметр. Он означает, что действие не завершится сразу после вызова хендлера, а поставит бизнес-процесс на паузу до тех пор, пока обработчик явно не вернёт результат методом bizproc.event.send. Без этого параметра Bitrix24 будет считать действие выполненным сразу после ответа хендлера, даже если тот ещё не успел ничего сделать.

Шаг 3. Добавляем действие в бизнес-процесс и передаём в него данные сделки
После регистрации действие появляется в конструкторе бизнес-процессов среди действий приложений — а благодаря фильтру по сделкам ещё и в списке роботов CRM-автоматизации на карточке стадии воронки. Перетаскиваем его на нужный шаг (например, после смены стадии сделки) и заполняем параметры — сюда подставляются переменные документа: {{ID}}, {{OPPORTUNITY}}, {{STAGE_ID}}. Никакого кода на этом шаге не нужно, это делается прямо в интерфейсе конструктора.
Шаг 4. Пишем обработчик, который получает параметры и вызывает внешний API
Bitrix24 присылает на HANDLER POST-запрос со значениями PROPERTIES, идентификатором документа (document_id — триплет вида ['crm', 'CCrmDocumentDeal', 'DEAL_123']) и служебным токеном события event_token, который понадобится дальше для ответа.
<?php
// handler.php — обработчик действия бизнес-процесса
// Bitrix24 присылает сюда значения параметров, document_id и служебный токен события
$eventToken = $_REQUEST['event_token'] ?? null; // понадобится, чтобы вернуть результат
$dealId = $_REQUEST['properties']['deal_id'] ?? null; // берём из PROPERTIES, которые сами же зарегистрировали
$amount = $_REQUEST['properties']['amount'] ?? null;
if (!$eventToken || !$dealId) {
http_response_code(400);
exit;
}
// Отвечаем Bitrix24 сразу, чтобы не держать соединение открытым —
// саму работу с внешней системой делаем уже после ответа
http_response_code(200);
if (function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
}
// Дальше — вызов внешней системы с таймаутом и повторными попытками (шаг 5),
// а затем передача результата обратно в бизнес-процесс (шаг 6)
$result = callExternalSystem($dealId, $amount);
returnResultToBizproc($eventToken, $result);
Обратите внимание: обработчик отвечает Bitrix24 сразу же, не дожидаясь ответа внешней системы. Это важно — иначе попытка открыть HTTP-соединение с внешней системой держит открытым и соединение с самим Bitrix24, а он ждать долго не станет.
Шаг 5. Настраиваем таймаут и повторные попытки при вызове внешней системы
Здесь начинается собственно интеграция. Внешняя система может ответить не сразу, отвечать с ошибкой или вообще не отвечать — обработчик должен быть готов к этому и не подвешивать бизнес-процесс навсегда.
<?php
// Отправляем данные сделки во внешнюю систему с ограничением по времени и ретраями
function callExternalSystem(string $dealId, string $amount): array
{
$maxAttempts = 3;
$timeoutSec = 8; // не тянем ожидание дольше нескольких секунд — внешние API часто нестабильны
for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
$ch = curl_init('https://external-system.example.com/api/orders');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(['deal_id' => $dealId, 'amount' => $amount]),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_TIMEOUT => $timeoutSec,
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($response !== false && $httpCode >= 200 && $httpCode < 300) {
return ['success' => true, 'data' => json_decode($response, true)];
}
// Пауза перед повтором растёт с каждой попыткой — 2 секунды перед второй, 4 секунды перед третьей
if ($attempt < $maxAttempts) {
sleep(2 ** $attempt);
}
}
return ['success' => false, 'error' => $error ?? 'HTTP ' . $httpCode];
}
Про таймауты стоит сказать отдельно, потому что здесь путают два разных ограничения. Первое — таймаут вашего же HTTP-клиента (CURLOPT_TIMEOUT в примере), это то, сколько вы готовы ждать ответа от внешней системы, и настраивается только на вашей стороне. Второе — то, сколько сам бизнес-процесс Bitrix24 может стоять на паузе, ожидая вызова bizproc.event.send. Здесь ограничение мягкое: процессы, зависшие в ожидании дольше года, портал останавливает автоматически, но никакого «таймаута в 30 секунд» на уровне самого действия нет. Это удобно — можно вызывать внешние очереди с обработкой в фоне — и одновременно опасно: если обработчик упал и не вызвал bizproc.event.send, сделка так и останется в статусе «Ожидание» до ручного вмешательства, а не до истечения условного лимита.
Шаг 6. Возвращаем результат в бизнес-процесс
Когда внешняя система ответила (или все попытки исчерпаны), нужно явно сообщить об этом процессу — иначе он так и останется на паузе.
<?php
// Возвращаем результат вызова обратно в бизнес-процесс
function returnResultToBizproc(string $eventToken, array $result): void
{
$properties = $result['success']
? ['status' => 'ok', 'external_id' => $result['data']['id'] ?? '']
: ['status' => 'error', 'external_id' => ''];
CRest::call('bizproc.event.send', [
'EVENT_TOKEN' => $eventToken,
'RETURN_VALUES' => $properties,
'LOG_MESSAGE' => $result['success']
? 'Заказ успешно создан во внешней системе'
: 'Не удалось создать заказ: ' . ($result['error'] ?? 'неизвестная ошибка'),
]);
}
Поле status из RETURN_PROPERTIES, которое действие вернуло в бизнес-процесс, дальше можно использовать в конструкторе — например, поставить условие «если status = error, отправить уведомление ответственному» и развести процесс по двум веткам. Это то, чего в принципе не может дать встроенный WebHook: он не возвращает в процесс вообще ничего.
Частые ошибки и как их избежать
Ошибка: ждут ответ от встроенного действия WebHook. Разработчики видят готовое действие «WebHook» в конструкторе и предполагают, что оно как минимум сообщит об успехе или ошибке. На деле оно отправляет GET-запрос и не возвращает в процесс никаких данных — по документации Bitrix24 это буквально «действие не возвращает никаких данных, оно только отправляет запрос». Если нужен ответ — только своё REST-действие.
Ошибка: делают синхронный вызов curl прямо в действии «PHP код». В конструкторе есть действие, которое выполняет произвольный PHP-код на портале (доступно только в коробочной версии и только администраторам). Соблазн вызвать внешний API прямо оттуда велик — но такой запрос выполняется синхронно в потоке самого бизнес-процесса, и если внешняя система не отвечает 20-30 секунд, все висит: и обработка очереди бизнес-процессов, и, в худшем случае, воркеры веб-сервера. Проверено на практике — лучше через отдельное REST-действие с собственным таймаутом, как в шаге 5.
Ошибка: не обрабатывают случай, когда обработчик сам падает до вызова bizproc.event.send. Например, обработчик получил запрос, начал вызывать внешнюю систему, а в этот момент упал сам PHP-процесс — истёк memory_limit, сервер перезагрузился. Результат: сделка навсегда зависает в статусе ожидания, потому что процесс ждёт события, которое никогда не придёт. Решение — логировать каждый вызов в отдельную таблицу со статусом «в работе» / «завершён» и сделать фоновый крон, который раз в несколько минут добивает зависшие вызовы и всё равно отправляет bizproc.event.send, пусть даже со статусом ошибки.
Ошибка: передают в свойствах действия персональные данные без проверки токена авторизации на своей стороне. Хендлер публично доступен по HTTPS — это не значит, что его нельзя дёрнуть напрямую, минуя Bitrix24. Обязательно проверяйте, что запрос действительно пришёл из вашего портала, прежде чем обрабатывать properties — Bitrix24 передаёт в auth авторизационные токены приложения, их и нужно сверять.
Из практики А2
Делали интеграцию бизнес-процесса сделки с внешним биллингом: при переходе на стадию «Счёт выставлен» бизнес-процесс должен был создать заказ в биллинговой системе клиента и получить обратно номер заказа, чтобы записать его в поле сделки. Первая версия обработчика вызывала внешний API синхронно и сразу возвращала результат в bizproc.event.send — работало нормально, пока биллинг не начал по вторникам уходить на технические работы на 5-10 минут. В эти окна обработчик либо падал по таймауту curl, либо зависал — а вместе с ним и десятки сделок стояли в статусе ожидания без объяснения, почему.
Переделали на схему с промежуточной таблицей: обработчик сразу пишет запись «в работе», отвечает Bitrix24 быстро, а сам вызов внешнего API с ретраями идёт уже после ответа. Отдельный крон раз в две минуты проверяет зависшие записи старше 10 минут — если внешняя система так и не ответила, отправляет в бизнес-процесс bizproc.event.send со статусом ошибки, и дальше в самом БП стоит ветка «повторить через час» вместо бесконечного ожидания. Разница почувствовалась сразу: вместо ручного разбора зависших сделок в понедельник-вторник процесс либо самовосстанавливался, либо явно уходил ответственному менеджеру с понятной причиной сбоя.
Отдельно оказалось полезным логировать в bizproc.activity.log не только факт успеха, но и то, какая именно попытка (первая, вторая, третья) прошла успешно. Когда стало видно, что большинство успешных вызовов проходят не с первой попытки, а со второй — стало ясно, что дело не в случайных сбоях сети, а в том, что внешняя система реально не успевает обработать первый запрос вовремя. Это уже повод обсудить с клиентом изменение таймаутов на его стороне, а не подбирать магические числа ретраев на своей.
Итог
После настройки бизнес-процесс Bitrix24 сам передаёт данные сделки или лида во внешнюю систему в момент нужного события — без ручного копирования и без опроса API по расписанию. Если нужен только односторонний пинг — хватит встроенного WebHook, для полноценной интеграции с обработкой ответа и ветвлением процесса — регистрируйте своё REST-действие по схеме из статьи. Если застряли на каком-то шаге — опишите ситуацию в комментарии или напишите нам напрямую.