Skip to main content

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.

  1. Abre Ajustes → Webhooks y elige Añadir webhook.
  2. Ponle un nombre e indica la URL https que recibirá los eventos.
  3. Elige los eventos que quieres enviar.
  4. Copia el secreto de firma. Solo se muestra una vez; puedes rotarlo más tarde.
Abrir Ajustes → Webhooks

Eventos

Cada webhook recibe solo los eventos a los que está suscrito.

EventoSe envía cuando
conversation.createdUn visitante inicia una conversación, en cualquier canal.
conversation.assignedSe asigna una conversación a un agente.
conversation.closedSe cierra una conversación.
message.createdSe guarda un mensaje: de un visitante, de un agente o una respuesta automática.
tag.addedSe añade una etiqueta a una conversación.
note.createdUn agente añade una nota interna o ejecuta un comando como /summary, cuyo resultado se guarda como nota (kind command_result).
integration.connectedSe conecta una integración.
integration.disconnectedSe desconecta una integración.
integration.errorFalla la sincronización de una integración.
webhook.testEliges 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.