Webhooks (Interacción Asincrónica de Servicios)
Patrón arquitectónico de interacción asincrónica entre servicios (Event-driven Push), donde el proveedor de eventos envía una solicitud HTTP POST con datos a una URL registrada del consumidor al ocurrir un evento del sistema.
1. Visión general del concepto y problema sistémico
La integración tradicional de sistemas a través de polling constante (Polling / Short Polling) sufre de dos problemas mutuamente excluyentes:
- Alta latencia de respuesta: Si el cliente verifica cambios cada 60 segundos, los datos se entregan en promedio con un retraso de 30 segundos.
- Colosal desperdicio de recursos computacionales: El 99% de las solicitudes HTTP devuelven una respuesta vacía
304 Not Modifiedo[], sobrecargando la base de datos y la pila de red.
Webhooks (Llamadas inversas a través de HTTP) cambian la paradigma de Pull a Push. En lugar de preguntar: "¿Hay nuevos datos?", el consumidor registra su URL (Webhook Listener). Tan pronto como ocurre un evento en el sistema del proveedor (transacción exitosa de Stripe, nuevo commit en GitHub, mensaje entrante de Telegram), el proveedor envía una solicitud HTTP POST con un payload JSON directamente al servidor del suscriptor.
Polling tradicional (Desperdicio de recursos):
Cliente ---> GET /events?since=... ---> Servidor (Vacío: [])
Cliente ---> GET /events?since=... ---> Servidor (Vacío: [])
Cliente ---> GET /events?since=... ---> Servidor (¡Nuevo evento!)
Webhook impulsado por eventos (Push inmediato):
Proveedor de eventos (Stripe / GitHub / Telegram)
|
| Evento: charge.succeeded (Event Payload + HMAC firma)
| HTTP POST https://api.mysite.com/webhooks/stripe
v
Consumidor (Validación de firma -> 200 OK -> Cola en segundo plano RabbitMQ/Redis)
2. Taxonomía arquitectónica y modelo mental
El ciclo de vida de un webhook se basa en tres pilares arquitectónicos:
- Autenticación criptográfica (Firma HMAC):
- El proveedor genera un hash criptográfico del cuerpo de la solicitud (
raw body) utilizando una clave secreta compartida (Webhook Secret), añadiéndolo en el encabezado (por ejemplo,Stripe-SignatureoX-Hub-Signature-256). - El consumidor calcula el mismo hash y lo valida, eliminando el riesgo de falsificación de la solicitud por un atacante.
- El proveedor genera un hash criptográfico del cuerpo de la solicitud (
- Idempotencia y deduplicación (Motor de Idempotencia):
- Cada evento tiene un ID único global (
evt_xxxxxxxxxx). - El consumidor verifica atómicamente la existencia del ID en la caché/BD (por ejemplo, a través de
INSERT ... ON CONFLICT DO NOTHINGo RedisSET key NX EX 86400).
- Cada evento tiene un ID único global (
- Política de entrega garantizada y reintentos (Retry & Dead-Letter Queue):
- En caso de que el servidor del consumidor no esté disponible, el proveedor reintenta las solicitudes con una escala exponencial (Exponential Backoff con jitter): después de 5 minutos, 30 minutos, 2 horas, 12 horas.
- Los eventos que no se pudieron procesar después del número máximo de intentos se envían a la Dead-Letter Queue (DLQ) para análisis manual por ingenieros.
3. Pipeline técnico y mecánica interna
Manejador de producción con verificación HMAC (Node.js / Express / TypeScript)
[!IMPORTANTE] Para verificar la firma, es necesario obtener el cuerpo sin procesar de la solicitud (
raw buffer), y no el objeto parseadoreq.body, de lo contrario, el hash no coincidirá debido a discrepancias en los espacios y el orden de las claves JSON.
import express, { Request, Response } from "express";
import crypto from "crypto";
const app = express();
// Obtención de raw buffer para endpoints de webhooks
app.post(
"/api/v1/stripe-webhook",
express.raw({ type: "application/json" }),
async (req: Request, res: Response) => {
const signature = req.headers["stripe-signature"] as string;
const secret = process.env.STRIPE_WEBHOOK_SECRET!;
if (!signature) {
return res.status(400).send("Falta el encabezado de firma");
}
// 1. Validación criptográfica de la firma a través de HMAC SHA-256
const computedHmac = crypto
.createHmac("sha256", secret)
.update(req.body)
.digest("hex");
// Comparación segura en tiempo fijo
const isValid = crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(computedHmac)
);
if (!isValid) {
return res.status(401).send("Firma inválida");
}
const payload = JSON.parse(req.body.toString());
const eventId = payload.id;
// 2. Verificación idempotente de deduplicación
const isNew = await redis.set(`webhook:processed:${eventId}`, "1", "NX", "EX", 86400);
if (!isNew) {
// El evento ya ha sido procesado — devolvemos 200 OK sin reejecución
return res.status(200).json({ status: "already_processed" });
}
// 3. Envío de trabajo pesado a la cola BullMQ
await eventQueue.add("process_payment", payload);
// 4. Confirmación inmediata de recepción
return res.status(200).json({ received: true });
}
);
4. Escenarios prácticos de ingeniería en producción
01. Pasarela de pago para AI SaaS (Stripe / Paddle / LiqPay)
El usuario paga una suscripción al plan Pro. El sistema de pago genera un evento invoice.payment_succeeded. El webhook actualiza instantáneamente el estado del usuario en la base de datos, restablece los límites de tokens en Redis y envía un correo de bienvenida, permitiendo al usuario continuar sin recargar la página.
02. Disparador de pipeline CI/CD autónomo (GitHub Webhooks)
Un ingeniero realiza git push en la rama main. GitHub envía un payload con el hash del commit a su propio servidor Coolify o ArgoCD. El servidor verifica el secreto, clona el código modificado, ejecuta pruebas unitarias en un contenedor Docker aislado y actualiza el servicio de producción según la estrategia Blue/Green.
03. Recolección asincrónica de resultados de inferencia de modelos AI pesados
Al invocar modelos de generación de video (Runway, Kling) o entrenamiento de LoRA, la solicitud dura varios minutos. En lugar de mantener una conexión HTTP abierta, el cliente proporciona webhook_url. Tan pronto como el clúster de GPU completa la generación, envía un webhook con el enlace final de S3 al medio generado.
5. Errores comunes, trampas y seguridad
- Parsing JSON antes de la verificación de la firma:
Si antes de la verificación de la firma se ejecutó el middleware estándar
express.json(), el serializador puede cambiar el orden de los campos, escapar caracteres Unicode o formatear números, lo que resultará en una discrepancia en la suma de verificación HMAC y un fallo deInvalid Signature. - Bloqueo de ejecución sincrónica (Webhook Deadlock):
La ejecución de transacciones pesadas o solicitudes HTTP prolongadas a APIs externas dentro del manejador de webhook garantiza un timeout en el lado del proveedor. Devuelva
200 OKinmediatamente después de validar la firma y guardar en la cola. - Falta de validación de marcas de tiempo (Replay Attacks): Un atacante que interceptó una solicitud de webhook legítima firmada puede volver a enviarla una hora después. Los proveedores añaden una marca de tiempo (timestamp) a la firma. Verifique que la diferencia entre el tiempo de la solicitud y el reloj del sistema del servidor no exceda la ventana permitida (por ejemplo, 5 minutos).
FAQ: Webhooks (Interacción Asincrónica de Servicios)
Términos relacionados
Telegram Bot API
Interfaz HTTP oficial de la plataforma Telegram que permite crear chatbots autónomos, asistentes AI, interfaces de Telegram Mini Apps (TMA) y canales de notificación interactiva.
Cron Schedulers y Timers de Systemd
Demonios del sistema (Linux cron, systemd timers) y colas distribuidas (BullMQ, Temporal) que garantizan la ejecución programada de tareas de ingeniería periódicas, copias de seguridad, sincronización de datos y agentes de IA.
Rate Limiting (Limitación de Frecuencia de Solicitudes y Protección de API)
Mecanismo sistémico para controlar la intensidad del tráfico entrante y saliente (Token Bucket, Sliding Window) para proteger el backend de agotamiento de recursos, ataques de fuerza bruta, DDoS de capa 7 y sobregiros financieros en puntos finales de IA.
Proxy Inverso (Nginx, Caddy, Traefik)
Capa arquitectónica intermedia que recibe tráfico externo de Internet (puertos 80/443), realiza la terminación SSL/TLS, compresión (Brotli/Gzip), almacenamiento en caché de estáticos y enruta de manera segura las solicitudes a aplicaciones internas.