Los webhooks son simples en la documentación. En producción, no

Este artículo analiza por qué las integraciones de webhooks fallan en producción mucho después de que el demo inicial funciona, cubriendo entregas duplicadas, eventos fuera de orden, requests falsificadas y reintentos que sobreviven las suposiciones de tiempo, junto con las decisiones de diseño que previenen cada modo de falla.

Equipo de desarrollo de software trabajando
10 ago 20269 min de lectura
Actualizado el 17 ago 2026

Configuras un webhook en veinte minutos. Registras una URL, elegís los eventos que te importan, ves llegar el primer payload en los logs. Se siente como una de esas piezas de infraestructura que simplemente funcionan. Tres semanas después, un proveedor de pagos dispara el mismo evento de "pago exitoso" cuatro veces seguidas, tu handler lo procesa cuatro veces, y un cliente termina cobrado dos veces por la misma orden.

Esa parte nunca aparece en la guía de inicio rápido. Los webhooks son una de las ideas más simples en sistemas distribuidos: cuando pasa algo, llamá a esta URL. Pero la distancia entre "llamar a esta URL cuando pasa algo" y "construir algo que sobreviva al internet real" es enorme, y casi nadie la cierra hasta que algo se rompe en producción.

La garantía de entrega que nadie te prometió

Esto es lo que la mayoría de los equipos hace mal el primer día: asumen entrega "al menos una vez" cuando en realidad están construyendo para "exactamente una vez", y no notan el desajuste hasta que les cuesta dinero. Cualquier proveedor serio de webhooks, Stripe, GitHub, Shopify, el que sea, documenta que los eventos pueden llegar más de una vez y de hecho van a llegar más de una vez. Cortes de red, timeouts de tu lado, reintentos del proveedor después de un error 5xx, un balanceador de carga que mata una conexión a mitad de respuesta. Todo eso se ve igual desde el lado de quien envía: no llegó confirmación, así que se reenvía.

Si tu handler trata cada webhook entrante como un evento nuevo que nunca vio antes, no tienes una integración de webhooks. Tienes un sistema que en algún momento va a procesar dos veces algo importante, y suele ser dinero, inventario, o una notificación que sale duplicada y hace que tu producto se vea roto.

La solución no es complicada en teoría. Cada evento tiene un ID. Guardas qué IDs ya procesaste. Revisas antes de actuar. En la práctica eso significa una tabla extra, una restricción de unicidad, y una decisión sobre cuánto tiempo guardas ese historial, porque "para siempre" no es gratis y "24 horas" puede no ser suficiente si el proveedor decide reintentar una entrega fallida una semana después. Stripe, como referencia, reintenta ciertos webhooks fallidos durante hasta tres días. Diseña tu ventana de deduplicación para esa realidad, no para el caso feliz que probaste en desarrollo.

El orden es una promesa que casi ningún proveedor hace

La segunda suposición que rompe cosas en silencio es el orden. Construiste tu handler asumiendo que el evento de "orden creada" llega antes que el de "orden enviada", porque así fue que pasaron los hechos. El problema es que las requests HTTP no se ponen en fila educadamente. Dos eventos disparados con sesenta milisegundos de diferencia pueden llegar a tu servidor en cualquier orden, sobre todo cuando corres más de una instancia detrás de un balanceador, o cuando un reintento queda encolado detrás de un evento más reciente.

La mayoría de los proveedores lo dicen explícitamente en su documentación, y la mayoría de las integraciones ignora la advertencia igual, porque construir un handler que no dependa del orden es más trabajo que construir uno que asume una línea de tiempo. Las opciones honestas son: diseñar tu máquina de estados para que los eventos fuera de orden sean seguros (una orden que ya se marcó como enviada simplemente ignora un evento tardío de "orden creada"), o volver a consultar el estado actual en la API de origen en lugar de confiar en que el payload cuenta toda la historia. Las dos cuestan más por adelantado que "procesar el evento como llega". Las dos te evitan el reporte de bug que dice que el sistema muestra una orden creada después de que se envió, algo genuinamente difícil de explicar a alguien que no es técnico.

Verificar la firma no es opcional, y saltearlo es más común de lo que parece

Tu endpoint de webhook es una URL pública que dispara acciones reales en tu sistema cuando recibe un POST. Si no verificas que la request realmente viene del proveedor, construiste una API que cualquiera en internet puede llamar para hacerle creer a tu sistema que un pago se completó, que se creó una cuenta, o que un envío salió.

Todo proveedor que se toma esto en serio firma sus payloads con una firma HMAC en un header, y la documentación de cada proveedor explica cómo verificarla. La razón por la que esto sigue apareciendo como un hueco en sistemas de producción no es ignorancia. Es que verificar la firma se siente como una formalidad cuando estás probando localmente con curl y un payload hardcodeado, así que queda comentado "por ahora", y el ticket para agregarlo nunca se prioriza una vez que la feature sale y funciona.

Los ingenieros que hacen esto bien tratan la verificación de firma como parte del endpoint, no como una mejora sobre él. No existe una versión "terminada" de un handler de webhooks sin eso, de la misma manera que no existe una versión "terminada" de un login sin verificar la contraseña.

Los reintentos van a sobrevivir tus suposiciones sobre el tiempo

Los proveedores no se rinden después de un intento fallido. Reintentan, generalmente con backoff exponencial, a veces durante días. Eso significa que tu handler tiene que sobrevivir a que lo llamen por un evento que pasó el martes pasado, con un estado que cambió por debajo mientras tanto. Una orden que se canceló después de que se disparó el webhook pero antes de que llegara el reintento. Un usuario que se borró entre el primer intento y el quinto.

Ahí es donde la idempotencia y el orden dejan de ser preocupaciones separadas y empiezan a combinarse. Un handler idempotente que asume que el estado actual coincide con el timestamp del payload va a seguir fallando. Los incidentes reales casi nunca los causa la primera entrega. Los causa el tercer reintento, cuatro días después, golpeando un sistema que ya siguió adelante sin avisarle a nadie.

Los timeouts son un contrato que no leíste

Los proveedores esperan que tu endpoint responda rápido, generalmente en pocos segundos, y cuentan un timeout como un fallo que vale la pena reintentar. Si tu handler hace trabajo real de forma sincrónica (actualizar una base de datos, llamar a una API downstream, enviar una notificación) antes de devolver un 200, estás apostando a que todo eso termine dentro de una ventana que no controlas.

El patrón que funciona: confirmar la recepción de inmediato, y hacer el trabajo real de forma asíncrona en una cola. Eso agrega una pieza más al sistema, que es exactamente por qué muchos equipos lo saltean al principio. También es por qué esos mismos equipos terminan debugueando procesamiento duplicado misterioso más adelante, porque un handler sincrónico lento que hace timeout se ve, del lado del proveedor, exactamente igual a un handler que nunca recibió el mensaje. Así que reintenta. Y vuelves al primer problema, solo que esta vez lo causaste tú mismo.

El costo de hacer esto mal se acumula en silencio

Lo peligroso de un handler de webhooks mal diseñado es que puede correr durante meses sin que nadie lo note. El procesamiento duplicado no tira abajo el servidor. Cobra dos veces a un cliente de cada diez mil, o dispara una notificación duplicada una vez cada cien mil, y a menos que alguien esté buscando duplicados específicamente, la primera señal de que algo anda mal es un ticket de soporte que parece un caso aislado en vez de un patrón.

Para cuando alguien conecta los puntos, suele haber un backlog de registros afectados sin una forma clara de distinguir cuáles se duplicaron realmente y cuáles se procesaron dos veces por otra razón legítima. Desenredar eso después de los hechos toma mucho más tiempo del que hubiera tomado construir la validación desde el principio. Esta es la parte que convierte la confiabilidad de los webhooks en un tema genuinamente senior y no en un checkbox: la falla no se anuncia, se acumula.

Nada de esto convierte a los webhooks en la opción equivocada

Nada de lo anterior es un argumento contra los webhooks. El polling es peor: más lento, más caro a escala, y no elimina ninguno de estos problemas, solo los esconde detrás de otro conjunto de suposiciones. Los webhooks siguen siendo la herramienta correcta para integración basada en eventos entre sistemas que no comparten infraestructura, y no van a desaparecer.

El punto es que "agregar un webhook" no es una tarea chica, aunque el código para recibir uno sean diez líneas. Recibir un payload es la tarea chica. Construir algo que sobreviva entregas duplicadas, llegadas fuera de orden, requests falsificadas, reintentos tardíos, y su propio tiempo de respuesta, todo al mismo tiempo, sin que nadie lo note cuando funciona bien, es la tarea real. Eso no es una feature de fin de semana. Es infraestructura, y merece que se le asigne presupuesto, revisión y dueño como a cualquier otra pieza de infraestructura, mucho antes de que llegue el primer ticket que pregunta por qué a un cliente le cobraron dos veces.

ESCRITO POR

Lead de contenido editorial de Howdy
Matías GomezEditorial Lead
COMPARTIR