Webhooks
TapToChat puede enviar una petición HTTPS a tu servidor cada vez que ocurre algo: una conversación nueva, un mensaje, una asignación. Cada petición va firmada para que sepas que viene de nosotros.
Configurar un webhook
Los webhooks forman parte del plan Pro. Los propietarios y administradores los gestionan en Ajustes.
- Abre Ajustes → Webhooks y elige Añadir webhook.
- Ponle un nombre e indica la URL https que recibirá los eventos.
- Elige los eventos que quieres enviar.
- Copia el secreto de firma. Solo se muestra una vez; puedes rotarlo más tarde.
Eventos
Cada webhook recibe solo los eventos a los que está suscrito.
| Evento | Se envía cuando |
|---|---|
conversation.created | Un visitante inicia una conversación, en cualquier canal. |
conversation.assigned | Se asigna una conversación a un agente. |
conversation.closed | Se cierra una conversación. |
message.created | Se guarda un mensaje: de un visitante, de un agente o una respuesta automática. |
tag.added | Se añade una etiqueta a una conversación. |
note.created | Un agente añade una nota interna o ejecuta un comando como /summary, cuyo resultado se guarda como nota (kind command_result). |
integration.connected | Se conecta una integración. |
integration.disconnected | Se desconecta una integración. |
integration.error | Falla la sincronización de una integración. |
webhook.test | Eliges Enviar evento de prueba en Ajustes. Nunca se envía en otro caso. |
Contenido
Cada petición es un POST con un cuerpo JSON en el mismo formato:
{
"id": "6f1c2b0e-8a7d-4c1e-9b3a-2d5e7f9a1c4b",
"event": "message.created",
"created_at": "2026-10-01T09:30:00.000Z",
"organization_id": "0c9a1f4e-3b2d-4e5f-8a6b-7c8d9e0f1a2b",
"data": {
"message": {
"id": "a1b2c3d4-…",
"conversation_id": "e5f6a7b8-…",
"sender_type": "visitor",
"sender_id": null,
"content": "Hello, is anyone there?",
"channel": "web",
"created_at": "2026-10-01T09:30:00.000Z"
},
"conversation": { "id": "e5f6a7b8-…", "status": "open", "visitor_name": "Ada", "…": "…" }
}
}id: el identificador del evento. Un reintento lleva el mismo id; úsalo para ignorar duplicados.event: el nombre del evento, como en la tabla anterior.data: lo que ha ocurrido. Sus campos dependen del evento; un evento de mensaje incluye el mensaje y su conversación.
Cabeceras
X-TapToChat-Signature: la firma: v1= seguido de un HMAC-SHA256 en hexadecimal.X-TapToChat-Timestamp: cuándo se firmó la petición, en segundos Unix.X-TapToChat-Event: el nombre del evento.X-TapToChat-Delivery-Id: esta entrega. Cada reintento lleva el mismo id.
Verificar la firma
Calcula un HMAC-SHA256 del timestamp, un punto y el cuerpo sin procesar de la petición, con tu secreto de firma como clave, y compáralo con la cabecera de firma en tiempo constante. Rechaza las peticiones cuyo timestamp se aleje más de 5 minutos de tu reloj, para que una petición capturada no pueda reenviarse.
Usa el secreto completo como clave, con su prefijo whsec_. Verifica el cuerpo sin procesar antes de analizarlo: un JSON vuelto a serializar no coincidirá.
import crypto from 'node:crypto'
// The raw request body, exactly as received: verify before parsing JSON.
export function verifyTapToChat(rawBody, headers, secret) {
const timestamp = headers['x-taptochat-timestamp']
const signature = headers['x-taptochat-signature']
if (!timestamp || !signature) return false
// Refuse old requests, so a captured one cannot be replayed later.
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false
const expected =
'v1=' + crypto.createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex')
const a = Buffer.from(signature)
const b = Buffer.from(expected)
return a.length === b.length && crypto.timingSafeEqual(a, b)
}Reintentos
Responde con cualquier estado 2xx en menos de 10 segundos para confirmar una entrega. Si no, se reintenta, hasta 6 intentos en total.
Las esperas entre intentos son 30s, 60s, 120s, 240s, 480s.
Una respuesta 4xx distinta de 408 o 429 detiene los reintentos de inmediato, igual que una redirección: las redirecciones no se siguen, así que indica la URL final.
En ocasiones una entrega puede llegar más de una vez. Usa el id del cuerpo para procesar cada evento una sola vez.
Desactivación automática
Si fallan 20 entregas seguidas, el webhook se desactiva y Ajustes muestra el motivo. Arregla el receptor y vuelve a activarlo; el contador empieza de nuevo.
A tener en cuenta
- La URL debe usar https y apuntar a una dirección pública.
- Los mensajes que los agentes envían desde la app móvil de TapToChat todavía no generan message.created. Los enviados desde el panel web y desde cada canal, sí.
¿Algo no funciona como se describe? Escríbenos.