для разработчиков
Исходящие вебхуки
ChatNeuron отправляет POST на адрес вашей системы, когда появляется или меняется заявка, начинается, переходит к человеку или закрывается диалог с клиентом, — без опроса API.
Подключение
- Владелец или администратор добавляет адрес в кабинете: Настройки → API и вебхуки → Исходящие вебхуки, и отмечает события.
- Адрес — только
https://, без логина и пароля в адресе. Адреса во внутренней сети не принимаются. - После добавления один раз показывается секрет
whsec_…— им проверяется подпись. Сохраните его сразу. - Кнопка «Проверить» отправляет событие
test. «Журнал» показывает доставки за 30 дней: тело, ответ вашего сервера, попытки — и отправляет событие снова. До 10 вебхуков на компанию.
События
| event | Когда |
|---|---|
lead.created | Новая заявка из любого канала или из API. Заявка, скрытая до оплаты тарифа, не отправляется. |
lead.updated | У заявки сменилась стадия или статус, поправили контакты: в кабинете, из CRM, после оплаты заказа. |
dialog.started | Клиент начал новый диалог в любом канале. Тестовые диалоги не отправляются. |
dialog.handed_off | Диалог передан человеку: клиент попросил менеджера или AI-сотрудник позвал сотрудника. |
dialog.closed | Диалог закрыт: 24 часа тишины или новый разговор после передачи человеку. |
message.created | Новая реплика клиента, AI-сотрудника или сотрудника компании. Событий много — подписывайтесь, если нужна переписка в реальном времени. |
test | Пробная отправка из настроек. |
Поля каждого события — в справочнике событий.
Запрос
Тело — JSON { id, event, createdAt, data }. id — id события, он же в заголовке X-ChatNeuron-Webhook-Id и одинаков во всех повторах: по нему отбрасывайте дубли. Номер попытки — в X-ChatNeuron-Delivery-Attempt. Пример lead.created:
POST /hooks/chatneuron HTTP/1.1
Content-Type: application/json
User-Agent: ChatNeuron-Webhooks/1
X-ChatNeuron-Event: lead.created
X-ChatNeuron-Webhook-Id: 8d0c7c1e-…
X-ChatNeuron-Delivery-Attempt: 1
X-ChatNeuron-Signature: t=1761556502,v1=5f2b…
{
"id": "8d0c7c1e-…",
"event": "lead.created",
"createdAt": "2026-10-27T09:15:02.114Z",
"data": {
"id": "cmv…",
"agentId": "cmu…",
"name": "Анна",
"phone": "+79991234567",
"email": null,
"message": "Нужна консультация по доставке",
"type": null,
"topic": "Доставка",
"source": "chat",
"score": 72,
"createdAt": "2026-10-27T09:15:02.114Z"
}
}Пример dialog.started:
{
"id": "1f4e…",
"event": "dialog.started",
"createdAt": "2026-10-27T09:12:40.003Z",
"data": {
"id": "cmv…",
"agentId": "cmu…",
"channel": "telegram",
"topic": null,
"startedAt": "2026-10-27T09:12:40.003Z"
}
}Проверка подписи
Заголовок X-ChatNeuron-Signature: t=<unix-время>,v1=<подпись>. Подпись — HMAC-SHA256 от строки <t>.<тело запроса> секретом вебхука, в hex. Считайте её от сырого тела, до разбора JSON, и отклоняйте запросы старше 5 минут.
Node.js
import { createHmac, timingSafeEqual } from "node:crypto"
// rawBody — тело запроса строкой, как пришло (до JSON.parse)
export function verifyChatNeuron(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")))
const t = Number(parts.t)
if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex")
const got = Buffer.from(parts.v1 ?? "", "hex")
const want = Buffer.from(expected, "hex")
return got.length === want.length && timingSafeEqual(got, want)
}Python
import hashlib, hmac, time
def verify_chatneuron(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
t = int(parts.get("t", 0))
if not t or abs(time.time() - t) > tolerance:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))Доставка
- Успех — любой ответ 2xx за 8 секунд. Отвечайте сразу, а долгую обработку делайте в фоне.
- Повторы: если адрес не ответил 2xx, повторим через 1, 5, 15 минут, 1, 3, 6 и 12 часов — всего 8 попыток за сутки. Доставка «как минимум один раз»: событие может прийти дважды, сверяйте
id. - Если адрес не принимает события 3 дня подряд, вебхук выключается, а владельцу приходит уведомление. Включите его в настройках, когда адрес снова заработает.
- Редиректы не выполняются. Пропущенное можно добрать из ленты событий
GET /events— там те же события за 30 дней.