AtendyDocumentación Ir al panel

Webhooks

Los dos webhooks del agente en Atendy: el de entrada (antes de la llamada) y el de fin de llamada, con la estructura del payload y buenas prácticas.

Webhooks (API)

Un webhook es una URL tuya (o de una herramienta como n8n, Make o tu propio servidor) a la que Atendy envía datos de forma automática en un momento concreto de la llamada. Cada agente puede tener dos webhooks, y sirven para cosas distintas: uno se dispara antes de que empiece la conversación y el otro cuando la llamada termina.

Webhook de entrada
Se dispara justo ANTES de que el agente empiece a hablar. Atendy te avisa de quién llama y tú puedes devolver datos de esa persona (nombre, plan, última cita...) para que el agente los use durante la llamada.
Webhook de fin de llamada
Se dispara cuando la llamada TERMINA y se ha analizado. Atendy te envía la transcripción, el resumen y los KPIs, con el mismo formato que el evento call_analyzed de Retell. Ideal para guardar el resultado en tu CRM, hoja de cálculo o base de datos.

Ambos se configuran dentro del agente, en su asistente de herramientas o ajustes del agente, pegando la URL correspondiente. Funcionan tanto en llamadas de teléfono como en las pruebas desde el navegador.

Webhook de entrada (antes de la llamada)

En cuanto entra una llamada (o empiezas una prueba), Atendy hace una petición a tu URL de entrada con los datos que ya conoce, sobre todo el número de teléfono de quien llama. Tu servidor puede usar ese número para buscar a la persona en tu sistema y responder con datos suyos. Todo lo que devuelvas queda disponible como variables dentro del prompt y del saludo.

  1. Prepara una URL en tu servidor o herramienta que reciba una petición POST con JSON.
  2. En el agente, pega esa URL en el campo del webhook de entrada.
  3. Cuando llegue una llamada, Atendy enviará a esa URL el número de origen y el identificador del agente.
  4. Tu servidor responde con un JSON que contenga las variables que quieras usar (por ejemplo el nombre del cliente).
  5. El agente arranca la conversación ya con esos datos cargados.

Esto es un ejemplo de lo que Atendy envía a tu webhook de entrada:

{
  "agent_id": "a1b2c3d4-0000-0000-0000-000000000000",
  "caller_number": "+34600123456",
  "room_name": "call-_+34600123456_a1B2c3"
}
El webhook de entrada solo se dispara en llamadas ENTRANTES. En una llamada saliente lanzada por API, los datos del cliente se los pasas tú directamente en el campo vars de la petición.

Y esto es lo que tu servidor debe responder: un JSON con las variables que quieras poner a disposición del agente. Los nombres de las claves son los que luego usarás entre dobles llaves.

{
  "nombre": "Ana García",
  "plan": "Premium",
  "ultima_cita": "2026-07-02"
}
El número de quien llama siempre está disponible como {{numero_origen}}, aunque no configures ningún webhook de entrada. Úsalo, por ejemplo, para que el agente lo confirme o para identificar al cliente.

Usar las variables en el prompt y el saludo

Cada variable que devuelva el webhook de entrada se escribe entre dobles llaves y Atendy la sustituye por su valor real justo antes de hablar. Puedes usarlas tanto en el prompt del sistema como en el saludo inicial.

Saludo inicial:
Hola {{nombre}}, gracias por llamar. Veo que tienes el plan {{plan}}.

Prompt del sistema:
Atiendes a {{nombre}}, cliente con plan {{plan}}.
Su última cita fue el {{ultima_cita}}. Ayúdale con lo que necesite
sobre su cuenta y confirma sus datos si hace falta.
Si una variable no llega (por ejemplo, porque el webhook no encontró a esa persona), Atendy elimina la etiqueta {{...}} en lugar de leerla en voz alta. Aun así, redacta el saludo para que suene natural con o sin el dato: usa una versión genérica cuando no haya nombre.

Webhook de fin de llamada

Cuando la llamada termina y Atendy ha analizado la conversación, envía a tu URL de fin de llamada un único mensaje con lo ocurrido: la transcripción completa, un resumen, los KPIs que hayas definido y los datos de la llamada. Es el momento ideal para volcar el resultado en tu CRM, avisar a tu equipo o lanzar un flujo en n8n.

El payload usa la misma estructura que el evento call_analyzed de Retell, para que los flujos ya construidos para ese formato funcionen sin cambios. Todo cuelga de un objeto call:

{
  "event": "call_analyzed",
  "call": {
    "call_id": "call-out-4f3a9c2b1d7e",
    "agent_id": "a1b2c3d4-0000-0000-0000-000000000000",
    "agent_name": "Recepción",
    "call_status": "ended",
    "start_timestamp": 1786303502000,
    "end_timestamp": 1786303645000,
    "duration_ms": 143000,
    "from_number": "+34911234567",
    "to_number": "+34600123456",
    "direction": "outbound",
    "disconnection_reason": "agent_hangup",
    "transcript": "Agent: Hola Ana, le llamo por su cita.\nUser: Sí, quería cambiarla.",
    "transcript_object": [
      { "role": "agent", "content": "Hola Ana, le llamo por su cita." },
      { "role": "user", "content": "Sí, quería cambiarla." }
    ],
    "call_analysis": {
      "call_summary": "La clienta pidió cambiar su cita del jueves al viernes por la tarde. Se reprogramó y se le confirmó.",
      "custom_analysis_data": {
        "cita_reprogramada": true,
        "motivo": "cambio de fecha"
      }
    }
  }
}

Campos que recibes, todos dentro de call:

CampoQué contiene
eventSiempre call_analyzed. Va en la raíz, no dentro de call.
call_idIdentificador de la llamada. En una llamada lanzada por API es exactamente el call_id que te devolvió el POST, así que es la forma de casar tu petición con su resultado.
agent_id / agent_nameEl agente que atendió la llamada.
call_statusSiempre ended: este aviso solo se envía con la llamada terminada.
directioninbound en llamadas entrantes, outbound en salientes y web en las pruebas desde el navegador.
from_number / to_numberNúmeros de la llamada según su dirección. En una saliente, from_number es tu número y to_number el de la persona a la que llamaste.
start_timestamp / end_timestampMomentos de inicio y fin en milisegundos desde el 1 de enero de 1970 (epoch), no en texto con fecha. Valen 0 si el inicio no se pudo determinar.
duration_msDuración en MILISEGUNDOS. Ojo si vienes de una integración que esperaba segundos.
disconnection_reasonCómo terminó: agent_hangup, user_hangup, call_transfer, max_duration_reached o error_llm_websocket_runtime. Este último significa que la cortó el sistema porque el agente dejó de responder o falló el modelo de voz, no que colgara la persona. En la llamada se guarda como terminada por el sistema (ended_by=system): en el Resumen cuenta como Sistema y en el registro de herramientas del Historial, fin_de_llamada dice «cortada por el sistema».
transcriptLa conversación como texto plano, con cada turno en una línea con el prefijo Agent: o User:.
transcript_objectLa misma conversación turno a turno, con role (agent o user) y content. Fíjate en que el texto va en content, no en text.
call_analysis.call_summaryEl resumen de la llamada.
call_analysis.custom_analysis_dataLos datos clave que le pediste extraer al agente (por ejemplo, si se cerró una cita).
crm_clientExtensión de Atendy (no existe en Retell): la ficha del CRM de la persona que llamó o a la que llamaste, con name y client_ref (su número de cliente), para volcar la llamada en la fila correcta sin cruzar teléfonos. Es null si tu cuenta no usa el CRM o esa persona aún no tiene ficha (la ficha de un desconocido se crea justo después de este aviso, así que en su primera llamada llega null).
metadataEl objeto metadata que enviaste al lanzar la llamada por la API (POST /api/v1/calls), devuelto TAL CUAL: sirve para casar la llamada con el registro de tu sistema sin guardar el call_id. Es {} si no enviaste nada (y en llamadas entrantes, siempre).
answered_at / connected_secondsExtensión de Atendy: momento (epoch en milisegundos) en que la otra parte DESCOLGÓ y segundos de conversación desde el descuelgue, sin contar el timbrado. En una saliente, answered_at null es la señal fiable de que nadie contestó.
amd_class / machine_detectedExtensión de Atendy, solo salientes: si el agente detectó un contestador o buzón de voz, machine_detected llega true y amd_class dice la clase (machine_vm, machine_ivr o machine_unavailable). En una conversación con una persona, false y null.
El sentimiento, el indicador de transferencia y el enlace a la grabación NO viajan en este payload: se guardan en la llamada y los ves en el Historial del panel. Si necesitas el sentimiento en tu sistema, puedes consultarlo con GET /api/v1/calls/{call_id} una vez el análisis esté listo.
El aviso se envía cuando el análisis de la llamada está terminado, alrededor de un minuto después de colgar. Si tu flujo necesita reaccionar en el instante en que la llamada acaba, no esperes a este webhook.
Las pruebas desde el navegador con Probar agente también disparan los dos webhooks y llegan con la marca de canal web. El widget público embebido (anónimo) no dispara webhooks, pero sí guarda la conversación en tu Historial (canal widget).

Buenas prácticas

El webhook de entrada tiene un límite de espera de unos 8 segundos. Si tu servidor tarda más en responder, Atendy continúa la llamada sin esas variables para no dejar al cliente esperando. Responde siempre lo más rápido posible.
Responde rápido
En el webhook de entrada, devuelve solo los datos imprescindibles y hazlo en menos de 8 segundos. Evita consultas lentas o cadenas de llamadas a otros sistemas antes de responder.
Devuelve un JSON válido
Responde siempre con JSON correcto y con código 200. Si respondes un error o texto no válido, el agente arrancará sin variables.
Trabaja pesado en el fin de llamada
Para procesos lentos (guardar en el CRM, enviar emails, actualizar hojas), usa el webhook de fin de llamada: ahí ya no hay nadie esperando al teléfono.
Prepárate para reintentos
Diseña tu endpoint para que recibir el mismo aviso dos veces no cause duplicados. Usa el call_id como referencia única.
Diseña con datos ausentes
Puede que el webhook no encuentre a la persona. Escribe el prompt y el saludo para que funcionen igual de bien sin las variables.
Usa HTTPS
Publica tus webhooks siempre bajo HTTPS. Atendy no envía datos a direcciones internas o privadas por seguridad.
Si usas n8n, Make o Zapier, crea un nodo Webhook que reciba el payload de fin de llamada y encadena a partir de ahí: guardar en Google Sheets, avisar por Slack o crear una tarea. No necesitas programar nada.

Receta: cada llamada en una fila de tu Excel o Google Sheets (n8n)

Es lo que más se pide: que cada llamada quede apuntada en la hoja donde el negocio ya lleva sus clientes, esté en Google Sheets, en OneDrive o en Dropbox. Se hace con el webhook de fin de llamada y un flujo de n8n de tres nodos. Con Make o Zapier es igual, cambian los nombres.

  1. En n8n crea un flujo nuevo y añade un nodo Webhook con método POST. Copia la URL de producción que te da.
  2. En Atendy, abre el agente → Integraciones y pega esa URL en «Webhook de agente · post-llamada». Guarda.
  3. Haz una llamada de prueba al agente (o usa Probar agente). Al minuto llegará a n8n el evento call_analyzed: en el nodo Webhook pulsa «Listen for test event» antes de llamar para ver los campos.
  4. Añade un nodo Google Sheets (acción «Append row») o Microsoft Excel 365 («Add a row») y apunta a tu hoja. Si tu archivo está en Dropbox, n8n tiene nodo de Dropbox pero no edita celdas: para Excel en Dropbox lo práctico es que la hoja viva en OneDrive o en Google Sheets, o mover el archivo.
  5. Mapea las columnas arrastrando desde el evento: fecha = {{ $json.call.start_timestamp }} (milisegundos; conviértelo con DateTime.fromMillis), teléfono = call.from_number en entrantes y call.to_number en salientes, nombre = call.crm_client.name, número de cliente = call.crm_client.client_ref, resumen = call.call_analysis.call_summary, y cada Dato del agente = call.call_analysis.custom_analysis_data.<nombre_del_dato>. Guarda también call.call_id: es tu referencia única.
  6. Si quieres actualizar la fila del cliente en vez de añadir una por llamada, usa la acción «Update row» con la columna del teléfono (o del número de cliente) como clave de búsqueda, y añade las columnas nuevas a la derecha de las tuyas.
  7. Activa el flujo. A partir de ahí cada llamada aparece en tu hoja un par de minutos después de colgar.
Los nombres de los Datos del agente son los que pusiste en la pestaña Datos del editor, en minúsculas y con guiones bajos (por ejemplo tipo_peritacion). Si un dato no se pudo extraer en esa llamada, no viene en custom_analysis_data: dale un valor por defecto en el nodo de la hoja para que la columna no quede rota.