Webhooks
Los Webhooks son notificaciones automatizadas a URLs definidas, que informan en tiempo real sobre eventos. Son útiles porque permiten una respuesta inmediata a eventos específicos, sin necesidad de una consulta continua. Los Webhooks ayudan a mantener los sistemas eficientes y receptivos.
Sus Webhooks se activan inmediatamente cuando ocurre un evento. Si no podemos alcanzar su URL o si responde con un estado de error, repetimos el envío según un plan fijo y, además, comprobamos automáticamente durante varios días si el receptor está fallando de forma permanente. Encontrará los detalles más abajo en Entrega y reintentos.
Entrega y reintentos
Cuándo reintentamos un Webhook
Un Webhook solo se vuelve a entregar cuando la respuesta de su servidor indica un error temporal:
- Name
HTTP 429- Type
- límite de tasa
- Description
Su endpoint indica que actualmente está recibiendo demasiadas solicitudes.
- Name
HTTP 5xx- Type
- error de servidor
- Description
En concreto
500,502,503y504, además de los códigos de error de origen de Cloudflare520a524(por ejemplo,521"Web Server Is Down", que Cloudflare devuelve en lugar de502para dominios con proxy).
- Name
Errores de conexión- Type
- red
- Description
Tiempos de espera, fallos de resolución DNS, errores de TLS/handshake o una conexión que se interrumpe durante la solicitud.
Todas las demás respuestas se consideran finales y no desencadenan otro intento de entrega. Esto incluye los códigos 4xx como 400, 401, 403 y 404, ya que normalmente indican un error de configuración en el lado del receptor que no se puede solucionar simplemente reintentando. Las respuestas en el rango 2xx y 3xx se consideran exitosas.
Tiempo de espera de la solicitud
Cada intento de entrega tiene un tiempo de espera de 5 segundos. Si su servidor no responde dentro de este margen, tratamos el intento como fallido y programamos un reintento según el plan que se describe a continuación.
Plan de reintentos
Si un intento falla, el siguiente se programa tras un retraso fijo y creciente. Realizamos un total de hasta 14 intentos; después, el trabajo se descarta. La ventana total de entrega abarca por tanto aproximadamente 3,5 días.
| Intento | Espera desde el intento anterior |
|---|---|
| 1 (inicial) | inmediatamente |
| 2 | 1 minuto |
| 3 | 5 minutos |
| 4 | 30 minutos |
| 5 | 60 minutos |
| 6 | 2 horas |
| 7 a 14 | 10 horas cada uno |
Cada intento se firma por separado y lleva encabezados X-Timestamp, X-Nonce y X-Signature nuevos, véase más abajo Verificar firma.
Desactivación automática en caso de fallos persistentes
Además de los reintentos individuales, supervisamos continuamente la tasa de error diaria por Webhook. Un Webhook se marca como "problemático" en cuanto, en un mismo día,
- la tasa de error diaria está por encima del 80 % y
- se han producido al menos 10 errores ese día.
A partir de ese momento, el Webhook se observa durante varios días. Si la situación no mejora, el sistema escala paso a paso:
| Día | Umbral (tasa de error rolling) | Acción |
|---|---|---|
| 1 | por encima del 80 % | Correo de aviso nivel 1 |
| 3 | por encima del 85 % | Correo de aviso nivel 2 |
| 5 | por encima del 90 % | Correo de aviso nivel 3 |
| 7 | por encima del 95 % | El Webhook se desactiva automáticamente, entrada en el audit-log y correo de desactivación |
Los contadores diarios se cuentan a partir del día en que se detecta el problema.
Tras una desactivación automática
Un Webhook desactivado automáticamente permanece inactivo hasta que lo reactive manualmente en el Dashboard. Tras la reactivación, las estadísticas comienzan de cero.
¿Qué cuenta como "exitoso"?
Para la evaluación de la tasa de error, toda respuesta que no sea un disparador de reintento cuenta como "ok". En concreto, las respuestas 2xx, 3xx e incluso 4xx (excepto 429) se tratan como exitosas, ya que desde la perspectiva del sistema de envío representan una respuesta final y unívoca.
Esto significa: un endpoint que responde de forma persistente con 404 no se desactivará automáticamente, sino que debe ser eliminado o corregido por usted manualmente.
Seguridad
Verificar IP de origen
Todos los Webhooks provienen de nuestra IPv4 195.201.160.143 o de la IPv6 2a01:4f8:13a:8e7::2. Si esto cambia, encontrará todos los detalles adicionales en nuestro Changelog.
Verificar firma
Todos los Webhooks a su servidor se firman con la clave de firma de su cuenta. Con estos datos, puede validar la autenticidad de las solicitudes recibidas de nosotros y evitar duplicaciones o solicitudes fraudulentas. Verifique la firma como se describe en la página Firma.
Le enviamos los siguientes encabezados en cada Webhook:
- Name
X-Signature- Type
- string
- Description
La firma generada por nosotros
- Name
X-Timestamp- Type
- integer
- Description
Marca de tiempo en la que la firma fue creada por nosotros
- Name
X-Nonce- Type
- string
- Description
Cadena generada aleatoriamente con 32 caracteres
Cada intento de entrega se firma por separado. El envío inicial, cada reintento automático del plan de reintentos y un reenvío manual desde el depurador de Webhooks llevan su propio X-Timestamp, su propio X-Nonce y una X-Signature correspondiente. Un reintento que llega horas después del evento sigue llevando, por tanto, una marca de tiempo actual y un nonce que nunca se había utilizado antes.
Por ello, la ventana de 30 segundos para la marca de tiempo y la unicidad del nonce descritas en la página Firma también se aplican sin cambios a los reintentos. Mantenga su ventana de validación en 30 segundos y no la amplíe para los reintentos.
El campo webhook_timestamp del payload es el momento del evento, no el momento del intento de entrega. En un reintento difiere deliberadamente del encabezado X-Timestamp. Valide siempre el valor del encabezado, nunca el campo del payload.
Ejemplos como JSON Payload
SMS
SMS entrante (sms_mo)
{
"data": {
"id": "681590",
"sender": "SMS",
"system": "491771783130",
"text": "Hello World",
"time": "1605878104",
"message_type": "SMS"
},
"webhook_event": "sms_mo",
"webhook_timestamp": "2020-12-02 11:55:44"
}
Cambio de estado de SMS (dlr)
{
"data": {
"msg_id": "77149843739",
"status": "TRANSMITTED",
"timestamp": "2021-08-24 08:08:00.000000"
},
"webhook_event": "dlr",
"webhook_timestamp": "2021-08-24T08:08:00+02:00"
}
Seguimiento de Rendimiento (tracking)
{
"webhook_event": "tracking",
"webhook_timestamp": "2022-07-27T07:38:18+02:00",
"data": {
"sms_id": "77182424125",
"sms_label": null,
"tracking_url": "https://svn.me/7oz",
"final_url": "https://www.google.com",
"type": "click",
"total_clicks": 5,
"total_views": 3
}
}
Voz
Cambio de estado del mensaje de voz (voice_status)
{
"data": {
"callerId": "49176123456789",
"duration": "4",
"id": "284195",
"pricePerMinute": 0.075,
"recipient": "4943160049851",
"status": "completed",
"timestamp": 1629786769
},
"webhook_event": "voice_status",
"webhook_timestamp": "2021-08-24T08:32:50+02:00"
}
Llamada entrante (voice_call)
{
"webhook_event": "voice_call",
"webhook_timestamp": "2024-08-02T07:28:59+02:00",
"data": {
"id": 0,
"caller": "4943160049851",
"time": 1722576539,
"system": "4915170517246"
}
}
Señal DTMF (voice_dtmf)
{
"webhook_event": "voice_dtmf",
"webhook_timestamp": "2024-08-02T07:28:59+02:00",
"data": {
"id": 0,
"callerId": "4943160049851",
"recipient": "4943160049851",
"status": "completed",
"system": "4915170517246",
"timestamp": 1722576539,
"duration": 2.76,
"pricePerMinute": 0.045,
"dtmf_digit": 9,
"total_price": 0.045
}
}
RCS
Mensaje fue entregado
{
"webhook_event": "rcs",
"webhook_timestamp": "2024-03-08T10:01:07+01:00",
"data":
{
"msg_id": "77233699836",
"status": "DELIVERED",
"timestamp": "1709888466.254410",
"foreign_id": null,
"agent_id": "myfancyagent"
}
}
Mensaje fue leído
{
"webhook_event": "rcs",
"webhook_timestamp": "2024-03-08T10:01:09+01:00",
"data":
{
"msg_id": "77233699836",
"status": "READ",
"timestamp": "1709888468.065783",
"foreign_id": null,
"agent_id": "myfancyagent"
}
}
Informe de estado de un mensaje enviado
{
"webhook_event": "rcs",
"webhook_timestamp": "2024-03-08T10:00:18+01:00",
"data":
{
"sender": "4915153952979",
"status": "IS_TYPING",
"agent_id": "myfancyagent"
}
}
Se envió un mensaje de texto
{
"webhook_event": "rcs",
"webhook_timestamp": "2024-03-08T09:47:12+01:00",
"data": {
"id": 1871353,
"sender": "4915153952979",
"time": 1709870553,
"message_type": "RCS",
"content_type": "text",
"text": "Hallo",
"agent_id": "myfancyagent"
}
}
Se seleccionó una respuesta sugerida
{
"webhook_event": "rcs",
"webhook_timestamp": "2024-03-08T05:59:33+01:00",
"data": {
"id": 1870983,
"sender": "4915153952979",
"time": 1709870129,
"message_type": "RCS",
"content_type": "suggestion_response",
"suggestion_response": {
"postbackData": "suggestion_2",
"text": "Suggestion #2",
"type": "REPLY"
},
"agent_id": "myfancyagent"
}
}
Se envió una imagen
{
"webhook_event": "rcs",
"webhook_timestamp": "2024-03-08T08:58:20+01:00",
"data": {
"id": 1871195,
"sender": "4915153952979",
"time": 1709870556,
"message_type": "RCS",
"content_type": "user_file",
"user_file": {
"thumbnail": {
"mimeType": "image/jpeg",
"fileSizeBytes": 10166,
"fileUri": "https://static.seven.io/uploads/rbm/61513d3d/6176746a5177553d/4f36357145416a773452537737696e495a366a68454a42344639574937716e62704156765358664d486e42776730337334675a76522b6d4f574b6e32626632546842493d.jpeg"
},
"payload": {
"mimeType": "image/jpeg",
"fileSizeBytes": 611314,
"fileName": "IMG_20240308_050231_01.jpg",
"fileUri": "https://static.seven.io/uploads/rbm/61513d3d/6176746a5177553d/4f76746e45515768746866717653444e6166336a513852375249614d753637656f414a6f4758624d5358417269306e767331493645372b47446654325a66764b3168453d.jpeg"
}
},
"agent_id": "myfancyagent"
}
}
Se envió un archivo
{
"webhook_event": "rcs",
"webhook_timestamp": "2024-03-08T11:07:51+01:00",
"data": {
"id": 1871485,
"sender": "4915153952979",
"time": 1709892233,
"message_type": "RCS",
"content_type": "user_file",
"user_file": {
"payload": {
"mimeType": "application/pdf",
"fileSizeBytes": 18810,
"fileName": "sample (1).pdf",
"fileUri": "https://static.seven.io/uploads/rbm/61513d3d/617678715267303d/4f616c675256696b73524c6c75337a4b5a66336a51705570464e665a752f6d4e70464d37546e504b5458423730556a7337314e70514c3241436650304d616e4b3078493d.pdf"
}
},
"agent_id": "myfancyagent"
}
}
Se compartió una ubicación
{
"webhook_event": "rcs",
"webhook_timestamp": "2024-03-08T11:07:51+01:00",
"data": {
"id": 1871485,
"sender": "4915153952979",
"time": 1709892233,
"message_type": "RCS",
"content_type": "location",
"location": {
"latitude": 54.3216562,
"longitude": 10.1350767
},
"agent_id": "myfancyagent"
}
}
Los mensajes de WhatsApp entrantes se entregan con el evento wa_mo. El objeto data contiene siempre los mismos campos base, independientemente de lo que haya enviado el cliente:
- Name
id- Type
- integer
- Description
ID interno del mensaje entrante
- Name
sender- Type
- string
- Description
Número de teléfono del remitente en formato internacional sin el signo más inicial
- Name
time- Type
- integer
- Description
Marca de tiempo Unix de la recepción
- Name
text- Type
- string
- Description
Contenido del mensaje. Texto plano en mensajes de texto, una cadena codificada en JSON para todos los demás tipos de contenido
- Name
message_type- Type
- string
- Description
Siempre
WA
- Name
system- Type
- string
- Description
Su número de WhatsApp Business al que se envió el mensaje
Si define un filtro de eventos en el Webhook, este se compara con system. Así puede distribuir los mensajes por número de WhatsApp Business.
Mensaje de texto entrante (wa_mo)
{
"webhook_event": "wa_mo",
"webhook_timestamp": "2026-08-21T14:50:12+02:00",
"data": {
"id": 4521987,
"sender": "353861234567",
"time": 1755780612,
"text": "Hello World",
"message_type": "WA",
"system": "4915170517246"
}
}
Tipos de contenido distintos del texto plano
Archivos multimedia, ubicaciones, contactos, botones de respuesta rápida y respuestas interactivas se entregan en el mismo sobre. El campo text contiene entonces una cadena codificada en JSON cuya clave type indica qué se ha recibido:
type | Enviado por el cliente |
|---|---|
user_file | Imagen, vídeo, audio, documento o sticker |
location | Una ubicación compartida |
contact | Una tarjeta de contacto compartida |
button | Botón de respuesta rápida de un mensaje de plantilla |
interactive | Respuesta a un mensaje interactivo (botón o lista) |
Decodifique text con su analizador JSON antes de acceder a estas estructuras. Los mensajes de texto no están codificados en JSON, así que compruebe el primer carácter o simplemente use la cadena sin procesar si la decodificación falla.
Contenido decodificado del campo text:
Multimedia (user_file)
{
"type": "user_file",
"user_file": [
{
"type": "image",
"mediaId": "1234567890123456",
"mimeType": "image/jpeg",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"fileName": "IMG_20260821_145012.jpg",
"fileUri": "https://static.seven.io/uploads/whatsapp/61513d3d/6176746a/4f36357145416a773452537737/IMG_20260821_145012.jpg",
"caption": "Mira esto"
}
]
}
type se deriva del tipo MIME y es uno de image, video, audio, document o sticker. Si no podemos descargar el archivo de Meta, falta fileUri y el objeto contiene en su lugar "downloadFailed": true. En ese caso utilice mediaId para obtener el archivo usted mismo.
Ubicación
{
"type": "location",
"location": {
"latitude": 54.3216562,
"longitude": 10.1350767,
"name": "seven communications GmbH",
"address": "Alte Weide 7, 24116 Kiel"
}
}
Contacto
{
"type": "contact",
"contact": {
"formattedName": "John Doe",
"firstName": "John",
"lastName": "Doe",
"middleName": null,
"phones": [{"phone": "+353861234567", "type": "CELL"}],
"emails": [{"email": "john@example.com", "type": "WORK"}],
"addresses": [],
"company": "Example Ltd",
"birthday": "1985-04-12"
}
}
Botón de respuesta rápida
{
"type": "button",
"button": {
"text": "Confirmar cita",
"payload": "confirm_2026_08_21"
}
}
Respuesta interactiva
{
"type": "interactive",
"interactive": {
"item_id": "option_2",
"title": "Opción #2",
"description": "Entrega el viernes"
}
}
Las reacciones (respuestas con emoji a un mensaje) no se entregan mediante wa_mo. Se almacenan y se muestran en Conversations, pero no existe ningún evento de Webhook para ellas ni ajuste para activarlo. Lo mismo se aplica a los cambios de estado de los mensajes que usted envía: estos se entregan como eventos dlr, no como wa_mo.