Вебхуки
Вебхуки сообщают о событиях школы сразу, как только они произошли: новый ученик, оплата, завершённый урок, зачисление в группу, новый лид. Без них тот же результат даёт только периодический опрос GET /students или GET /payments с фильтром updated_since — рабочий вариант, но с задержкой в размер интервала опроса и с лишними запросами, когда событий не было. Вебхук устраняет и то, и другое: BigBen CRM сам отправляет POST-запрос на ваш URL в момент события.
Как подключить
Подписки на вебхуки настраиваются внутри CRM. Публичный API-ключ (bb_<prefix>_<secret>) для этого не подходит — управление подписками живёт в другом контуре авторизации. Заводит подписку сотрудник школы с ролью владельца или администратора:
CRM → Настройки → Интеграции → карточка «Вебхуки» → «Настроить».
В панели школа:
- указывает URL приёмника — обычный HTTPS-эндпоинт на вашей стороне;
- отмечает события, на которые подписывается (раздел Каталог событий);
- получает секрет подписки — строку для проверки подписи. Секрет показывается один раз, в момент создания подписки, и восстановить его позже нельзя — только выпустить новую подписку;
- может отправить тестовое событие и посмотреть журнал последних 50 доставок (статус, HTTP-код ответа, число попыток) прямо в этой панели.
Внимание
Если вы разрабатываете сайт или сервис школы, но сами не имеете доступа к её CRM — секрет и URL вам должен передать администратор школы. Публичный API-ключ для этого не подходит, им нельзя ни создать подписку, ни прочитать секрет.
Каталог событий
| Событие | Когда срабатывает |
|---|---|
student.created | В школе зарегистрирован новый ученик |
lead.created | Создан новый лид (в том числе через POST /leads, см. Сценарии использования) |
group.enrolled | Ученика зачислили в группу |
lesson.completed | Преподаватель отметил посещение урока учеником (статус «был») |
payment.received | Проведена оплата |
Каждое событие приходит POST-запросом с телом:
{
"event": "student.created",
"account_id": 12345,
"payload": { "...": "..." },
"timestamp": "2026-08-14T10:15:00+00:00"
}account_id — школа-владелец подписки, timestamp — момент отправки в ISO8601. Содержимое payload зависит от события.
student.created
{
"event": "student.created",
"account_id": 12345,
"payload": {
"student_id": 88123,
"student_name": "Иванова Мария",
"filial_id": 3
},
"timestamp": "2026-08-14T10:15:00+00:00"
}lead.created
{
"event": "lead.created",
"account_id": 12345,
"payload": {
"lead_id": 55210,
"name": "Пётр Сидоров",
"phone": "+79261234567",
"source": "api"
},
"timestamp": "2026-08-14T10:16:00+00:00"
}group.enrolled
{
"event": "group.enrolled",
"account_id": 12345,
"payload": {
"enrollment_id": 91004,
"student_id": 88123,
"group_id": 410,
"group_name": "Английский B1, вт/чт 18:00",
"filial_id": 3
},
"timestamp": "2026-08-14T10:20:00+00:00"
}lesson.completed
{
"event": "lesson.completed",
"account_id": 12345,
"payload": {
"student_id": 88123,
"lesson_id": 30217,
"group_id": 410,
"teacher_id": 512,
"visit_status": 1,
"filial_id": 3
},
"timestamp": "2026-08-14T18:05:00+00:00"
}payment.received
{
"event": "payment.received",
"account_id": 12345,
"payload": {
"student_id": 88123,
"payment_id": 77001,
"amount": 450000,
"payment_amount": 450000,
"payment_type": "card",
"group_id": 410,
"filial_id": 3
},
"timestamp": "2026-08-14T11:00:00+00:00"
}amount и payment_amount — сумма в исходном виде, как она хранится в CRM (в примере выше это копейки в записи оплаты). Не полагайтесь на конкретный порядок величины без сверки с суммой, которую видит школа в интерфейсе — для точной суммы в копейках используйте GET /payments (поле amount_kopecks, см. Справочник API).
Проверка подписи
Каждый запрос приходит с заголовком X-BigBen-Signature — hex-строка HMAC-SHA256 от тела запроса (ровно тех байт, что пришли в теле, без повторной сериализации JSON), посчитанная на секрете вашей подписки. Проверяйте подпись до обработки события и сравнивайте результат функцией постоянного времени — обычное сравнение строк === через побайтовое сравнение раскрывает секрет через тайминг-атаку.
PHP
<?php
function verifyBigBenSignature(string $rawBody, string $signatureHeader, string $secret): bool
{
$expected = hash_hmac('sha256', $rawBody, $secret);
return hash_equals($expected, $signatureHeader);
}
$rawBody = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_BIGBEN_SIGNATURE'] ?? '';
$secret = getenv('BIGBEN_WEBHOOK_SECRET');
if (!verifyBigBenSignature($rawBody, $signature, $secret)) {
http_response_code(401);
exit;
}
$event = json_decode($rawBody, true);
// обработка $event['event'], $event['account_id'], $event['payload']
http_response_code(200);Node.js
const crypto = require('crypto')
const express = require('express')
const app = express()
// важно: нужны СЫРЫЕ байты тела, не распарсенный JSON — подпись считается по ним
app.use(express.raw({ type: 'application/json' }))
function verifyBigBenSignature(rawBody, signatureHeader, secret) {
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
const expectedBuf = Buffer.from(expected, 'utf8')
const signatureBuf = Buffer.from(signatureHeader || '', 'utf8')
if (expectedBuf.length !== signatureBuf.length) {
return false
}
return crypto.timingSafeEqual(expectedBuf, signatureBuf)
}
app.post('/webhooks/bigben', (req, res) => {
const signature = req.header('X-BigBen-Signature')
const secret = process.env.BIGBEN_WEBHOOK_SECRET
if (!verifyBigBenSignature(req.body, signature, secret)) {
return res.sendStatus(401)
}
const event = JSON.parse(req.body.toString('utf8'))
// обработка event.event, event.account_id, event.payload
res.sendStatus(200)
})
app.listen(3000)Требования к эндпоинту приёмника
- Отвечайте быстро и 2xx-кодом. BigBen CRM ждёт ответ до 10 секунд; любой код вне диапазона 2xx или таймаут считаются неудачей и запускают ретрай. Если обработка события медленная (отправка писем, синхронизация с внешней системой) — сохраните событие и ответьте
200сразу, обработку вынесите в фоновую очередь на своей стороне. - Обрабатывайте события идемпотентно. Ретраи и, реже, дублирующие доставки возможны — держите на своей стороне ключ дедупликации, например по паре (
event,payload.student_id/payload.lead_id/…,timestamp) или по внутреннему id изpayload, и игнорируйте повтор, если событие уже обработано. - Проверяйте подпись на каждый запрос, включая повторные доставки. Секретность URL приёмника защитой не считается: адрес утекает в логи прокси и историю браузера.
Ретраи и что происходит после неудачи
Если приёмник ответил не 2xx или не ответил вовсе, BigBen CRM повторяет доставку того же события до 4 раз всего, с паузами перед повторными попытками:
| Попытка | Пауза перед ней |
|---|---|
| 2-я | 1 минута |
| 3-я | 5 минут |
| 4-я | 30 минут |
| — | 2 часа (после 4-й, если бы была 5-я) |
Внимание
Порядок пауз — 1 мин / 5 мин / 30 мин / 2 ч перед попытками №2–4 соответственно. Если 4-я попытка тоже не удалась, доставка помечается провалившейся, ретраев больше не будет.
После 4-й неудачной попытки подписка помечается проблемной, и все сотрудники школы с полным доступом получают уведомление в CRM о том, что доставка по URL не проходит. Сама подписка не отключается автоматически — она продолжает получать новые события, пока школа не отключит её вручную.
Отладка
- В панели «Вебхуки» (CRM → Настройки → Интеграции) школа видит журнал последних 50 доставок на подписку: событие, статус, HTTP-код ответа, число попыток.
- Там же можно отправить тестовое событие (
webhook.test) на текущий URL и сразу увидеть результат — удобно проверить, что эндпоинт вообще отвечает 2xx, до того как ждать реального события. - Если приёмник стабильно недоступен, проще всего временно отключить подписку в панели (без удаления — история доставок сохраняется) и включить обратно, когда эндпоинт снова готов принимать запросы.
Коды ошибок публичного API — на отдельной странице: Коды ошибок. Лимиты на сам публичный API (не на вебхуки — они не считаются в бюджет запросов ключа) — Лимиты запросов.