Skip to main content
Los webhooks permiten recibir notificaciones HTTP en tiempo real cuando ocurren eventos en tu workspace. Solo se entregan eventos para agentes y suscripciones activas.
Requiere permisos webhooks:read para consultas y webhooks:write para crear, actualizar o eliminar. Todos los endpoints requieren el header X-Workspace-Id.

Eventos disponibles

Los webhooks pueden suscribirse a los siguientes eventos. La lista al día siempre se puede consultar en el catálogo, con GET /v1/catalogs/items?filter=eq(type,%22webhook_event%22).
El catálogo acepta suscripciones a trece eventos, pero hoy la plataforma sólo emite seis: call.callStarted, call.callEnded, call.callAnalyzed, call.transferBridged, whatsapp.messageReceived y whatsapp.messageSent. Los demás se crean con 201 y quedan activos, pero ninguna ruta de código los dispara todavía: si montas tu integración sobre ellos, no vas a recibir nada. Están marcados abajo como sin emisor.

Eventos de Tools

  • tool.invoked (sin emisor todavía): Se ejecuta cuando un agente termina de invocar una herramienta y recibe una respuesta.

Eventos de llamadas (Calls)

  • call.callStarted: Una llamada de voz comenzó para uno de los agentes del workspace.
  • call.callEnded: Una llamada de voz finalizó (incluye URLs de grabación y transcripción).
  • call.callMissed (sin emisor todavía): Una llamada no fue contestada antes del timeout.
  • call.callAnalyzed: El análisis posterior a la llamada ya está disponible.
  • call.transferBridged: Una transferencia se conectó con el destino.
  • call.voicemailReceived (sin emisor todavía): Un interlocutor dejó un mensaje de buzón de voz.

Eventos de WhatsApp

  • whatsapp.messageReceived: Un usuario de WhatsApp envió un mensaje entrante al workspace.
  • whatsapp.messageSent: El workspace envió un mensaje saliente de WhatsApp.
  • whatsapp.messageDelivered (sin emisor todavía): Un mensaje saliente de WhatsApp llegó al dispositivo del destinatario.
  • whatsapp.messageFailed (sin emisor todavía): Un mensaje saliente de WhatsApp falló al entregarse.
  • whatsapp.conversationStarted (sin emisor todavía): La plataforma abrió una nueva sesión de conversación de WhatsApp.
  • whatsapp.conversationEnded (sin emisor todavía): Una sesión de conversación de WhatsApp existente se cerró.

Seguridad

Todas las entregas de webhook incluyen:
  • HTTPS requerido: Solo se aceptan URLs HTTPS.
  • Firma HMAC-SHA256: Cada request incluye el header x-signature con la firma del payload.
  • Timestamp: Header x-timestamp con marca temporal ISO-8601.
  • Secreto: El secreto se genera automáticamente (mínimo 16 caracteres) o puedes proveer el tuyo.

Verificación de firmas

Para validar que el webhook proviene de Agents Studio, verifica la firma HMAC-SHA256. Con method: 'POST' —el valor por defecto— se firma {timestamp}.{cuerpo crudo}:

Webhooks con method: 'GET'

Un webhook creado con method: ‘GET’ no recibe cuerpo: el evento viaja aplanado como query string —cada campo del payload es un parámetro, y los valores que no son string ni número van serializados como JSON— y la firma se calcula sobre {timestamp}.{URL completa, con su query string}, no sobre el cuerpo. Verificar una entrega GET con la receta de arriba falla siempre.

Gestión de webhooks

Crear webhook

Listar webhooks

Los tres listados de esta sección —webhooks, suscripciones y deliveries— sólo aceptan page, limit y filter. Mandar sort, fields, include o q devuelve 400 (property sort should not exist), también si los pasas como opciones del SDK. El orden es fijo: del más reciente al más antiguo.

Obtener webhook

Deliveries (historial de entregas)

Los deliveries representan cada intento de entrega de un evento a tu endpoint (incluye status, intentos, error y response status).

Listar deliveries de un webhook

Obtener un delivery por ID

Actualizar webhook

Eliminar webhook

Gestión de suscripciones

Crear suscripción a evento

Listar suscripciones

Obtener suscripción

Actualizar suscripción

Eliminar suscripción

Estructura de payloads

Evento: call.callEnded

Se dispara cuando una llamada de voz finaliza. Incluye información del objetivo alcanzado, URLs de grabación y transcripción.

Valores de endReason y sipCode

sipCode es lo que te dice si la llamada se estableció — que alguien o algo descolgó, no necesariamente una persona. Los códigos de la familia 2xx significan que se estableció. El 183 depende de si tu workspace tiene habilitados los valores marcados con †: sin ellos, 183 es un buzón de voz (se estableció); con ellos, 183 es «sonó y nadie contestó» (no se estableció). La columna «¿contestó una persona?» suele ser la que decide si hay que volver a llamar. Estos tres campos (sipCode, endReason, durationMs) viajan igual en call.callEnded y en call.callAnalyzed, que son dos eventos de la misma llamada: si tu integración actúa sobre ellos, desduplica por callId.
† Estos valores se habilitan por workspace. Mientras no estén habilitados en el tuyo, esas llamadas llegan con endReason y sipCode en null en lugar del valor de la tabla — no con otro código, salvo voicemail_reached y dial_no_answer, que llegan con el código entre paréntesis fuera de él (183 y 180). Escríbenos si quieres que los habilitemos en tu workspace.
Con los valores † habilitados, el sipCode ya no separa a una persona de una máquina: el buzón, el IVR y una persona que contestó llegan los tres con 200. Si tu lógica de re-marcado decide sólo por el sipCode, va a dar por atendido un buzón. Para distinguirlos, usa endReason.
sipCode puede venir en null. Significa que la llamada terminó con un motivo que todavía no traducimos a código SIP, no que no se haya establecido. Un sipCode vacío por sí solo no autoriza a volver a llamar: si durationMs es mayor que cero, la llamada duró y lo más probable es que se estableciera.Y ojo con los dos bordes, porque son justo los que llevan a re-marcar a quien no toca: durationMs puede llegar en 0 o en null aunque la llamada sí se estableciera —es el caso de call.transferBridged, que se emite mientras la llamada sigue abierta con un agente humano—, y que se estableciera no significa que contestara una persona: un buzón de voz también dura.

Evento: call.callStarted

Se dispara cuando una llamada de voz comienza.
Una misma llamada dispara dos entregas de call.callStarted, con formas distintas y su propio timestamp: la de notificación (datos de la llamada) y la de tracking (latencia y ejecución). Tu handler tiene que tolerar las dos y no puede asumir que los campos de una estén en la otra.
Entrega de notificación — direction, fromNumber y toNumber van en la raíz del payload, no bajo metadata:
Entrega de tracking — sin toNumber ni direction, con la latencia y los identificadores de ejecución:
isTest viaja en las dos entregas y es lo único que distingue una llamada de prueba de una real: úsalo si no quieres que las pruebas de integración entren a tu CRM.

Evento: tool.invoked

Este evento no se emite todavía: la clave existe en el catálogo y la suscripción se crea, pero ninguna ruta de código lo dispara. El payload de abajo es la forma prevista, no algo que vayas a recibir hoy.
Se dispararía cuando un agente termina de ejecutar una herramienta.

Evento: whatsapp.messageReceived

Se dispara cuando se recibe un mensaje entrante de WhatsApp. El payload es plano: el teléfono del cliente es customerPhone (no from), el contenido es text (no message.text.body) y conversationId va en la raíz, no bajo metadata.
buttonId llega con valor cuando el cliente respondió tocando un botón, y es null en un mensaje de texto normal. contactFlowId identifica el ciclo de contacto activo de ese número, o es null si no hay ninguno.

Mejores prácticas

1. Procesar eventos de forma idempotente

Los webhooks pueden ser reenviados en caso de fallos. Asegúrate de que tu endpoint pueda procesar el mismo evento múltiples veces sin efectos secundarios:
Esta clave deduplica los reintentos de una misma entrega, que repiten el timestamp. No colapsa las dos entregas de call.callStarted —cada una sella el suyo— y no debería: llevan datos distintos y las dos te interesan.

2. Responder rápidamente

Responde con 2xx lo antes posible y procesa el evento de forma asíncrona:

3. Reintentos automáticos

El sistema reintenta entregas fallidas hasta 5 veces con backoff exponencial:
  • Intento 1: inmediato
  • Intento 2: ~2 segundos después
  • Intento 3: ~4 segundos después
  • Intento 4: ~8 segundos después
  • Intento 5: ~16 segundos después
Solo respuestas con código 2xx se consideran exitosas.

4. Monitorear entregas

Usa los campos successCount, failureCount y lastDeliveryAt del webhook para monitorear la salud:

5. Filtrar por agente

Si solo necesitas eventos de un agente específico, configura agentId al crear el webhook: