¿Qué es un Webhook?
Un POST HTTP enviado automáticamente cuando ocurre un evento. Inverso del polling: los servicios envían en vez de que tú preguntes.
¿Qué es un webhook?
Un webhook es una petición HTTP POST que un servicio envía a otro servicio automáticamente cuando ocurre algo. El servicio receptor registra una URL con el servicio emisor de antemano; cuando ocurre el evento disparador, el emisor hace POST de un payload JSON (o form-encoded) a esa URL. El receptor devuelve un 200 OK para confirmar, y el emisor sigue. Sin polling, sin esperar — eventos entregados según ocurren.
Los webhooks son la fontanería de las integraciones de internet modernas. Cuando Stripe cobra una tarjeta, hace POST de un evento charge.succeeded a tu URL de webhook registrada. Cuando GitHub mergea un PR, hace POST de un evento pull_request.closed. Cuando Shopify envía un pedido, tu app recibe un POST order.fulfilled. El patrón es idéntico entre miles de productos SaaS: te digo una URL, tú me haces POST cuando pasan cosas.
Webhooks vs polling: por qué ganaron los webhooks
Antes de los webhooks, la integración significaba polling: tu servicio pregunta repetidamente a la API "¿hay algo nuevo?" cada pocos segundos. El polling tiene tres problemas:
- Latencia. Si haces polling cada 60 s, tu retraso medio de detección de eventos es 30 s. Las features en tiempo real (chat, pagos, alertas) no toleran esto.
- Carga desperdiciada. El 99% de los polls devuelven "nada nuevo". Estás martilleando la API por respuestas vacías, pagando compute y ancho de banda por nada.
- Límites de tasa. La mayoría de APIs limitan requests por minuto. El polling agresivo agota el presupuesto; el polling conservador empeora la latencia.
Los webhooks invierten el modelo. El proveedor sabe cuándo ocurre un evento (lo causó), así que te hace POST inmediatamente — típicamente en cientos de milisegundos. Cero polling, cero requests desperdiciadas, cero desperdicio de límites de tasa. El proveedor obtiene lógica de integración más simple; el consumidor obtiene actualizaciones casi en tiempo real.
Cómo funciona la entrega de un webhook (paso a paso)
Un ciclo de vida típico de webhook:
- Registras una URL con el proveedor. Normalmente en una UI de configuración: "Envía webhooks a
https://api.myapp.com/webhooks/stripe". El proveedor almacena esta URL. - Ocurre un evento del lado del proveedor. Un usuario paga, un build termina, una fila se actualiza.
- El proveedor construye un payload. Normalmente JSON:
{"event": "charge.succeeded", "data": {"id": "ch_123", "amount": 1500}}. - El proveedor hace POST del payload a tu URL. Las cabeceras normalmente incluyen el tipo de evento, un ID de entrega para idempotencia y una cabecera de firma para verificación.
- Tu endpoint recibe el POST y lo procesa. Verifica la firma, parsea el JSON, haz lo que el evento requiera (actualizar BD, enviar email, fan-out a otros servicios).
- Tu endpoint devuelve 200 OK rápidamente. La mayoría de proveedores consideran las respuestas no-2xx un fallo de entrega y reintentan con backoff exponencial. Si el procesamiento tarda más de unos segundos, acepta el webhook (200) y encola el trabajo para procesamiento asíncrono.
Verificar autenticidad del webhook (no te saltes esto)
Cualquiera que conozca tu URL de webhook puede hacer POST a ella. Sin verificación, un atacante puede falsificar eventos — fingir que Stripe te pagó, fingir que GitHub mergó un PR malicioso, fingir que llegó un envío. Tres patrones comunes de verificación:
- Firma HMAC. El proveedor firma el payload con un secreto compartido usando HMAC-SHA256, envía la firma en una cabecera (
Stripe-Signature,X-Hub-Signature-256, etc.). Tu endpoint recalcula la HMAC y rechaza requests que no coincidan. La mayoría de proveedores modernos (Stripe, GitHub, Shopify) usan esto. - Token bearer en cabecera. Más simple pero menos robusto: el proveedor envía un token secreto estático en
Authorization: Bearer .... Cualquiera que capture un request válido puede replayearlo; menos común en sistemas modernos. - Lista blanca de IPs. Acepta POSTs solo de rangos de IP de proveedor documentados. Útil como defensa en profundidad pero se rompe cuando los proveedores rotan IPs.
La firma HMAC es el default correcto. Combina con una ventana de replay corta (rechaza requests con timestamps de hace más de unos minutos) para prevenir ataques de replay.
Idempotencia: gestiona entregas duplicadas
Las redes fallan. Los proveedores reintentan. Tu endpoint recibirá el mismo webhook dos veces — a veces más. Las integraciones ingenuas crean filas de base de datos duplicadas, envían emails duplicados, cobran a clientes dos veces. La idempotencia previene esto.
Dos enfoques comunes:
- Clave de idempotencia del proveedor. La mayoría de proveedores incluyen un ID de entrega en cabeceras (
Stripe-Webhook-Id,X-GitHub-Delivery) — globalmente único por evento. Almacena el ID tras procesar y rechaza duplicados. Simple, fiable. - Dedup a nivel de aplicación. Calcula un hash del tipo de evento + campos clave (ej. ID de pedido + estado) y trata hashes repetidos dentro de un TTL como duplicados. Útil cuando el ID de entrega del proveedor no es fiable.
Patrones de fiabilidad de webhooks
Confirma rápido, procesa async
El receptor de webhook debería devolver 200 en menos de 1-2 segundos, incluso si el trabajo real tarda más. Empuja el payload a una cola (SQS, Redis stream, Postgres LISTEN/NOTIFY) y procesa desde un worker. El procesamiento síncrono lento causa timeouts del proveedor, reintentos y entregas duplicadas.
Implementa una tabla outbox / inbox
Persiste los webhooks entrantes a una tabla inbox ANTES de hacer cualquier procesamiento. Si tu worker crashea a medio procesamiento, puedes replayear desde la inbox. El ID de entrega del proveedor se convierte en la clave primaria de la inbox para idempotencia.
Monitoriza el éxito de entrega
La mayoría de proveedores exponen un log de entregas (Stripe → Webhooks → entregas recientes). Compruébalo periódicamente. Un pico en respuestas 5xx o 4xx en tu endpoint normalmente significa que tu inbox está rota antes de que lo notarías de otra forma.
Usa túneles webhook en desarrollo
Herramientas como ngrok, Smee y Cloudflare Tunnel exponen tu localhost a internet público para que los proveedores puedan hacer POST a tu entorno de desarrollo. Esencial para depuración local — los webhooks no pueden alcanzar localhost:3000 desde los servidores de Stripe sin uno.
Gotchas comunes de webhooks
- Entrega fuera de orden. Los proveedores normalmente entregan eventos en orden, pero los reintentos de red pueden voltear el orden. No asumas que
order.createdllega antes queorder.fulfilled— gestiona ambas posibilidades. - Límites de tamaño de payload. Muchos proveedores limitan payloads a 4-8 MB. Las entidades grandes (ej. una exportación de 50.000 filas) se envían como URL para obtener por separado, no inline.
- Estrictez HTTP/HTTPS. La mayoría de proveedores requieren HTTPS; algunos rechazan certs autofirmados. Usa un cert real (Let's Encrypt es gratis).
- Parseo de body antes de verificación de firma. Muchos esquemas HMAC firman el body crudo. Si tu framework parsea JSON primero, puede que ya no tengas los bytes crudos. Usa middleware que preserve el body crudo para verificación, luego parsea.
FAQ: Webhooks
¿Son los webhooks lo mismo que una URL de callback?
Sí — "webhook", "callback HTTP" y "API inversa" describen todos el mismo patrón. Diferentes términos vinieron de diferentes comunidades (desarrolladores web, proveedores de API, defensores de REST) pero el mecanismo técnico es idéntico.
¿Cuál es la diferencia entre webhooks y WebSockets?
Los webhooks son POSTs HTTP unidireccionales enviados cuando ocurren eventos. Los WebSockets son conexiones TCP bidireccionales persistentes. Usa webhooks para eventos ocasionales entre servicios; usa WebSockets para comunicación bidireccional de alta frecuencia (chat, cursores en vivo, colaboración en tiempo real).
¿Puede mi receptor estar hospedado en serverless?
Sí — los webhooks encajan genial con AWS Lambda, Cloudflare Workers o Vercel functions. El modelo de confirmación breve (devuelve 200 rápido, procesa async) mapea bien a serverless. Solo dimensiona la función para cold starts rápidos; algunos proveedores hacen timeout agresivamente.
¿Cómo pruebo handlers de webhook localmente?
Usa un túnel: ngrok, Smee, Cloudflare Tunnel. El túnel expone tu localhost sobre una URL HTTPS pública a la que los proveedores pueden hacer POST. Stripe, GitHub y la mayoría de proveedores modernos también ofrecen un CLI para forwardear eventos directamente a localhost sin un túnel.
¿Qué pasa si mi endpoint está caído cuando ocurre un evento?
La mayoría de proveedores reintentan con backoff exponencial (1 min, 5 min, 30 min, etc.) durante 24-72 horas. Después de eso, el evento se descarta — y tus datos son permanentemente inconsistentes. Monitoriza fallos de entrega y ten un mecanismo de replay idempotente para outages más largos que la ventana de reintento.
¿Debo usar webhooks o polling para mi caso de uso?
Webhooks para: eventos en tiempo real, triggers de baja frecuencia, notificaciones de pago, cualquier cosa donde la latencia importe. Polling para: sincronización de datos masivos, situaciones donde el proveedor no ofrece webhooks, o cuando no puedes ejecutar un endpoint HTTPS público. En la práctica, las integraciones modernas usan ambos: webhooks para alertas inmediatas, polling periódico como red de seguridad para cazar entregas perdidas.
Cómo usa LoadFocus los webhooks
LoadFocus expone webhooks para eventos de finalización de test y alertas: cuando tu prueba de carga programada termina, hacemos POST de la URL de resultados a tu endpoint registrado para que tu pipeline de CI pueda reaccionar (postear a Slack, gatear un deploy, actualizar un dashboard). Cuando un monitor de API dispara una alerta, el mismo patrón de payload entrega el incidente a PagerDuty, Opsgenie o tu servicio personalizado. Configura webhooks en tu configuración de cuenta; las firmas HMAC se incluyen por defecto.
Herramientas LoadFocus relacionadas
Lleva este concepto a la práctica con LoadFocus — la misma plataforma que potencia todo lo que acabas de leer.