Saltar al contenido
Documentación

Enviar leads y traspasos a tus herramientas con webhooks

Envía leads nuevos, traspasos y peticiones de chat en vivo, formularios enviados, citas reservadas y peticiones de devolución de intoCHAT a tu servidor, Zapier, Make o n8n como JSON firmado: eventos, contenido, firmas y reintentos.

Un webhook envía un evento de tu agente a la dirección que elijas, como una petición JSON firmada por POST, en cuestión de segundos. Úsalo para llevar los leads nuevos a tu CRM o para iniciar una automatización. Funciona con tu propio servidor y con herramientas de automatización que aceptan webhooks entrantes, como Zapier, Make o n8n: usas el disparador de la herramienta para webhooks entrantes. Las apps propias de intoCHAT para Zapier y Make están construidas y estarán disponibles en cuanto se publiquen en los directorios de esas herramientas; consulta Zapier y Make. Para recibir estos eventos como mensajes en un canal de Slack no necesitas un webhook: usa los avisos en Slack.

Eventos

EventoNombre en el panelSe envía cuando
lead.createdNew leadse guarda un contacto nuevo, desde el formulario de leads o a partir de datos que el visitante escribió en el chat.
handoff.requestedHand-off requestedel agente envió una conversación a tu equipo por correo. Consulta Traspaso por correo.
form.submittedForm submittedun visitante envió uno de tus formularios de chat.
booking.createdMeeting bookedun visitante reservó una cita desde el chat. Consulta Reservas con Cal.com o Calendly.
live_chat.requestedLive chat requestedel agente pidió a tu equipo que se uniera al chat, porque un visitante pidió hablar con una persona mientras alguien de tu equipo estaba en línea. Consulta Chat en vivo.
return.requestedReturn requestedun visitante pidió devolver o cambiar artículos de un pedido que el agente verificó. Consulta Estado de pedidos y devoluciones.

lead.created se envía una vez por contacto. Un visitante que vuelve y envía los mismos datos no genera un evento nuevo, como tampoco un chat que añade un teléfono a un contacto ya guardado en ese chat.

handoff.requested solo se envía si el correo a tu equipo salió de verdad. Los traspasos desde el Playground también lo envían, con "test": true. Si conectaste los tickets de helpdesk, se envía cuando el ticket se ha creado o ha fallado, normalmente unos segundos después, y lleva el ticket cuando lo hay.

form.submitted se envía una vez por cada envío. Un formulario solo se puede enviar una vez por conversación, y un envío nunca genera lead.created, aunque el formulario pida una dirección de correo.

booking.created se envía una vez por reserva, después de que Cal.com o Calendly la haya aceptado y de que intoCHAT la haya guardado. Una reserva nunca genera lead.created. Las reservas desde el Playground son reservas reales y también lo envían, sin marca de prueba. Los cambios que se hagan después en Cal.com o Calendly, como una cancelación, no envían ningún evento.

live_chat.requested se envía una vez por petición, cuando el agente pide a tu equipo que se una, tanto si alguien se une como si no. Un visitante que vuelve a pedirlo después de que la petición caducara o de que terminara el chat en vivo genera una nueva. El Playground nunca pide el chat en vivo, así que nunca envía este evento.

return.requested se envía una vez por petición de devolución, cuando intoCHAT la ha guardado. Cada conversación puede enviar una petición por pedido, así que volver a pedirlo para el mismo pedido no envía nada. Las peticiones desde el Playground también lo envían, sin marca de prueba.

Añadir un webhook

  1. Abre tu agente y ve a la pestaña Leads. Webhooks está hacia el final de la pestaña, encima de Slack alerts.
  2. Haz clic en Add webhook.
  3. En Endpoint URL, pega la dirección que te da tu servidor o tu herramienta para webhooks entrantes.
  4. En Events to send, deja marcados los eventos que quieras: New lead, Hand-off requested, Form submitted, Meeting booked, Live chat requested y Return requested. En un webhook nuevo están marcados los seis. Un webhook que añadiste antes conserva sus eventos: haz clic en Edit y marca Form submitted, Meeting booked, Live chat requested o Return requested si debe recibirlos.
  5. Deja Active activado y haz clic en Add webhook.
  6. Copia el secreto de firma que aparece, guárdalo donde tu receptor pueda leerlo y haz clic en I've saved it.
  7. Haz clic en Send test event para comprobar la conexión. El resultado aparece debajo de los botones.

La dirección tiene que empezar por https:// y apuntar a un servidor público. Las direcciones de redes privadas o internas, como localhost o 192.168.1.10, se rechazan al guardar y se comprueban de nuevo antes de cada envío. Cada agente puede tener hasta 5 webhooks.

Los webhooks que Zapier, Make o tu propio código suscribieron a través de la API REST no cuentan para esos 5; consulta Webhooks creados por integraciones.

Para pausar un webhook sin perder su configuración, desactívalo: mostrará Paused. Los eventos que ocurren durante la pausa no se envían, ni siquiera después. Un envío que espera un reintento falla en su siguiente intento; cuando el webhook vuelva a estar activo, puedes reenviarlo. Edit cambia la dirección y los eventos. Remove elimina el webhook y su historial de envíos.

Webhooks creados por integraciones

Una integración puede suscribir un webhook a tu agente a través de la API REST, con una de tus claves de API. Las apps de Zapier y Make lo hacen cuando activas un Zap o añades un disparador instantáneo a un escenario.

Estos webhooks aparecen en la tarjeta Webhooks junto a los demás, con una etiqueta que indica de dónde vienen: via Zapier, via Make o via API. Se envían, se firman, se reintentan y se registran como los webhooks que añades tú, y puedes usar en ellos Send test event, Recent deliveries, el interruptor Active y New secret.

  • No tienen botón Edit: el Zap, el escenario o el código que los creó es el dueño de la dirección y de los eventos, y cambiarlos aquí lo rompería.
  • Remove elimina uno. El Zap o el escenario sigue activo, pero no recibe más eventos; desactívalo también allí, o desactívalo y vuelve a activarlo para suscribir un webhook nuevo.
  • La integración elimina su webhook por sí misma cuando desactivas el Zap o eliminas el disparador.
  • Revocar la clave de API que los creó los elimina. Consulta Revocar una clave.
  • Un agente puede tener hasta 25, aparte de los 5 que añades tú.

El secreto de firma

Cada webhook tiene su propio secreto de firma, que empieza por whsec_. Solo se muestra completo una vez, justo después de añadir el webhook o de crear un secreto nuevo. Después, la tarjeta solo muestra sus cuatro últimos caracteres.

Si pierdes el secreto o crees que alguien más lo conoce, haz clic en New secret. El secreto anterior deja de funcionar al momento, así que pasa el nuevo a tu receptor enseguida.

Qué se envía

Cada envío es una petición POST con un cuerpo JSON y estas cabeceras:

CabeceraValor
Content-Typeapplication/json
User-AgentintoCHAT-Webhooks/1.0
X-IntoChat-EventEl nombre del evento, por ejemplo lead.created
X-IntoChat-DeliveryEl ID del envío. Es el mismo en cada reintento.
X-IntoChat-TimestampCuándo se envió este intento, en segundos Unix
X-IntoChat-Signaturesha256= seguido de la firma; ver más abajo

El cuerpo siempre tiene el mismo envoltorio. id es el ID del envío, el mismo valor que X-IntoChat-Delivery:

{
  "id": "DELIVERY_ID",
  "event": "lead.created",
  "created_at": "2026-10-07T09:30:00.000Z",
  "agent": { "id": "AGENT_ID", "name": "Acme Assistant" },
  "data": { }
}

Datos de lead.created

CampoContenido
leadIdEl ID del lead
agentId, agentNameEl agente que captó el lead
emailEl correo electrónico, o null
phoneEl teléfono, o null
nameEl nombre del formulario, o null. Nunca se toma un nombre de un mensaje del chat.
customFieldsLas respuestas a tus campos propios, como lista de { "id", "label", "value" }. Vacía si no hay ninguna.
sourceform o chat
conversationIdLa conversación de la que viene el lead, o null
collectedAtCuándo se guardó el lead, como hora ISO 8601 en UTC
identitySolo para un visitante verificado con la verificación de identidad: { "userId", "name", "email" }. Si no, no aparece.

Datos de handoff.requested

CampoContenido
conversationIdLa conversación traspasada
visitorEmailLa dirección que dio el visitante para la respuesta
summaryLo que necesita el visitante, con las palabras del agente
pageLa página donde empezó el chat, o null
countryEl código de país del visitante, de dos letras, o null
requestedAtCuándo se envió el traspaso, como hora ISO 8601 en UTC
testtrue en un traspaso desde el Playground; si no, false
ticketSolo cuando los tickets de helpdesk abrieron un ticket para este traspaso: { "provider", "id", "url" }, donde provider es zendesk, freshdesk o hubspot, id es el número del ticket como cadena de texto y url lo abre en tu helpdesk. Si no, no aparece.

Datos de form.submitted

CampoContenido
submissionIdEl ID del envío
formId, formNameEl formulario enviado
agentId, agentNameEl agente que mostró el formulario
answersLas respuestas del visitante, como lista de { "id", "label", "value" }, en el orden del formulario. Los campos opcionales que quedaron vacíos no aparecen.
conversationIdLa conversación en la que se envió el formulario
submittedAtCuándo se guardó el envío, como hora ISO 8601 en UTC
identitySolo para un visitante verificado con la verificación de identidad: { "userId", "name", "email" }. Si no, no aparece.

Datos de booking.created

CampoContenido
bookingIdEl ID de la reserva en intoCHAT
providerEl calendario: calcom o calendly
providerBookingIdEl ID de la reserva en Cal.com, o la URI del invitado en Calendly, por ejemplo https://api.calendly.com/scheduled_events/…/invitees/…
agentId, agentNameEl agente con el que se reservó la cita
eventTypeId, eventTypeUri, eventTitleEl tipo de evento reservado: eventTypeId es el número de Cal.com y eventTypeUri es la URI de Calendly. El otro es null.
startAt, endAtCuándo empieza y termina la cita, como horas ISO 8601 en UTC
timeZoneLa zona horaria del visitante, por ejemplo Europe/Madrid, o UTC si su navegador no indicó ninguna
attendeeName, attendeeEmailEl nombre y el correo que introdujo el visitante
providerStatusaccepted, o pending si primero tienes que confirmar la reserva en Cal.com. Las reservas de Calendly siempre son accepted.
conversationIdLa conversación en la que se reservó la cita
createdAtCuándo se guardó la reserva, como hora ISO 8601 en UTC
identitySolo para un visitante verificado con la verificación de identidad: { "userId", "name", "email" }. Si no, no aparece.

Datos de live_chat.requested

CampoContenido
conversationIdLa conversación en la que el visitante pidió hablar con una persona
visitorMessageEl último mensaje del visitante, recortado a 500 caracteres con … al final si es más largo
pageLa página donde empezó el chat, o null
countryEl código de país del visitante, de dos letras, o null
requestedAtCuándo pidió el agente a tu equipo que se uniera, como hora ISO 8601 en UTC

El evento no indica si alguien se unió. Quién tomó el control del chat, y cuándo, se ve en la bandeja de Live chat y en la transcripción.

Datos de return.requested

CampoContenido
returnRequestIdEl ID de la petición en intoCHAT
providerLa tienda: shopify o woocommerce
orderName, orderIdEl pedido tal como lo muestra tu tienda, por ejemplo #1042, y su ID en la tienda
emailLa dirección de correo del pedido. El visitante demostró que la conoce, o la pasó la verificación de identidad de tu web.
kindreturn o exchange
itemsLos artículos, cada uno como { "title", "quantity" }. El título incluye la variante cuando el producto se pidió en varias, por ejemplo Wool scarf - Blue.
reasonEl motivo, con las palabras del visitante
statusrequested
agentId, agentNameEl agente con el que se hizo la petición
conversationIdLa conversación en la que se hizo la petición
createdAtCuándo se guardó la petición, como hora ISO 8601 en UTC

No se ha aprobado ni cambiado nada en tu tienda. Gestiona la devolución allí y después márcala como resuelta en la lista Returns de la pestaña Leads.

Los nombres de campo de todos los eventos usan camelCase.

Evento de prueba

Send test event envía una petición con "event": "test" y estos datos: { "message": "This is a test event from intoCHAT. Your webhook is set up correctly." }. Se envía sean cuales sean los eventos elegidos, también con el webhook en pausa, y solo se intenta una vez. Como sus datos son distintos de los de un evento real, una herramienta que asigna campos a partir de un ejemplo necesita un evento real, por ejemplo un lead que envíes tú mismo desde tu web.

Comprobar la firma

Comprueba cada petición antes de fiarte de ella. X-IntoChat-Signature es sha256= seguido del HMAC-SHA256 en hexadecimal de la marca de tiempo, un punto y el cuerpo sin procesar, es decir ${timestamp}.${rawBody}, con el secreto de firma del webhook como clave. La marca de tiempo es el valor de X-IntoChat-Timestamp.

  • Calcula la firma sobre el cuerpo exactamente como llega, antes de interpretar el JSON.
  • Compara en tiempo constante.
  • Rechaza las marcas de tiempo antiguas, para que una petición interceptada no se pueda repetir. El ejemplo de abajo admite 5 minutos.

Este ejemplo de Node.js es el mismo que aparece en Verify signatures en la tarjeta Webhooks, con un botón Copy:

const crypto = require("crypto")

// rawBody: the request body exactly as received, before JSON parsing.
function verifyIntoChatWebhook(rawBody, headers, secret) {
  const timestamp = headers["x-intochat-timestamp"]
  const signature = headers["x-intochat-signature"] || ""

  // Reject anything older than 5 minutes, so a captured request can't be replayed.
  const age = Math.abs(Date.now() / 1000 - Number(timestamp))
  if (!timestamp || !(age <= 300)) return false

  const expected = "sha256=" + 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)
}

Si tu herramienta no puede comprobar firmas, trata la dirección del webhook como una contraseña: cualquiera que la conozca puede enviarle peticiones.

Responder y evitar duplicados

  • Responde con cualquier estado 2xx en menos de 10 segundos. Todo lo demás cuenta como fallo: otro código de estado, una respuesta que no llega a tiempo o un error de conexión o de certificado.
  • Las redirecciones no se siguen. Una respuesta 3xx cuenta como fallo, así que indica la dirección final.
  • El mismo evento puede llegar más de una vez, por ejemplo si tu servidor lo guardó pero respondió demasiado tarde. Usa X-IntoChat-Delivery, o el id del cuerpo, para ignorar un envío que ya procesaste.
  • Para un mismo evento, cada webhook recibe su propio envío, con su propio ID.

Reintentos

  • El primer intento sale justo después del evento. Si falla, siguen hasta dos reintentos rápidos en pocos segundos.
  • Si también fallan, el envío muestra Retrying y una tarea programada lo vuelve a intentar más tarde. Los reintentos terminan tras 6 intentos en total o 24 horas después del evento, lo que ocurra antes. Después, el envío queda marcado como Failed.
  • La tarea programada se ejecuta ahora una vez al día. En la práctica, un envío que sigue fallando tras los reintentos rápidos tiene uno o dos intentos más, dentro de aproximadamente un día desde el evento.
  • Un envío a una dirección que resulta apuntar a una red privada falla en el acto, sin reintentos.
  • Cada reintento conserva el ID del envío y se vuelve a firmar con una marca de tiempo nueva.

Envíos recientes

Haz clic en Recent deliveries debajo de un webhook para ver sus 20 últimos envíos. Cada uno muestra su estado (Sending, Retrying, Delivered o Failed), el evento, la hora, el número de intentos y el estado HTTP. Un envío pendiente de reintento muestra cuándo toca el siguiente, y uno que no tuvo éxito muestra el error, por ejemplo No response within 10 seconds.

Un envío fallido tiene un botón Resend que lo intenta una vez más, al momento. Los envíos se eliminan a los 30 días.

Siguientes pasos

Ver como Markdown