Saltar al contenido de la página
Documentación
Documentación de Make Agent Fast

Webhooks

Registra un endpoint público, verifica payloads sin procesar firmados, deduplica eventos y gestiona los reintentos de forma segura.

De un vistazo

Registra un endpoint público, verifica payloads sin procesar firmados, deduplica eventos y gestiona los reintentos de forma segura.

Entrega de webhooksEventos firmados salen de la plataforma hacia tu endpoint
Evento de la plataformaFirmarHMACHTTPS POSTTu app
HMAC-SHA256Al menos una vez8 intentos

Los webhooks para desarrolladores envían a tu servidor los eventos de tu cuenta, de modo que no necesitas hacer polling. La entrega es de al menos una vez: los eventos pueden duplicarse, retrasarse o llegar desordenados.

Registrar un endpoint#

Crea una clave con webhooks:write y luego registra un receptor HTTPS público junto con el conjunto mínimo de eventos que necesites.

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"]
  }'

La URL debe usar HTTPS, no puede incluir usuario, contraseña, fragmento ni puerto personalizado, y debe resolver únicamente a direcciones IP públicas. Durante la entrega no se siguen las redirecciones.

La respuesta 201 revela una sola vez un secreto de firma whsec_.... Guárdalo en el gestor de secretos del receptor antes de cerrar la respuesta. Si más adelante listas los endpoints, obtendrás la URL, los eventos, el estado, la última entrega y el último error, pero no el secreto.

Tipos de eventos#

EventoSe emite cuando
site.publishedUn sitio pasa a estar publicado
site.unpublishedUn sitio vuelve al estado de borrador
lead.createdEl agente captura un nuevo lead
lead.qualifiedEl agente califica un lead, o un lead se acepta en el pipeline de ventas
booking.requestedUn visitante solicita o reserva un horario
handoff.requestedEl agente transfiere una conversación a una persona (handoff)
opportunity.createdUn lead se convierte en una oportunidad del pipeline
opportunity.updatedCambia la asignación, el valor, la siguiente acción u otros campos de una oportunidad
opportunity.stage_changedUna oportunidad pasa a otra etapa
opportunity.wonUna oportunidad se cierra como ganada
opportunity.lostUna oportunidad se cierra como perdida
conversation.startedComienza un nuevo hilo de conversación
conversation.message.createdSe añade un mensaje a una conversación
knowledge.readyUna fuente de conocimiento termina de procesarse
broadcast.sentUn broadcast termina su flujo de envío
domain.activatedRetirado: no se ofrecen dominios personalizados; este evento nunca se emite

Formato de la solicitud#

Make Agent Fast envía un POST con Content-Type: application/json, User-Agent: MakeAgentFast-Webhooks/1.0 y estas cabeceras:

maf-event-id: evt_...
maf-event-type: lead.created
maf-signature: t=1784210566,v1=HEX_HMAC_SHA256

El cuerpo JSON tiene un envelope estable. Los campos dentro de data dependen del evento y pueden recibir campos adicionales.

{
  "id": "evt_2c34...",
  "type": "lead.created",
  "created_at": "2026-07-16T10:02:46.000Z",
  "data": {
    "site_id": "SITE_ID",
    "lead_id": "LEAD_ID"
  }
}

Usa el id del cuerpo o maf-event-id como clave de deduplicación. No uses la hora de entrega ni un ID de base de datos generado.

Verificar la firma en Node.js#

Lee los bytes exactos sin procesar antes de parsear el JSON. El valor firmado es 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"));
}

Rechaza las firmas mal formadas y las marcas de tiempo anteriores a tu ventana de repetición aceptada; cinco minutos es un valor predeterminado razonable. Compara bytes de longitud fija en tiempo constante.

Verificar la firma en 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)

Parsea y procesa el JSON solo después de que la verificación de la firma haya sido correcta.

Confirmar y procesar#

  1. Verifica la firma y la marca de tiempo.
  2. Inserta el ID del evento en una tabla con una restricción de unicidad.
  3. Si ya existe, devuelve 204 sin repetir el efecto secundario.
  4. Confirma el evento (commit) o encola tu tarea interna.
  5. Devuelve rápidamente una respuesta 2xx.

El emisor de Make Agent Fast agota el tiempo de espera a los 10 segundos. Solo considera correcta una respuesta 2xx y no sigue redirecciones.

Comportamiento de los reintentos#

Las entregas fallidas se encolan hasta un máximo de ocho intentos. El backoff comienza en unos dos segundos, se duplica con jitter y tiene un tope de una hora; la hora real de entrega puede ser posterior cuando los workers están ocupados. Como los reintentos pueden sobrevivir a la solicitud que los originó y llegar desordenados, nunca dependas de que un evento llegue inmediatamente antes que otro.

Devuelve una respuesta que no sea 2xx solo cuando quieras que Make Agent Fast reintente. Si un evento tiene una versión que ya no se admite de forma permanente o el destino fue eliminado, acepta y registra el evento, o elimina el endpoint, en lugar de generar fallos transitorios interminables.

Rotar un secreto de firma#

Los secretos de firma no se pueden revelar ni editar. Para rotarlos de forma segura:

  1. Crea un segundo endpoint que apunte a una ruta de receptor temporal o con versión.
  2. Guarda el secreto recién revelado.
  3. Acepta y deduplica los eventos de ambos endpoints.
  4. Comprueba que el nuevo endpoint recibe entregas válidas.
  5. Elimina el endpoint antiguo con DELETE /webhook-endpoints/{endpoint_id}.

Si la URL del receptor se mantiene igual, haz que la aplicación receptora acepte ambos secretos durante el periodo de solapamiento y utiliza los IDs de evento para evitar efectos secundarios duplicados.

Solución de problemas#

SíntomaQué revisar
No hay entregasEstado del endpoint, eventos seleccionados, DNS público, certificado HTTPS y si el evento realmente ocurrió
La firma no coincideAcceso al cuerpo sin procesar, concatenación exacta de timestamp.body, secreto correcto del endpoint y transformaciones del cuerpo por parte de proxies
Entregas repetidasCódigo de respuesta, tiempo de espera de 10 segundos, excepciones internas y unicidad del ID de evento
Error de dirección privadaLos registros DNS deben resolver solo a direcciones públicas; localhost y los rangos internos se rechazan
Fallo por redirecciónRegistra directamente la URL HTTPS final; las redirecciones no se siguen

Nunca incluyas el secreto de firma, la clave bearer ni el payload completo de un visitante en un mensaje a soporte. Incluye el ID del endpoint, el ID del evento, la hora del fallo y los logs del receptor sanitizados.