Вебхуки
Зарегистрируйте публичную конечную точку, проверяйте подписанные сырые полезные нагрузки, дедуплицируйте события и безопасно управляйте повторами.
Зарегистрируйте публичную конечную точку, проверяйте подписанные сырые полезные нагрузки, дедуплицируйте события и безопасно управляйте повторами.
Вебхуки разработчика отправляют события аккаунта на ваш сервер, чтобы не нужно было опрашивать. Доставка минимум один раз: события могут дублироваться, задерживаться или приходить не по порядку.
Зарегистрируйте конечную точку#
Создайте ключ с webhooks:write, затем зарегистрируйте публичный HTTPS приёмник и наименьший набор событий, который вам нужен.
curl --fail-with-body https://makeagent.fast/api/v1/webhook-endpoints \
-X POST \
-H "Authorization: Bearer $MAF_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: webhook-production-v1" \
-d '{
"url": "https://api.example.com/webhooks/make-agent-fast",
"events": ["lead.created", "conversation.started"]
}'URL должен использовать HTTPS, не может содержать имя пользователя, пароль, фрагмент или пользовательский порт и должен разрешаться только в публичные IP-адреса. Редиректы не выполняются во время доставки.
Ответ 201 один раз раскрывает секрет подписи whsec_.... Сохраните его в менеджере секретов приёмника перед закрытием ответа. Перечисление конечных точек позже возвращает URL, события, статус, последнюю доставку и последнюю ошибку — но не секрет.
Типы событий#
| Событие | Испускается когда |
|---|---|
site.published | Сайт становится опубликованным |
site.unpublished | Сайт возвращается в состояние черновика |
lead.created | Агент собирает новую заявку |
conversation.started | Начинается новая ветка диалога |
conversation.message.created | Сообщение добавляется в диалог |
knowledge.ready | Источник знаний завершает обработку |
broadcast.sent | Рассылка завершает процесс отправки |
domain.activated | Устарело — собственные домены не предлагаются; это событие никогда не испускается |
Формат запроса#
Make Agent Fast отправляет POST с Content-Type: application/json, User-Agent: MakeAgentFast-Webhooks/1.0 и этими заголовками:
maf-event-id: evt_...
maf-event-type: lead.created
maf-signature: t=1784210566,v1=HEX_HMAC_SHA256Тело JSON имеет стабильную оболочку. Поля внутри data зависят от события и могут получать добавочные поля.
{
"id": "evt_2c34...",
"type": "lead.created",
"created_at": "2026-07-16T10:02:46.000Z",
"data": {
"site_id": "SITE_ID",
"lead_id": "LEAD_ID"
}
}Используйте id тела или maf-event-id как ключ дедупликации. Не используйте время доставки или сгенерированный ID базы данных.
Проверьте подпись в Node.js#
Читайте точные сырые байты до разбора JSON. Подписанное значение — TIMESTAMP + "." + RAW_BODY.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyWebhook(rawBody: Buffer, header: string, secret: string) {
const values = Object.fromEntries(header.split(",").map((part) => part.split("=", 2)));
const timestamp = values.t;
const supplied = values.v1;
if (!timestamp || !supplied || !/^[a-f0-9]{64}$/.test(supplied)) return false;
const age = Math.abs(Date.now() / 1_000 - Number(timestamp));
if (!Number.isFinite(age) || age > 300) return false;
const expected = createHmac("sha256", secret)
.update(`${timestamp}.`)
.update(rawBody)
.digest("hex");
return timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(supplied, "hex"));
}Отклоняйте неверно сформированные подписи и временные метки старше вашего принятого окна повторного воспроизведения; пять минут — разумное значение по умолчанию. Сравнивайте байты фиксированной длины за постоянное время.
Проверьте подпись в Python#
import hashlib
import hmac
import time
def verify_webhook(raw_body: bytes, header: str, secret: str) -> bool:
values = dict(part.split("=", 1) for part in header.split(",") if "=" in part)
timestamp = values.get("t", "")
supplied = values.get("v1", "")
try:
if abs(time.time() - int(timestamp)) > 300:
return False
except ValueError:
return False
signed = timestamp.encode() + b"." + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, supplied)Разбирайте и обрабатывайте JSON только после успеха проверки подписи.
Подтверждайте и обрабатывайте#
- Проверьте подпись и временную метку.
- Вставьте ID события в таблицу с уникальным ограничением.
- Если оно уже существует, верните
204без повторения побочного эффекта. - Зафиксируйте событие или поставьте в очередь вашу внутреннюю задачу.
- Быстро верните ответ
2xx.
Отправитель Make Agent Fast истекает по времени после 10 секунд. Он считает успешными только 2xx и не выполняет редиректы.
Поведение повторов#
Неудачные доставки ставятся в очередь до восьми попыток. Откат начинается около двух секунд, удваивается с джиттером и ограничен одним часом; фактическое время доставки может быть позже, когда воркеры заняты. Поскольку повторы могут пережить инициирующий запрос и приходить не по порядку, никогда не полагайтесь на приход одного события непосредственно перед другим.
Возвращайте ответ не-2xx только когда хотите, чтобы Make Agent Fast повторил. Для навсегда неподдерживаемой версии события или удалённого назначения примите и запишите его или удалите конечную точку вместо производства бесконечных временных сбоев.
Ротируйте секрет подписи#
Секреты подписи не могут быть раскрыты или отредактированы. Для безопасной ротации:
- Создайте вторую конечную точку, указывающую на временный или версионный путь приёмника.
- Сохраните её недавно раскрытый секрет.
- Принимайте и дедуплицируйте события от обеих конечных точек.
- Проверьте, что новая конечная точка получает валидные доставки.
- Удалите старую конечную точку через
DELETE /webhook-endpoints/{endpoint_id}.
Если URL приёмника остаётся тем же, заставьте принимающее приложение принимать оба секрета во время перекрытия и используйте ID событий для предотвращения дублирующих побочных эффектов.
Решение проблем#
| Симптом | Проверка |
|---|---|
| Нет доставок | Статус конечной точки, выбранные события, публичный DNS, сертификат HTTPS и произошло ли событие вообще |
| Несовпадение подписи | Доступ к сырому телу, точная конкатенация timestamp.body, правильный секрет конечной точки и трансформации тела проксей |
| Повторные доставки | Код возврата, 10-секундный таймаут, внутренние исключения и уникальность ID событий |
| Ошибка приватного адреса | Записи DNS должны разрешаться только в публичные адреса; localhost и внутренние диапазоны отклоняются |
| Сбой редиректа | Зарегистрируйте финальный HTTPS URL напрямую; редиректы не выполняются |
Никогда не включайте секрет подписи, ключ bearer или полную полезную нагрузку посетителя в сообщение поддержки. Включайте ID конечной точки, ID события, время сбоя и очищенные логи приёмника.