Вебхуки — CRM сама сообщит о событии
Создана сделка, сменился статус, пришла оплата, добавлен клиент — CRM отправит уведомление на ваш сервер.
Вебхук — это уведомление, которое CRM сама отправляет на адрес вашего сервера, когда что-то произошло. Не нужно каждую минуту спрашивать «есть ли новые сделки?» — CRM пришлёт их сама за несколько секунд.
События
deal.created— создана сделка.deal.status_changed— сменился статус сделки (в уведомлении есть и прежний статус).deal.paid— по сделке пришла оплата (в уведомлении — сумма оплаты).client.created— добавлен клиент.
Подключить вебхук
- Откройте Настройки → API и вебхуки, блок «Вебхуки».
- Вставьте адрес вашего сервера. Подходит только
https://и только адрес, доступный из интернета. - Отметьте события, которые нужны, и нажмите Добавить адрес.

Адрес, события и кнопка «Добавить адрес» - Скопируйте секрет из тёмного блока — он показывается один раз. По нему ваш сервер проверяет, что уведомление пришло именно из BSTCRM.
- Нажмите Проверить — CRM отправит тестовое событие
ping. Ответ сервера и все отправки видны по кнопке Журнал.
Что приходит
Запрос POST с JSON. Заголовки: X-BSTCRM-Event (событие), X-BSTCRM-Delivery (уникальный номер уведомления), X-BSTCRM-Signature (подпись).
{
"id": "5e1c9a2e-0f7b-4f43-9b8b-6a2f1a9d3c71",
"event": "deal.status_changed",
"created_at": "2026-10-01T09:12:44Z",
"data": {
"deal": {
"id": 214, "url": "https://ваша-компания.bstcrm.ru/orders/214", "title": "Новая сделка",
"branch": {"id": 1, "name": "Акорус"},
"status": {"id": 12, "name": "Успешно реализовано", "is_success": true},
"total_amount": 4500.0, "paid_amount": 4500.0, "debt": 0.0,
"client": {"id": 75, "name": "Иван Тестовый", "phone": "+79001234567"},
"vehicle": {"id": 41, "brand": "Volkswagen", "model": "Polo", "plate": "Х345ТУ 78", "vin": "V6W5YDLMA9TZZV479"},
"created_at": "2026-10-01T08:40:02Z", "issued_at": "2026-10-01T09:12:44Z"
},
"previous_status": {"id": 11, "name": "Готово к выдаче"}
}
}Для deal.paid вместо previous_status приходит "payment": {"amount": 4500.0}, для client.created — "client": {...}.
Как ответить
Верните код 200–299 в течение 5 секунд. Если сервер не ответил или ответил ошибкой, CRM повторит отправку через 5 и через 30 секунд. После 50 неудачных отправок подряд адрес выключается — включить его снова можно кнопкой Включить.
Проверка подписи
Заголовок выглядит так: X-BSTCRM-Signature: t=1696150364,v1=5f2b…. Подпись — HMAC-SHA256 от строки <t>.<тело запроса> с вашим секретом. Проверяйте подпись по сырому телу запроса и отклоняйте уведомления старше 5 минут.
Python:
import hashlib, hmac, time
def is_from_bstcrm(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(item.split("=", 1) for item in header.split(","))
ts, signature = parts.get("t", "0"), parts.get("v1", "")
expected = hmac.new(secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature) and abs(time.time() - int(ts)) < 300PHP:
<?php
function isFromBstcrm(string $rawBody, string $header, string $secret): bool {
parse_str(str_replace(',', '&', $header), $parts);
$expected = hash_hmac('sha256', $parts['t'] . '.' . $rawBody, $secret);
return hash_equals($expected, $parts['v1'] ?? '') && abs(time() - (int)$parts['t']) < 300;
}
$raw = file_get_contents('php://input');
if (!isFromBstcrm($raw, $_SERVER['HTTP_X_BSTCRM_SIGNATURE'] ?? '', 'whsec_ваш_секрет')) { http_response_code(401); exit; }
$event = json_decode($raw, true);
http_response_code(200);Node.js:
const crypto = require("crypto");
function isFromBstcrm(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 || ""));
}