Перейти к содержимому страницы
Документация
Документация Make Agent Fast

Вебхуки

Зарегистрируйте публичную конечную точку, проверяйте подписанные сырые полезные нагрузки, дедуплицируйте события и безопасно управляйте повторами.

Кратко

Зарегистрируйте публичную конечную точку, проверяйте подписанные сырые полезные нагрузки, дедуплицируйте события и безопасно управляйте повторами.

Доставка вебхуковПодписанные события уходят с платформы на ваш endpoint
Событие платформыПодписьHMACHTTPS POSTВаше приложение
HMAC-SHA256Минимум раз8 попыток

Вебхуки разработчика отправляют события аккаунта на ваш сервер, чтобы не нужно было опрашивать. Доставка минимум один раз: события могут дублироваться, задерживаться или приходить не по порядку.

Зарегистрируйте конечную точку#

Создайте ключ с 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 только после успеха проверки подписи.

Подтверждайте и обрабатывайте#

  1. Проверьте подпись и временную метку.
  2. Вставьте ID события в таблицу с уникальным ограничением.
  3. Если оно уже существует, верните 204 без повторения побочного эффекта.
  4. Зафиксируйте событие или поставьте в очередь вашу внутреннюю задачу.
  5. Быстро верните ответ 2xx.

Отправитель Make Agent Fast истекает по времени после 10 секунд. Он считает успешными только 2xx и не выполняет редиректы.

Поведение повторов#

Неудачные доставки ставятся в очередь до восьми попыток. Откат начинается около двух секунд, удваивается с джиттером и ограничен одним часом; фактическое время доставки может быть позже, когда воркеры заняты. Поскольку повторы могут пережить инициирующий запрос и приходить не по порядку, никогда не полагайтесь на приход одного события непосредственно перед другим.

Возвращайте ответ не-2xx только когда хотите, чтобы Make Agent Fast повторил. Для навсегда неподдерживаемой версии события или удалённого назначения примите и запишите его или удалите конечную точку вместо производства бесконечных временных сбоев.

Ротируйте секрет подписи#

Секреты подписи не могут быть раскрыты или отредактированы. Для безопасной ротации:

  1. Создайте вторую конечную точку, указывающую на временный или версионный путь приёмника.
  2. Сохраните её недавно раскрытый секрет.
  3. Принимайте и дедуплицируйте события от обеих конечных точек.
  4. Проверьте, что новая конечная точка получает валидные доставки.
  5. Удалите старую конечную точку через DELETE /webhook-endpoints/{endpoint_id}.

Если URL приёмника остаётся тем же, заставьте принимающее приложение принимать оба секрета во время перекрытия и используйте ID событий для предотвращения дублирующих побочных эффектов.

Решение проблем#

СимптомПроверка
Нет доставокСтатус конечной точки, выбранные события, публичный DNS, сертификат HTTPS и произошло ли событие вообще
Несовпадение подписиДоступ к сырому телу, точная конкатенация timestamp.body, правильный секрет конечной точки и трансформации тела проксей
Повторные доставкиКод возврата, 10-секундный таймаут, внутренние исключения и уникальность ID событий
Ошибка приватного адресаЗаписи DNS должны разрешаться только в публичные адреса; localhost и внутренние диапазоны отклоняются
Сбой редиректаЗарегистрируйте финальный HTTPS URL напрямую; редиректы не выполняются

Никогда не включайте секрет подписи, ключ bearer или полную полезную нагрузку посетителя в сообщение поддержки. Включайте ID конечной точки, ID события, время сбоя и очищенные логи приёмника.