#Webhook-обработчик событий
🕐 Сложность: medium | Скоупы: crm | Стек: Node.js, Express
#Что делаем
Создаём Express-сервер, который принимает webhook-события от Bitrix24, валидирует их, логирует и выполняет действия в зависимости от типа события. Например, при создании новой сделки сервер автоматически запрашивает её детали через Entity API и записывает в лог. Это основа для построения event-driven интеграций, которые реагируют на изменения в реальном времени.
#Необходимо
- API-ключ Вайбкод с правами
crm - Node.js 18+
- Пакеты:
express - Публичный URL для приёма webhooks (ngrok для разработки)
#Полный код
// webhook-handler.js
// Express-сервер для обработки webhook-событий Bitrix24
import express from 'express';
const API_KEY = process.env.VIBE_API_KEY;
const BASE_URL = process.env.VIBE_BASE_URL;
const PORT = process.env.PORT || 3001;
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Получаем сделку через Entity API
async function getDeal(dealId) {
const response = await fetch(`${BASE_URL}/v1/deals/${dealId}`, {
headers: { 'X-Api-Key': API_KEY },
});
const { data } = await response.json();
return data;
}
// ──────────────────────────────────────────
// Обработчики событий
// ──────────────────────────────────────────
// Обработка создания новой сделки
async function handleDealAdd(eventData) {
const dealId = eventData.data?.FIELDS?.ID;
if (!dealId) {
console.log(' ⚠️ ID сделки отсутствует в данных события');
return;
}
console.log(` 📋 Загружаем данные сделки #${dealId}...`);
try {
const deal = await getDeal(dealId);
console.log(` Название: ${deal.title}`);
console.log(` Сумма: ${deal.amount} ${deal.currency}`);
console.log(` Стадия: ${deal.stageId}`);
console.log(` Ответственный: ID ${deal.assignedById}`);
// Здесь можно добавить дополнительную логику:
// - отправить уведомление в Slack/Telegram
// - создать задачу для менеджера
// - обновить внешнюю систему
} catch (error) {
console.error(` ❌ Ошибка при загрузке сделки: ${error.message}`);
}
}
// Обработка обновления сделки
async function handleDealUpdate(eventData) {
const dealId = eventData.data?.FIELDS?.ID;
if (!dealId) return;
console.log(` 📋 Сделка #${dealId} обновлена`);
try {
const deal = await getDeal(dealId);
console.log(` Текущая стадия: ${deal.stageId}`);
console.log(` Сумма: ${deal.amount}`);
} catch (error) {
console.error(` ❌ Ошибка: ${error.message}`);
}
}
// Обработка удаления сделки
function handleDealDelete(eventData) {
const dealId = eventData.data?.FIELDS?.ID;
console.log(` 🗑️ Сделка #${dealId} удалена`);
}
// Карта обработчиков по типу события
const EVENT_HANDLERS = {
ONCRMDEALADD: handleDealAdd,
ONCRMDEALUPDATE: handleDealUpdate,
ONCRMDEALDELETE: handleDealDelete,
};
// ──────────────────────────────────────────
// HTTP-эндпоинты
// ──────────────────────────────────────────
// Основной эндпоинт для приёма webhook-событий
app.post('/webhook', async (req, res) => {
const timestamp = new Date().toLocaleString('ru-RU');
const eventData = req.body;
const eventName = eventData.event || 'UNKNOWN';
console.log(`\n[${timestamp}] 📥 Получено событие: ${eventName}`);
console.log(` Данные: ${JSON.stringify(eventData.data?.FIELDS || {})}`);
// Ищем обработчик для данного события
const handler = EVENT_HANDLERS[eventName];
if (handler) {
try {
await handler(eventData);
} catch (error) {
console.error(` ❌ Ошибка обработки: ${error.message}`);
}
} else {
console.log(` ℹ️ Обработчик для события ${eventName} не зарегистрирован`);
}
// Bitrix24 ожидает ответ 200, иначе будет повторять запрос
res.status(200).json({ status: 'ok' });
});
// Проверка работоспособности
app.get('/health', (req, res) => {
res.json({
status: 'running',
uptime: process.uptime(),
events: Object.keys(EVENT_HANDLERS),
});
});
// ──────────────────────────────────────────
// Запуск
// ──────────────────────────────────────────
app.listen(PORT, () => {
console.log('🕐 Webhook-сервер запущен');
console.log(` Порт: ${PORT}`);
console.log(` Endpoint: POST /webhook`);
console.log(` Health: GET /health\n`);
console.log('💡 Для разработки используйте ngrok:');
console.log(' npx ngrok http 3001');
});
#Как это работает
- Express-сервер запускается и слушает POST-запросы на эндпоинте
/webhook. - Bitrix24 отправляет HTTP POST с данными события (тип события, ID сущности и др.) при каждом изменении в CRM.
- Сервер определяет тип события (создание, обновление, удаление сделки) и находит соответствующий обработчик.
- Обработчик запрашивает актуальные данные сделки через Entity API (
GET /v1/deals/:id) для получения полной информации. - Сервер всегда отвечает статусом 200 — иначе Bitrix24 будет повторять отправку события.
Примечание: Регистрация обработчиков событий (
event.bind,event.get) выполняется через настройки портала Битрикс24 или прямой REST API. Эти методы не имеют entity-обёртки в Вайбкод, и являются разовыми инфраструктурными операциями.
#Что можно улучшить
- Добавить очередь событий (Redis/BullMQ) для надёжной обработки при большой нагрузке
- Реализовать верификацию подписи запроса для защиты от поддельных вызовов
- Добавить обработчики для других сущностей: контакты, компании, задачи, лиды
- Сохранять историю полученных событий в базу данных для отладки и аналитики