Вебхуки приложения и события: настройка, темы, проверка подписи
Обновлено: 11 ноября 2026 · 8 мин чтения
Вебхук это HTTP-запрос, который отправляется на Ваш сервер, когда в Clomni происходит событие: как только клиент пишет, приходит message.created, и проверять список каждую минуту не нужно. Эта статья о вебхуке приложения на developer.clomni.ai. Вебхук workspace в панели («Settings › Workspace settings › Integrations › Webhook») отдельный, для него см. статью «Настроить вебхук (webhook): отправлять события в свою систему».
Шаги
-
1
Настройте вебхук
В разделе приложения «Webhooks»:
- Укажите URL. Адрес должен начинаться с
https://и быть открыт из интернета: внутренние адреса, такие какlocalhost,10.x,192.168.x, не принимаются, перенаправления не выполняются. - Выберите темы. Во внутреннем приложении можно выбрать только темы, которые соответствуют одобренному scope, в публичном приложении достаточно запрошенного scope.
- Сохраните секрет: он начинается с
whsec_и показывается только один раз.
У одного приложения один адрес вебхука.
- Укажите URL. Адрес должен начинаться с
-
2
Подтвердите адрес
Когда Вы сохраняете URL, Clomni отправляет туда
{ "type": "webhook.verify", "challenge": "c2b1f0a8e7d94c3b" }. Ваш сервер должен в течение 5 секунд ответить200с этим же значением в теле:{ "challenge": "c2b1f0a8e7d94c3b" }. Если ответа нет, вебхук остаётся «не подтверждён» и события не отправляются: исправьте сервер и сохраните адрес на портале ещё раз. Этот запрос тоже подписывается. -
3
Темы и нужные scope
conversation.created,conversation.updated(метка, приоритет, атрибут),conversation.status_changed,conversation.assigned:conversations:read.message.created(входящее, исходящее, внутренняя заметка),message.updated(статус сталdelivered,read,failed):messages:read.contact.created,contact.updated:contacts:read.
Если scope удалён из приложения, соответствующая тема тоже перестаёт приходить. В публичном приложении, когда администратор отключает приложение, приходит
app.uninstalledнезависимо от выбранных тем. -
4
Формат запроса
Заголовки:
Content-Type: application/json,User-Agent: Clomni-Webhooks/1.0,webhook-id,webhook-timestamp,webhook-signature. Тело:{ "id": "evt_2Wq8mX4vT9kLpR3n", "type": "message.created", "api_version": "v1", "workspace_id": 42, "occurred_at": "2026-11-08T16:00:00.412Z", "data": { "id": 51234, "conversation_number": 1042, "direction": "incoming", "content": "Здравствуйте, когда придёт мой заказ?" } }idсовпадает с заголовкомwebhook-id, используйте его, чтобы распознать дубликат.dataв том же виде, что в эндпоинтах синхронизации. Вconversation.updatedиconversation.status_changedесть такжеchanged_attributes. В публичном приложении в конверте есть ещёinstallation_id. -
5
Проверьте подпись
Вебхук без проверки может подделать кто угодно.
- Читайте тело в сыром виде, не разбирайте JSON и не записывайте его заново.
- Подписываемый текст:
{webhook-id}.{webhook-timestamp}.{сырое тело}. - Ключ: часть секрета после
whsec_, декодированная из base64. - Вычислите HMAC-SHA256 и запишите результат в base64.
- В
webhook-signatureможет быть несколько записейv1,<подпись>через пробел: если одна из них совпадает при сравнении за постоянное время, подпись верна. - Если
webhook-timestampстарше 5 минут, отклоните запрос.
import crypto from "node:crypto"; function verifyWebhook(rawBody, headers, secret) { const id = headers["webhook-id"], ts = headers["webhook-timestamp"], sigs = headers["webhook-signature"]; if (!id || !ts || !sigs || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64"); const expected = crypto.createHmac("sha256", key).update(`${id}.${ts}.${rawBody}`).digest(); return sigs.split(" ").some((part) => { const [v, sig] = part.split(","); const given = Buffer.from(sig || "", "base64"); return v === "v1" && given.length === expected.length && crypto.timingSafeEqual(given, expected); }); }import base64, hashlib, hmac, time def verify_webhook(raw_body: bytes, headers, secret: str) -> bool: msg_id, ts, sigs = headers.get("webhook-id"), headers.get("webhook-timestamp"), headers.get("webhook-signature") if not msg_id or not ts or not sigs or abs(time.time() - int(ts)) > 300: return False key = base64.b64decode(secret.removeprefix("whsec_")) expected = base64.b64encode(hmac.new(key, f"{msg_id}.{ts}.".encode() + raw_body, hashlib.sha256).digest()).decode() return any(p.partition(",")[0] == "v1" and hmac.compare_digest(p.partition(",")[2], expected) for p in sigs.split(" "))Тестовые значения: секрет
whsec_aSGonRLU/DDLGZkKrR6fe7DCdyC22NiWOb8feJg/O6k=,webhook-idevt_2Wq8mX4vT9kLpR3n,webhook-timestamp1794153600, тело{"id":"evt_2Wq8mX4vT9kLpR3n","type":"message.created","api_version":"v1","workspace_id":42,"occurred_at":"2026-11-08T16:00:00Z","data":{"id":51234}}. Результат должен бытьv1,hJufoXNHJ1dUd1zcZdtiPLkp9Px9eS+ZIUi9odi4Cvk=. Этот секрет только для документации, время старое: при проверке отключите проверку 5 минут. -
6
Доставка и повторы
- Сервер должен вернуть
2xxза 5 секунд: поставьте событие в очередь и сразу ответьте. - Если
2xxне пришёл, Clomni отправляет повторно через 1 минуту, 5 минут, 30 минут, 2 часа, 6 часов, 12 часов и 24 часа. - Если 3 дня подряд ни одна доставка не удалась, вебхук отключается, и владельцу приложения уходит e-mail. Исправьте сервер и снова включите вебхук на портале.
- Одно и то же событие может прийти дважды (отбрасывайте дубликаты по
webhook-id), порядок не гарантируется (сортируйте поoccurred_at). message.updatedне сообщает причинуfailed: прочитайте сообщение и посмотритеcontent_attributes.external_error.
- Сервер должен вернуть
-
7
Проверка на портале
В разделе «Webhooks» есть журнал доставок за последние 30 дней (тема, статус, код ответа, длительность, ошибка). «Send test» (отправить тест) сразу отправляет пробное событие типа
webhook.test, «Resend» (отправить повторно) повторяет неудачную доставку. После смены секрета в течение 24 часов в заголовке две подписи (старая и новая). Если сервер долго не работал, заберите пропущенные события черезGET /v1/eventsс теми жеid. Для локальной проверки получите HTTPS-адрес через туннель, например ngrok или Cloudflare Tunnel. -
8
Если вебхук не приходит
- Вебхук на портале в статусе «подтверждён»? Если нет, сервер не возвращает
challengeза 5 секунд. - Одобрен ли scope темы?
- Отвечает ли сервер
2xxза 5 секунд? Посмотрите в журнале код ответа и ошибку. - Открыт ли URL из интернета, это
https, нет ли перенаправления? - Если вебхук отключён, включите его снова.
- Вебхук на портале в статусе «подтверждён»? Если нет, сервер не возвращает