Saltar al contenido

Webhooks ​

Los webhooks en Yarla son peticiones HTTP que enviamos a tu servidor en un momento fijo de la conversación. Los creas como una acción de Petición HTTP; este artículo explica lo que tienen en común los tres momentos: la firma, la política de reintentos y los tiempos de espera.

Tres momentos ​

MomentoQué ocurreArtículo
Antes de la llamadaEn cuanto entra una llamada telefónica, antes del saludo, enviamos a tu endpoint el número de quien llama, tu propio número y los datos de tu empresa. No ocurre en llamadas salientes, SMS, WhatsApp ni chat web. El asistente recibe como contexto el JSON que devuelves; usas los valores en el saludo como {{clave}} y en las acciones posteriores a la conversación como {{inboundContext.clave}}.Webhook entrante (antes de la llamada)
Durante la conversaciónSegún tu condición, el asistente decide llamar a tu endpoint (una «herramienta»), por ejemplo para consultar el estado de un pedido. Tu respuesta vuelve al asistente, que la usa en la conversación.Webhook durante la llamada
Después de la conversaciónCuando la conversación ha terminado y ya se han obtenido el resumen y las variables personalizadas, enviamos los datos de la conversación a tu endpoint.Webhook después de la llamada

Por defecto, los tres son peticiones POST con Content-Type: application/json. En la acción puedes cambiar el método, las cabeceras y el cuerpo. La firma siempre se incluye, salvo en GET: esa petición no lleva cuerpo ni firma.

¿Quieres un mensaje por cada conversación terminada, cada solicitud nueva y cada cita de toda tu cuenta, sin una acción por asistente? Usa los webhooks de eventos (Enterprise). Se firman de la misma manera.

Comprobar la firma ​

Toda petición que enviamos, salvo GET, lleva la cabecera:

text
X-Yarla-Signature: t=1757581245,v1=5f1c9b2a7d0e4c6b8a1f3e5d7c9b2a4f6e8d0c2b4a6f8e0d2c4b6a8f0e2d4c6b
  • t es la hora Unix, en segundos, en la que enviamos la petición.
  • v1 es HMAC-SHA256(secret, t + "." + rawBody), codificado en hexadecimal.

Encontrarás el secreto en Ajustes → Conexiones, en Secreto de webhook. Con Generar nuevo creas un secreto nuevo tras confirmarlo; el anterior deja de funcionar, así que actualiza tu servidor justo después. Solo los administradores de tu propia empresa ven y renuevan aquí el secreto. Un revendedor o un agente de soporte que esté viendo tu cuenta no lo ve.

Compruébala así:

  1. Divide la cabecera por la coma y lee t y v1.
  2. Rechaza la petición si la diferencia entre la hora actual y t es de más de 300 segundos (protección contra peticiones repetidas).
  3. Calcula el HMAC exactamente sobre el cuerpo sin procesar, es decir, los bytes tal como los recibes, no el JSON serializado de nuevo.
  4. Compara el resultado con v1 mediante una comparación en tiempo constante.

Ejemplo en Node.js con Express, para una acción posterior a la conversación:

js
const express = require('express');
const crypto = require('crypto');

const app = express();
const SECRET = process.env.YARLA_WEBHOOK_SECRET;
const TOLERANCE_SECONDS = 300;

function verifySignature(header, rawBody) {
  if (!header) return false;
  const parts = Object.fromEntries(header.split(',').map((kv) => kv.split('=')));
  const t = Number(parts.t);
  const v1 = parts.v1;
  if (!t || !v1) return false;
  if (Math.abs(Date.now() / 1000 - t) > TOLERANCE_SECONDS) return false;

  const expected = crypto
    .createHmac('sha256', SECRET)
    .update(`${t}.`)
    .update(rawBody) // Buffer con los bytes sin procesar
    .digest('hex');

  const a = Buffer.from(expected, 'hex');
  const b = Buffer.from(v1, 'hex');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// express.raw hace que req.body sea un Buffer y no JSON ya analizado
app.post('/yarla/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  if (!verifySignature(req.get('X-Yarla-Signature'), req.body)) {
    return res.status(401).send('firma no válida');
  }

  const event = JSON.parse(req.body.toString('utf8'));

  // Responde de inmediato y procesa después de forma asíncrona
  res.status(200).json({});
  setImmediate(() => handleEvent(event));
});

function handleEvent(event) {
  // Sin cuerpo propio, la petición incluye, entre otros, conversationId, summary y outcome
  if (event.outcome === 'callback_requested') {
    // por ejemplo: crear una tarea en tu CRM
  }
}

app.listen(3000);

Usa el cuerpo sin procesar

Si tu framework analiza el JSON antes de que compruebes la firma, calculas el HMAC sobre otra cadena y nunca coincidirá. En Express usa express.raw; en otros frameworks hay una opción equivalente para obtener los bytes originales.

Política de reintentos ​

No reintentamos automáticamente una petición fallida, y tampoco puedes volver a ejecutarla a mano. Una petición se considera fallida si devuelve un código de estado fuera de 2xx, si se agota el tiempo de espera o si no se puede acceder a tu endpoint. Seguimos las redirecciones y cuenta la última respuesta. Usa en la acción la dirección definitiva: una redirección con 301, 302 o 303 convierte tu petición en un GET sin cuerpo.

  • Después de la conversación: el resultado aparece en la conversación, en la Bandeja. Abre Detalles técnicos y mira en Qué hicieron las acciones: ahí está el código de estado y, si hay un código de error, el principio de la respuesta. Si se agota el tiempo de espera o no se puede acceder a tu endpoint, solo aparece el mensaje de error. Si una acción falla, las siguientes se ejecutan igualmente.
  • Antes de la llamada: el asistente empieza sin los datos de esa acción. Lo que tu endpoint sí devolvió está en Detalles técnicos, en Datos buscados antes de la llamada; una petición fallida no aparece ahí.
  • Durante la conversación: el resultado no se muestra en la conversación. Si recibe un código de error, el asistente sabe que la consulta ha fallado y lo resuelve en la conversación.

Por eso, diseña tu endpoint para las acciones posteriores a la conversación de esta forma: comprueba la firma, guarda la petición, responde de inmediato con 2xx y procesa los datos después de forma asíncrona. Si los procesas dentro de la propia petición, agotarás el tiempo de espera en cuanto tu CRM vaya algo lento. En las acciones antes de la llamada y durante la conversación, el asistente necesita tu respuesta: devuelve los datos enseguida, dentro del tiempo de espera indicado abajo.

Permitir direcciones IP ​

No hay una lista de direcciones IP fijas: nuestras direcciones de salida pueden cambiar. Protege tu endpoint con HTTPS y la firma. Si quieres una capa adicional, añade en la acción una cabecera propia con un token largo y aleatorio, y compruébala en tu servidor. En una petición GET, esa cabecera es la única comprobación, porque no se envía firma.

Tiempos de espera ​

MomentoTiempo de esperaSi se supera
Antes de la llamada3 segundosEl asistente empieza sin los datos de esta acción.
Durante la conversación6 segundosLa consulta falla y el asistente no recibe datos de tu endpoint. En una llamada telefónica, el asistente puede quedarse en silencio, así que responde con margen dentro de este tiempo.
Después de la conversación8 segundosLa acción se marca como fallida en la conversación.

Si sueles acercarte a estos límites, consulta Resolución de problemas: webhooks.

Artículos relacionados ​

Yarla Ayuda — la guía de tu asistente telefónico.