Catálogo de eventos
Todos los tipos de evento a los que puedes suscribir un webhook, cada uno con sus campos, su versión, su esquema JSON y un ejemplo tal y como llega; los objetos que comparten y los vocabularios cerrados. Cómo crear un webhook y verificar la firma está en la guía.
El catálogo
#Todos los tipos a los que puedes suscribirte. En data cada clave está siempre presente: null cuando no aplica. Nunca hay identificadores de nuestros proveedores.
El contrato solo crece. Podemos añadir campos a un tipo o valores a un vocabulario sin avisar, así que tu receptor debe ignorar lo que no conoce. Cada tipo indica su versión y enlaza su esquema JSON. Ver versiones y compatibilidad.
Llamadas
call.dispatched · Llamada iniciada v1
#Llamadas
Cuando el proveedor de voz acepta la llamada y el teléfono del cliente empieza a sonar. Una vez por llamada.
subject: calls/<call.id>.
Esquema JSON de data: https://calligence.ai/docs/webhooks/schemas/call.dispatched/v1.json.
| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
call | objeto call | no | La llamada. Ver call. |
lead | objeto lead | sí | El lead (el cliente dentro de la campaña), o null si no hay ninguno. Ver lead. |
customer | objeto customer | sí | El cliente, o null. Ver customer. |
campaign | objeto campaign | sí | La campaña, o null si no hay ninguna. Ver campaign. |
agent | objeto agent | no | El agente. Ver agent. |
dispatched_at | fecha y hora ISO-8601 UTC | no | Cuándo el proveedor aceptó la llamada (igual que time). |
Ejemplo (formateado; los campos de contenido van incluidos):
{
"specversion": "1.0",
"id": "evt_fc61d094008b4d00a9f371e2176e3746",
"source": "urn:calligence:pro:org:42",
"type": "call.dispatched",
"subject": "calls/9182",
"time": "2026-10-05T09:30:00.218000Z",
"datacontenttype": "application/json",
"dataschema": "https://calligence.ai/docs/webhooks/schemas/call.dispatched/v1.json",
"sequence": "01791192600218000",
"data": {
"call": {
"id": 9182,
"direction": "outbound",
"status": "initiated",
"attempt_index": 1,
"started_at": "2026-10-05T09:30:00Z",
"ended_at": null,
"duration_s": null,
"from_number": "+34910000000",
"to_number": "+34600111222"
},
"lead": {
"id": 5561,
"status": "PENDIENTE",
"status_reason": null,
"product_name": "Producto de ejemplo",
"product_code": "PROD-01"
},
"customer": {
"id": 3120,
"external_id": "A-1027",
"phone": "+34600111222",
"name": "María López"
},
"campaign": {
"id": 27,
"name": "Campaña de ejemplo"
},
"agent": {
"type": "mi_agente"
},
"dispatched_at": "2026-10-05T09:30:00.218000Z"
}
}
call.failed · Llamada no conectada v1
#Llamadas
Cuando una llamada no llega a conectar: no pudimos iniciarla (dispatch) o se inició y no conectó (initiation: comunicaba, nadie contestó…). Una vez por llamada.
subject: calls/<call.id>.
Esquema JSON de data: https://calligence.ai/docs/webhooks/schemas/call.failed/v1.json.
| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
call | objeto call | no | La llamada. Ver call. |
lead | objeto lead | sí | El lead (el cliente dentro de la campaña), o null si no hay ninguno. Ver lead. |
customer | objeto customer | sí | El cliente, o null. Ver customer. |
campaign | objeto campaign | sí | La campaña, o null si no hay ninguna. Ver campaign. |
agent | objeto agent | no | El agente. Ver agent. |
stage | texto de etapas de fallo | no | En qué etapa falló. Ver etapas. |
reason | texto de motivos de fallo | no | Por qué. Ver motivos de fallo. |
failed_at | fecha y hora ISO-8601 UTC | no | Cuándo lo supimos (igual que time). |
Ejemplo (formateado; los campos de contenido van incluidos):
{
"specversion": "1.0",
"id": "evt_b7a00930eb3cc218809c7f54e49c0045",
"source": "urn:calligence:pro:org:42",
"type": "call.failed",
"subject": "calls/9183",
"time": "2026-10-05T10:36:40.500000Z",
"datacontenttype": "application/json",
"dataschema": "https://calligence.ai/docs/webhooks/schemas/call.failed/v1.json",
"sequence": "01791196600500000",
"data": {
"call": {
"id": 9183,
"direction": "outbound",
"status": "failed",
"attempt_index": 2,
"started_at": "2026-10-05T10:36:10Z",
"ended_at": "2026-10-05T10:36:40.500000Z",
"duration_s": 0.0,
"from_number": "+34910000000",
"to_number": "+34600111222"
},
"lead": {
"id": 5561,
"status": "PENDIENTE",
"status_reason": null,
"product_name": "Producto de ejemplo",
"product_code": "PROD-01"
},
"customer": {
"id": 3120,
"external_id": "A-1027",
"phone": "+34600111222",
"name": "María López"
},
"campaign": {
"id": 27,
"name": "Campaña de ejemplo"
},
"agent": {
"type": "mi_agente"
},
"stage": "initiation",
"reason": "no_answer",
"failed_at": "2026-10-05T10:36:40.500000Z"
}
}
call.ended · Llamada terminada v1
#Llamadas
Cuando termina una llamada y recibimos su transcripción, antes de analizarla. Lleva el estado, la duración y la hora de fin que nos dio el proveedor. Una vez por llamada.
subject: calls/<call.id>.
Esquema JSON de data: https://calligence.ai/docs/webhooks/schemas/call.ended/v1.json.
| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
call | objeto call | no | La llamada. Ver call. |
lead | objeto lead | sí | El lead (el cliente dentro de la campaña), o null si no hay ninguno. Ver lead. |
customer | objeto customer | sí | El cliente, o null. Ver customer. |
campaign | objeto campaign | sí | La campaña, o null si no hay ninguna. Ver campaign. |
agent | objeto agent | no | El agente. Ver agent. |
ended_reason | texto de motivos de fallo | sí | null si la llamada terminó bien; si falló, el motivo (nunca unknown: sin motivo del proveedor es provider_error). |
transcript contenido | lista de objetos turno | sí | La conversación, turno a turno. Solo con «Incluir contenido»; null si no hay o si se recortó. |
transcript_available | booleano | no | true si la llamada tiene transcripción, aunque este webhook no reciba contenido. Así sabes si merece la pena activarlo. |
transcript_truncated | booleano | no | true cuando la transcripción se quitó porque el cuerpo superaba 256 KB. |
Ejemplo (formateado; los campos de contenido van incluidos):
{
"specversion": "1.0",
"id": "evt_ab20a902151d5fc47fc589fd3d3b06f7",
"source": "urn:calligence:pro:org:42",
"type": "call.ended",
"subject": "calls/9182",
"time": "2026-10-05T09:33:12.731000Z",
"datacontenttype": "application/json",
"dataschema": "https://calligence.ai/docs/webhooks/schemas/call.ended/v1.json",
"sequence": "01791192792731000",
"data": {
"call": {
"id": 9182,
"direction": "outbound",
"status": "completed",
"attempt_index": 1,
"started_at": "2026-10-05T09:30:00Z",
"ended_at": "2026-10-05T09:33:10Z",
"duration_s": 190.0,
"from_number": "+34910000000",
"to_number": "+34600111222"
},
"lead": {
"id": 5561,
"status": "PENDIENTE",
"status_reason": null,
"product_name": "Producto de ejemplo",
"product_code": "PROD-01"
},
"customer": {
"id": 3120,
"external_id": "A-1027",
"phone": "+34600111222",
"name": "María López"
},
"campaign": {
"id": 27,
"name": "Campaña de ejemplo"
},
"agent": {
"type": "mi_agente"
},
"ended_reason": null,
"transcript": [
{
"role": "agent",
"text": "Hola, María. Te llamo por tu solicitud de información."
},
{
"role": "user",
"text": "Sí, dime."
}
],
"transcript_available": true,
"transcript_truncated": false
}
}
call.analyzed · Llamada analizada v1
#Llamadas
Cuando termina el análisis de la llamada: el veredicto, el resumen, los datos extraídos y la próxima llamada. Solo cuando el análisis se completa; si falla, se reintenta y el evento sale con el intento que lo completa. Una llamada sin agente que la analice también lo emite, con outcome, summary, extracted y next_call_at a null. Una vez por llamada.
subject: calls/<call.id>.
Esquema JSON de data: https://calligence.ai/docs/webhooks/schemas/call.analyzed/v1.json.
| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
call | objeto call | no | La llamada. Ver call. |
lead | objeto lead | sí | El lead (el cliente dentro de la campaña), o null si no hay ninguno. Ver lead. |
customer | objeto customer | sí | El cliente, o null. Ver customer. |
campaign | objeto campaign | sí | La campaña, o null si no hay ninguna. Ver campaign. |
agent | objeto agent | no | El agente. Ver agent. |
outcome | objeto outcome | sí | El veredicto del análisis. Ver outcome. null si la llamada no tiene agente que la analice. |
summary | texto | sí | El resumen de la llamada, en español; null si no se pudo generar. |
extracted | objeto | sí | Los datos extraídos de la conversación: los comunes (name, preferred_contact_times, wants_callback…) y los propios de tu agente. Sus claves dependen del agente: trátalo como un objeto abierto. null si la extracción falló. |
next_call_at | fecha y hora ISO-8601 UTC | sí | Cuándo está programada la próxima llamada a este lead, si este análisis programó una; si no, null (puede haber otra programada antes). Es la hora programada: si cae fuera del horario de la campaña o en un festivo excluido, la llamada sale en el siguiente hueco permitido. |
transcript contenido | lista de objetos turno | sí | La conversación, turno a turno. Solo con «Incluir contenido»; null si no hay o si se recortó. |
transcript_truncated | booleano | no | true cuando la transcripción se quitó porque el cuerpo superaba 256 KB. |
Ejemplo (formateado; los campos de contenido van incluidos):
{
"specversion": "1.0",
"id": "evt_889cbc460859b8759e6875beda55481a",
"source": "urn:calligence:pro:org:42",
"type": "call.analyzed",
"subject": "calls/9182",
"time": "2026-10-05T09:33:21.044000Z",
"datacontenttype": "application/json",
"dataschema": "https://calligence.ai/docs/webhooks/schemas/call.analyzed/v1.json",
"sequence": "01791192801044000",
"data": {
"call": {
"id": 9182,
"direction": "outbound",
"status": "completed",
"attempt_index": 1,
"started_at": "2026-10-05T09:30:00Z",
"ended_at": "2026-10-05T09:33:10Z",
"duration_s": 190.0,
"from_number": "+34910000000",
"to_number": "+34600111222"
},
"lead": {
"id": 5561,
"status": "EXITO",
"status_reason": "acepta_traspaso",
"product_name": "Producto de ejemplo",
"product_code": "PROD-01"
},
"customer": {
"id": 3120,
"external_id": "A-1027",
"phone": "+34600111222",
"name": "María López"
},
"campaign": {
"id": 27,
"name": "Campaña de ejemplo"
},
"agent": {
"type": "mi_agente"
},
"outcome": {
"lead_status": "EXITO",
"reason": "acepta_traspaso",
"intent": "acepta_traspaso"
},
"summary": "La clienta confirma su interés y acepta que un asesor la llame el jueves por la tarde.",
"extracted": {
"additional_info": null,
"age": null,
"is_asap": false,
"key_points": null,
"name": "María López",
"preferred_contact_times": "el jueves por la tarde",
"wants_callback": true
},
"next_call_at": null,
"transcript": [
{
"role": "agent",
"text": "Hola, María. Te llamo por tu solicitud de información."
},
{
"role": "user",
"text": "Sí, dime."
}
],
"transcript_truncated": false
}
}
whatsapp.message.received · Mensaje de WhatsApp recibido v1
#Cuando un cliente te escribe por WhatsApp, con texto o con un adjunto. Una vez por mensaje.
subject: whatsapp_messages/<message.id>.
Esquema JSON de data: https://calligence.ai/docs/webhooks/schemas/whatsapp.message.received/v1.json.
| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
customer | objeto customer | sí | El cliente, o null. Ver customer. |
lead | objeto lead | sí | El lead (el cliente dentro de la campaña), o null si no hay ninguno. Ver lead. |
campaign | objeto campaign | sí | La campaña, o null si no hay ninguna. Ver campaign. |
message | objeto message | no | El mensaje recibido (abajo). |
Dentro de message:
| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
id | entero | no | El id del mensaje en Calligence. |
received_at | fecha y hora ISO-8601 UTC | no | Cuándo llegó. |
text contenido | texto | sí | El texto. Solo con «Incluir contenido»; null si el mensaje es solo un adjunto. |
media | objeto media | sí | El adjunto, si trae uno. Ver media. |
sender_number | texto | sí | Tu número de WhatsApp que recibió el mensaje, en E.164. |
customer_number | texto | sí | El número del cliente, en E.164. |
Ejemplo (formateado; los campos de contenido van incluidos):
{
"specversion": "1.0",
"id": "evt_ea9157b81d6e9d31126458ad7f1ef00f",
"source": "urn:calligence:pro:org:42",
"type": "whatsapp.message.received",
"subject": "whatsapp_messages/771",
"time": "2026-10-05T09:40:00Z",
"datacontenttype": "application/json",
"dataschema": "https://calligence.ai/docs/webhooks/schemas/whatsapp.message.received/v1.json",
"sequence": "01791193200000000",
"data": {
"customer": {
"id": 3120,
"external_id": "A-1027",
"phone": "+34600111222",
"name": "María López"
},
"lead": {
"id": 5561,
"status": "PENDIENTE",
"status_reason": null,
"product_name": "Producto de ejemplo",
"product_code": "PROD-01"
},
"campaign": {
"id": 27,
"name": "Campaña de ejemplo"
},
"message": {
"id": 771,
"received_at": "2026-10-05T09:40:00Z",
"text": "¿Me podéis llamar mañana?",
"media": null,
"sender_number": "+34910000000",
"customer_number": "+34600111222"
}
}
}
whatsapp.message.sent · Mensaje de WhatsApp enviado v1
#Cuando aceptamos el envío de un mensaje nuestro: una plantilla, el aviso previo a una llamada, una respuesta de la IA o de tu equipo. Que salga hacia WhatsApp y llegue lo cuenta después whatsapp.message.status_updated. Una vez por mensaje.
subject: whatsapp_messages/<message.id>.
Esquema JSON de data: https://calligence.ai/docs/webhooks/schemas/whatsapp.message.sent/v1.json.
| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
customer | objeto customer | sí | El cliente, o null. Ver customer. |
lead | objeto lead | sí | El lead (el cliente dentro de la campaña), o null si no hay ninguno. Ver lead. |
campaign | objeto campaign | sí | La campaña, o null si no hay ninguna. Ver campaign. |
message | objeto message | no | El mensaje enviado (abajo). |
call_id | entero | sí | La llamada relacionada, si la hay. |
Dentro de message:
| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
id | entero | no | El id del mensaje en Calligence. |
sent_at | fecha y hora ISO-8601 UTC | no | Cuándo aceptamos el envío. Su salida hacia WhatsApp la marca después el estado sent. |
author | texto de autores de un mensaje de whatsapp | no | Quién lo escribió. Ver autores. |
template_key | texto | sí | La plantilla usada, si es una plantilla. |
text contenido | texto | sí | El texto. Solo con «Incluir contenido»; null en una plantilla (template, precall_notice): su texto no se publica. |
sender_number | texto | sí | Tu número de WhatsApp, en E.164. |
customer_number | texto | sí | El número del cliente, en E.164. |
Ejemplo (formateado; los campos de contenido van incluidos):
{
"specversion": "1.0",
"id": "evt_b413def0dabc0829be8dd2043877d28c",
"source": "urn:calligence:pro:org:42",
"type": "whatsapp.message.sent",
"subject": "whatsapp_messages/772",
"time": "2026-10-05T09:40:12Z",
"datacontenttype": "application/json",
"dataschema": "https://calligence.ai/docs/webhooks/schemas/whatsapp.message.sent/v1.json",
"sequence": "01791193212000000",
"data": {
"customer": {
"id": 3120,
"external_id": "A-1027",
"phone": "+34600111222",
"name": "María López"
},
"lead": {
"id": 5561,
"status": "PENDIENTE",
"status_reason": null,
"product_name": "Producto de ejemplo",
"product_code": "PROD-01"
},
"campaign": {
"id": 27,
"name": "Campaña de ejemplo"
},
"message": {
"id": 772,
"sent_at": "2026-10-05T09:40:12Z",
"author": "ai",
"template_key": null,
"text": "¡Claro! ¿A qué hora te viene bien?",
"sender_number": "+34910000000",
"customer_number": "+34600111222"
},
"call_id": null
}
}
whatsapp.message.status_updated · Estado de un mensaje de WhatsApp v1
#Cuando cambia el estado de entrega de un mensaje enviado (entregado, leído, fallido…) o cuando un envío falla. Una vez por mensaje y estado.
subject: whatsapp_messages/<message.id>.
Esquema JSON de data: https://calligence.ai/docs/webhooks/schemas/whatsapp.message.status_updated/v1.json.
| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
customer | objeto customer | sí | El cliente, o null. Ver customer. |
lead | objeto lead | sí | El lead (el cliente dentro de la campaña), o null si no hay ninguno. Ver lead. |
campaign | objeto campaign | sí | La campaña, o null si no hay ninguna. Ver campaign. |
message | objeto message | no | El mensaje (abajo). |
status | texto de estados de entrega de whatsapp | no | El nuevo estado. Ver estados de entrega. |
error | texto de errores de envío de whatsapp | sí | Por qué falló, si status es failed. Ver errores de envío. null en cualquier otro estado, también en undelivered. |
updated_at | fecha y hora ISO-8601 UTC | no | Cuándo cambió. |
Dentro de message:
| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
id | entero | no | El id del mensaje en Calligence. |
author | texto de autores de un mensaje de whatsapp | no | Quién lo escribió. Ver autores. |
sender_number | texto | sí | Tu número de WhatsApp, en E.164. |
customer_number | texto | sí | El número del cliente, en E.164. |
Ejemplo (formateado; los campos de contenido van incluidos):
{
"specversion": "1.0",
"id": "evt_6bf1be0dcd9f8f00c25fd75a19b09bd6",
"source": "urn:calligence:pro:org:42",
"type": "whatsapp.message.status_updated",
"subject": "whatsapp_messages/772",
"time": "2026-10-05T09:40:40Z",
"datacontenttype": "application/json",
"dataschema": "https://calligence.ai/docs/webhooks/schemas/whatsapp.message.status_updated/v1.json",
"sequence": "01791193240000000",
"data": {
"customer": {
"id": 3120,
"external_id": "A-1027",
"phone": "+34600111222",
"name": "María López"
},
"lead": {
"id": 5561,
"status": "PENDIENTE",
"status_reason": null,
"product_name": "Producto de ejemplo",
"product_code": "PROD-01"
},
"campaign": {
"id": 27,
"name": "Campaña de ejemplo"
},
"message": {
"id": 772,
"author": "ai",
"sender_number": "+34910000000",
"customer_number": "+34600111222"
},
"status": "read",
"error": null,
"updated_at": "2026-10-05T09:40:40Z"
}
}
whatsapp.message.media_processed · Adjunto de WhatsApp procesado v1
#Cuando terminamos de procesar un adjunto recibido (por ejemplo, la transcripción de una nota de voz). Un adjunto que no llega a procesarse no lo emite. Una vez por adjunto.
subject: whatsapp_messages/<message.id>.
Esquema JSON de data: https://calligence.ai/docs/webhooks/schemas/whatsapp.message.media_processed/v1.json.
| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
customer | objeto customer | sí | El cliente, o null. Ver customer. |
lead | objeto lead | sí | El lead (el cliente dentro de la campaña), o null si no hay ninguno. Ver lead. |
campaign | objeto campaign | sí | La campaña, o null si no hay ninguna. Ver campaign. |
message | objeto message | no | El mensaje que traía el adjunto (abajo). |
media | objeto media | no | El adjunto procesado (abajo). |
Dentro de message:
| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
id | entero | no | El id del mensaje en Calligence. |
Dentro de media:
| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
kind | texto de tipos de adjunto | no | Ver tipos de adjunto. |
content_type | texto | sí | El tipo MIME que declaró WhatsApp. |
status | texto de resultados del procesado de un adjunto | no | Cómo acabó. Ver resultados. |
text contenido | texto | sí | El texto obtenido (por ejemplo, la transcripción). Solo con «Incluir contenido». |
Ejemplo (formateado; los campos de contenido van incluidos):
{
"specversion": "1.0",
"id": "evt_e8bc0b6d73a8347bb98ce4fe01120aaa",
"source": "urn:calligence:pro:org:42",
"type": "whatsapp.message.media_processed",
"subject": "whatsapp_messages/773",
"time": "2026-10-05T09:41:40Z",
"datacontenttype": "application/json",
"dataschema": "https://calligence.ai/docs/webhooks/schemas/whatsapp.message.media_processed/v1.json",
"sequence": "01791193300000000",
"data": {
"customer": {
"id": 3120,
"external_id": "A-1027",
"phone": "+34600111222",
"name": "María López"
},
"lead": {
"id": 5561,
"status": "PENDIENTE",
"status_reason": null,
"product_name": "Producto de ejemplo",
"product_code": "PROD-01"
},
"campaign": {
"id": 27,
"name": "Campaña de ejemplo"
},
"message": {
"id": 773
},
"media": {
"kind": "audio",
"content_type": "audio/ogg",
"status": "done",
"text": "Mejor por la tarde, a partir de las seis."
}
}
}
Leads
lead.status_changed · Cambio de estado del lead v1
#Leads próximamente
Cada vez que cambia el estado de un lead: por el análisis de una llamada, cuando tu equipo registra o corrige un resultado, cuando el chat de WhatsApp decide un veredicto, al volver a entrar en una campaña o al marcarlo como trabajado fuera. Si el estado no cambia, no hay evento. Una vez por cambio.
Ya puedes suscribirte a este tipo, pero todavía no se emite: lo documentamos para que puedas prepararte. Esta marca desaparecerá cuando empiece a llegar.
subject: leads/<lead.id>.
Esquema JSON de data: https://calligence.ai/docs/webhooks/schemas/lead.status_changed/v1.json.
| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
lead | objeto lead | no | El lead, ya con su estado nuevo. Ver lead. |
customer | objeto customer | sí | El cliente, o null. Ver customer. |
campaign | objeto campaign | sí | La campaña, o null si no hay ninguna. Ver campaign. |
from | texto de estados del lead | no | El estado anterior. Ver estados del lead. |
to | texto de estados del lead | no | El estado nuevo. |
reason | texto | sí | El motivo del cambio (texto libre, puede ampliarse). |
call_id | entero | sí | La llamada relacionada, si la hay. |
changed_at | fecha y hora ISO-8601 UTC | no | Cuándo cambió. |
Ejemplo (formateado; los campos de contenido van incluidos):
{
"specversion": "1.0",
"id": "evt_69f0449b53cffe573977bae5123c929b",
"source": "urn:calligence:pro:org:42",
"type": "lead.status_changed",
"subject": "leads/5561",
"time": "2026-10-05T09:33:21.500000Z",
"datacontenttype": "application/json",
"dataschema": "https://calligence.ai/docs/webhooks/schemas/lead.status_changed/v1.json",
"sequence": "01791192801500000",
"data": {
"lead": {
"id": 5561,
"status": "EXITO",
"status_reason": "acepta_traspaso",
"product_name": "Producto de ejemplo",
"product_code": "PROD-01"
},
"customer": {
"id": 3120,
"external_id": "A-1027",
"phone": "+34600111222",
"name": "María López"
},
"campaign": {
"id": 27,
"name": "Campaña de ejemplo"
},
"from": "PENDIENTE",
"to": "EXITO",
"reason": "acepta_traspaso",
"call_id": 9182,
"changed_at": "2026-10-05T09:33:21.500000Z"
}
}
webhook.test · Evento de prueba
#Lo que envía Enviar prueba. No es un tipo al que se pueda suscribir: llega solo cuando lo pides, con subject endpoints/<id>.
Esquema JSON de data: https://calligence.ai/docs/webhooks/schemas/webhook.test/v1.json.
{
"specversion": "1.0",
"id": "evt_273932f5d0f839406081315a1136aa4d",
"source": "urn:calligence:pro:org:42",
"type": "webhook.test",
"subject": "endpoints/3",
"time": "2026-10-05T09:30:30Z",
"datacontenttype": "application/json",
"dataschema": "https://calligence.ai/docs/webhooks/schemas/webhook.test/v1.json",
"sequence": "01791192630000000",
"data": {
"endpoint_id": 3,
"note": "Evento de prueba de Calligence"
}
}
Objetos comunes
#Los objetos que comparten los eventos de llamadas, WhatsApp y leads.
call
#| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
id | entero | no | El id de la llamada en Calligence. |
direction | texto de sentido de la llamada | no | Ver sentido. |
status | texto de estados de la llamada | no | Ver estados de la llamada. |
attempt_index | entero | sí | Qué intento es para este lead (1, 2, 3…); null en una entrante. |
started_at | fecha y hora ISO-8601 UTC | sí | Cuándo empezó. |
ended_at | fecha y hora ISO-8601 UTC | sí | Cuándo terminó: la hora que nos dio el proveedor de voz; si no la dio (por ejemplo, una llamada cortada al saludar), cuándo lo supimos. En call.failed (initiation), siempre cuándo lo supimos. null mientras la llamada no ha terminado (call.dispatched, call.failed en dispatch). |
duration_s | número | sí | Duración en segundos, según el proveedor. Es la duración de la llamada: no la calcules como ended_at − started_at. 0 en call.failed (initiation); null antes de terminar. |
from_number | texto | sí | Quién llama, en E.164: tu número en una saliente, el del cliente en una entrante. null si el cliente llamó con el número oculto. |
to_number | texto | sí | A quién se llama, en E.164: el del cliente en una saliente, tu número en una entrante. null si es el del cliente y lo tenía oculto. |
lead
#| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
id | entero | no | El id del lead en Calligence. |
status | texto de estados del lead | no | Su estado en el momento del evento. Ver estados del lead. |
status_reason | texto | sí | El motivo interno de ese estado (texto libre, puede ampliarse). |
product_name | texto | sí | El producto que le interesa al cliente. |
product_code | texto | sí | El código de ese producto. |
customer
#| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
id | entero | no | El id del cliente en Calligence. |
external_id | texto | sí | Tu identificador, tal y como nos llegó; null si el lead entró sin él. |
phone | texto | sí | Su teléfono, en E.164; null si llamó con el número oculto. |
name | texto | sí | Su nombre; null si no lo conocemos. |
campaign
#| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
id | entero | no | El id de la campaña en Calligence. |
name | texto | sí | Su nombre. |
agent
#| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
type | texto | sí | La clave del agente que hizo o atendió la llamada; null si no hubo agente. |
Un turno de transcript
#| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
role | texto de turnos de la transcripción | no | Quién habla. Ver turnos. |
text | texto | no | Lo que dijo. |
outcome
#| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
lead_status | texto de estados del lead | sí | El estado que decidió esta llamada. Puede no coincidir con lead.status, que es el estado del lead después del análisis: si tu equipo o el chat de WhatsApp ya habían cerrado el lead, una llamada sin veredicto no lo reabre y lead.status conserva ese cierre. Para automatizar, usa lead.status. |
reason | texto | sí | El motivo de ese veredicto (texto libre, puede ampliarse). |
intent | texto | sí | La intención detectada en el cliente (por ejemplo acepta_traspaso o reagendar_calligence), o null. |
media
#| Campo | Tipo | ¿Puede ser null? | Qué es |
|---|---|---|---|
kind | texto de tipos de adjunto | no | Ver tipos de adjunto. |
content_type | texto | sí | El tipo MIME que declaró WhatsApp. |
Vocabularios cerrados
#Los campos con un conjunto fijo de valores. Pueden ganar valores nuevos (trátalos con un caso por defecto), nunca perderlos.
Estados del lead
#| Valor | Qué significa |
|---|---|
PENDIENTE | En curso: el lead sigue abierto (quedan intentos o hay otra llamada programada). Como veredicto de una llamada (outcome) significa «sin veredicto». |
EXITO | Objetivo conseguido: cita acordada, traspaso aceptado o transferencia a una persona de tu equipo. |
INVALIDO | Descartado: ilocalizable tras todos los intentos, persona equivocada o no cumple los requisitos. |
NO_INTERESADO | El cliente rechazó la propuesta. |
TRABAJADO_EXTERNO | Tu equipo lo cerró fuera de Calligence. Nunca sale del análisis de una llamada. |
Motivos de fallo
#| Valor | Qué significa |
|---|---|
busy | El número comunicaba. |
no_answer | Sonó y nadie contestó. |
not_connected | La red telefónica no conectó la llamada por otro motivo: número inexistente, fuera de servicio, rechazada… |
unreachable_number | No se puede llamar a ese número desde Calligence (destino no permitido). Reintentar no servirá. |
missing_variables | Faltaba un dato que el agente necesita para saludar (por ejemplo, el nombre del cliente): la llamada no se hizo o se cortó al empezar. Revisa los datos del lead. |
provider_error | Un error de nuestro proveedor de voz. |
dispatch_error | Un error interno al iniciar la llamada. |
unknown | El proveedor no dio ningún motivo. Solo aparece en call.failed. |
Etapas de fallo
#| Valor | Qué significa |
|---|---|
dispatch | Calligence no pudo iniciar la llamada: normalmente, el teléfono del cliente no llegó a sonar (ver los casos límite). |
initiation | La llamada se inició, pero no llegó a conectar con el cliente. |
Sentido de la llamada
#| Valor | Qué significa |
|---|---|
outbound | Saliente: la hizo tu agente. |
inbound | Entrante: el cliente llamó a tu número. |
Estados de la llamada
#| Valor | Qué significa |
|---|---|
initiated | Iniciada, todavía sin resultado. |
completed | La conversación terminó con normalidad. |
failed | La llamada falló: no conectó, o la conversación se cortó por un error. |
Turnos de la transcripción
#| Valor | Qué significa |
|---|---|
agent | Habla el agente de IA. |
user | Habla el cliente. |
Autores de un mensaje de WhatsApp
#| Valor | Qué significa |
|---|---|
template | Una plantilla aprobada por Meta (la única forma de escribir fuera de la ventana de 24 h). |
precall_notice | El aviso que se envía antes de una llamada. |
ai | Una respuesta del agente de IA. |
operator | Una persona de tu equipo, desde la consola. |
Estados de entrega de WhatsApp
#| Valor | Qué significa |
|---|---|
sent | Salió hacia WhatsApp. |
delivered | Llegó al teléfono del cliente. |
read | El cliente lo leyó (si tiene activadas las confirmaciones de lectura). |
failed | No se pudo enviar o entregar; error dice por qué. |
undelivered | WhatsApp no pudo entregarlo. Llega sin motivo: su error es null. |
Errores de envío de WhatsApp
#| Valor | Qué significa |
|---|---|
rejected | WhatsApp rechazó el envío. |
not_sent | No llegó a enviarse. |
outcome_unknown | No sabemos si se envió (se cortó la comunicación con el proveedor); no lo reintentamos para no duplicarlo. |
delivery_failed | Se aceptó, pero después no se pudo enviar ni entregar: un failed que llega tras whatsapp.message.sent. |
Tipos de adjunto
#| Valor | Qué significa |
|---|---|
audio | Una nota de voz o un audio. |
image | Una imagen. |
media | Cualquier otro adjunto (documento, vídeo…). |
Resultados del procesado de un adjunto
#| Valor | Qué significa |
|---|---|
done | Convertido a texto (por ejemplo, una nota de voz transcrita). |
failed | La conversión falló. |
too_large | Demasiado grande para procesarlo. |
unsupported | Formato no admitido. |
disabled | El procesado de ese tipo de adjunto no está activado. |
Casos límite
#- Un webhook filtrado por campañas nunca recibe lo que no tiene campaña: por ejemplo, la llamada entrante de alguien que no es lead de ninguna campaña activa, o los mensajes de WhatsApp de un chat que no pertenece a ningún lead. Solo la reciben los webhooks con «Todas».
- Un intento que no llega a existir no emite nada. Si no podemos ni crear la llamada (no hay ningún número disponible, el cliente ya no existe), no hay
call.failed. - Muy raramente,
call.dispatchedva seguido decall.failed(dispatch): el proveedor aceptó la llamada pero fallamos al registrarlo. El teléfono pudo sonar, y después pueden llegarcall.endedycall.analyzed. - Igual de raro: si falla el guardado que registra que el proveedor aceptó la llamada, no llega
call.dispatched, solocall.failed(dispatch), aunque el teléfono pudo sonar. call.failedllega cuando terminamos de procesar el fallo. Si ese procesado falla una y otra vez, la llamada se queda como iniciada y no se emite.call.analyzedsolo llega si el análisis se completa, youtcome.lead_statuses lo que decidió esa llamada, mientras quelead.statuses el estado del lead después: pueden diferir.- Lo que no sabemos viaja como
null, nunca como un valor de relleno: un cliente sin nombre tienename: null, y una llamada entrante,attempt_index: null.