---
title: "Catálogo de eventos"
description: "Todos los tipos de eventos de webhook que emite ThunderPhone."
---

Cada cuerpo de webhook tiene un campo `type` cuyo valor es uno de los tipos
de eventos de esta página. Cuando te suscribes a un
[endpoint](/es/webhooks/endpoints), el arreglo `events` debe contener los
tipos de eventos que quieres (o estar vacío para suscribirte a todo,
excepto los eventos por turno [`telephony.turn`](#telephony-turn) /
[`web.turn`](#web-turn), que se entregan solo a endpoints que los
indican explícitamente).

Dos estilos de entrega incluyen estos eventos:

* Las **entregas a endpoints** siempre son notificaciones **no bloqueantes** con [reintentos](/es/webhooks/overview): responde con cualquier código 2xx; el sobre incluye un `event_id` para deduplicar.
* Los intercambios **bloqueantes** se ejecutan solo en el [webhook heredado de URL única](/es/webhooks/overview): la solicitud de configuración [`telephony.incoming` / `web.incoming`](/es/webhooks/call-incoming) (números en modo webhook: 13 s, incluido un reintento; claves de widget en modo webhook: 10 s) y el [despacho de herramientas](/es/tools/overview) en modo webhook. Tu respuesta da forma a la llamada en curso.

Las cargas de ejemplo a continuación muestran el sobre de endpoint en su orden
de transmisión (claves ordenadas alfabéticamente: `data`, `event_id`, `type`);
las entregas heredadas incluyen los mismos `data` sin `event_id`.

## Eventos de llamadas

### `telephony.incoming`

Se envía cuando una llamada entrante llega a uno de tus
[números de teléfono](/api-reference/phone-numbers). Las entregas al endpoint son
notificaciones sin confirmación enviadas para **todas** las llamadas entrantes, tanto si
el número está configurado para un agente como para un webhook. Los números sin
un agente asignado también reciben la solicitud de configuración **bloqueante**
en el webhook heredado; consulta
[`telephony.incoming` / `web.incoming`](/es/webhooks/call-incoming) para ver
el esquema completo de solicitud / respuesta.

```json
{
  "data": {
    "call_id": 987654321,
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  },
  "event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
  "type": "telephony.incoming"
}
```

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `agent_id` | integer \| null | Agente que atendió la llamada, cuando había uno asignado |
| `agent_name` | string \| null | Agente que atendió la llamada, cuando había uno asignado |

### `telephony.complete`

Se envía cuando termina una llamada telefónica entrante o saliente. No bloqueante.
Incluye la transcripción, la URL de la grabación cuando está disponible y el resumen de facturación. Consulta
[`telephony.complete` / `web.complete`](/es/webhooks/call-complete) para ver
el esquema de la carga útil.

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `agent_id` | integer \| null | Agente que atendió la llamada, cuando había uno asignado |
| `agent_name` | string \| null | Agente que atendió la llamada, cuando había uno asignado |

### `telephony.tool`

Se envía después de que una llamada telefónica invoque una
[herramienta de función](/es/tools/overview). Notificación de auditoría no bloqueante:
la herramienta ya se ejecutó cuando se entrega este evento; cubre
tus propias herramientas de función y la herramienta integrada `send_sms` (no otras herramientas integradas,
de base de conocimientos, de conexión de aplicaciones ni MCP). Para `send_sms`,
tanto `arguments.to` como `arguments.body` son `[REDACTED]`. Las
transcripciones de finalización también los ocultan; obtén el contenido y el destinatario desde el
recurso SMS con ámbito de organización mediante el `message_id` de la respuesta.

```json
{
  "data": {
    "arguments": { "date": "2026-04-21" },
    "call_id": 987654321,
    "from_number": "+14155550199",
    "response": {
      "response": { "available_slots": ["9:00 AM", "2:00 PM"] },
      "status": 200
    },
    "to_number": "+15551234567",
    "tool_name": "search_appointments"
  },
  "event_id": "1f0a7c3e-52d4-4a0e-8f4b-b1a6a1c0d9e2",
  "type": "telephony.tool"
}
```

`response` es el resultado ejecutado: `{"status": <http status>,
"response": <your endpoint's JSON>}` si tiene éxito, o
`{"status": <status>, "error": "<message>"}` si falla.

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `agent_id` | integer \| null | Agente que atendió la llamada, cuando había uno asignado |
| `agent_name` | string \| null | Agente que atendió la llamada, cuando había uno asignado |

### `telephony.turn`

Se envía mientras una llamada telefónica está **en curso**, una vez por cada
intervención con voz a medida que sucede: las respuestas habladas del agente y
las intervenciones transcritas de quien llama. Te permite seguir la conversación
en vivo mediante webhooks simples en lugar de consultar repetidamente
[`GET /v1/calls/{call_id}/transcript`](/api-reference/calls#get-transcript).
No bloqueante.

<Note>
  **Se requiere una suscripción explícita.** A diferencia de todos los demás tipos de evento,
  los eventos de intervención nunca se entregan mediante un arreglo `events` vacío
  (suscripción a todos). Un endpoint los recibe solo cuando su lista `events`
  contiene `telephony.turn` / `web.turn` explícitamente.
</Note>

```json
{
  "data": {
    "call_id": 987654321,
    "entry_type": "completion",
    "from_number": "+14155550199",
    "position": 7,
    "role": "assistant",
    "start_ms": 15200,
    "text": "How many employees does your company have?",
    "to_number": "+15551234567"
  },
  "event_id": "8d3f5a2c-7b1e-4c9a-b6d0-2e4f6a8c0d1e",
  "type": "telephony.turn"
}
```

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `agent_id` | integer \| null | Agente que atendió la llamada, cuando había uno asignado |
| `agent_name` | string \| null | Agente que atendió la llamada, cuando había uno asignado |
| `position` | integer | Índice de la intervención en el historial de la llamada; una identidad estable para ordenar |
| `role` | string | `assistant` (voz del agente) o `user` (voz de quien llama) |
| `text` | string | Texto de la transcripción de la intervención tal como se conoce al momento de la emisión |
| `entry_type` | string | Tipo de entrada subyacente del historial: `completion` (agente), o `user_turn` / `span` (quien llama) |
| `start_ms`, `end_ms` | integer | Desplazamientos de audio en ms desde el inicio de la llamada; presentes solo cuando el tiempo de reproducción ya se conocía al momento de la emisión |

<Note>
  La entrega se realiza al menos una vez y las intervenciones pueden llegar **fuera de orden**:
  elimina duplicados con el `event_id` del sobre y ordena por `position`. Las
  transcripciones de quien llama aún pueden refinarse después de la emisión; la transcripción de
  [`telephony.complete`](/es/webhooks/call-complete) posterior a la llamada es
  la fuente autoritativa. Solo se emiten intervenciones habladas: las llamadas a herramientas, los susurros y
  otras entradas internas nunca aparecen en este flujo.
</Note>

### `web.incoming`

El equivalente de `telephony.incoming` para el canal web se envía cuando inicia una sesión de
[widget web](/es/widget/overview) o una llamada de prueba de micrófono del creador.
Las entregas al endpoint son sin confirmación para cada sesión web.
Las claves publicables en `mode="webhook"` también reciben la
solicitud de configuración **bloqueante** en el webhook heredado; esa
solicitud bloqueante tiene una estructura diferente (`origin_domain`,
`publishable_key_prefix`; sin números de teléfono). Consulta
[`telephony.incoming` / `web.incoming`](/es/webhooks/call-incoming).

```json
{
  "data": {
    "call_id": 987654322,
    "from_number": "web",
    "origin_domain": "https://example.com",
    "publishable_key_prefix": "pk_live_a1b2",
    "to_number": "+15551234567"
  },
  "event_id": "9a2b4c6d-8e0f-4a1b-9c2d-3e4f5a6b7c8d",
  "type": "web.incoming"
}
```

`from_number` siempre es el literal `"web"`. Para las sesiones del widget
en modo webhook, `to_number` está vacío (el número del agente de la sesión se asigna
después de la configuración); para las llamadas de prueba de micrófono del creador, `origin_domain` y
`publishable_key_prefix` están vacíos.

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `agent_id` | integer \| null | Agente que atendió la llamada, cuando había uno asignado |
| `agent_name` | string \| null | Agente que atendió la llamada, cuando había uno asignado |

### `web.complete`

El equivalente de `telephony.complete` para el canal web cubre las llamadas del
widget web (`direction: "web"`) y las llamadas de prueba de micrófono del creador
(`direction: "test"`). No bloqueante. Tiene la misma estructura de carga útil que
[`telephony.complete`](/es/webhooks/call-complete), además de `origin_domain`,
con `from_number` establecido en `"web"`.

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `agent_id` | integer \| null | Agente que atendió la llamada, cuando había uno asignado |
| `agent_name` | string \| null | Agente que atendió la llamada, cuando había uno asignado |

<Note>
  En el webhook heredado de URL única, las llamadas de prueba de micrófono del creador
  históricamente se informan como `telephony.complete`; solo las llamadas con `direction:
  "web"` usan allí el tipo `web.complete`. El sistema de endpoints
  asigna tanto las llamadas web como las de prueba a `web.*`. Las cargas útiles históricas pueden
  contener los valores heredados de `direction` `widget` o `mic`.
</Note>

### `web.tool`

El equivalente de `telephony.tool` para el canal web. `data` contiene
`origin_domain` en lugar de `from_number` / `to_number`.

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `agent_id` | integer \| null | Agente que atendió la llamada, cuando había uno asignado |
| `agent_name` | string \| null | Agente que atendió la llamada, cuando había uno asignado |

### `web.turn`

El equivalente de [`telephony.turn`](#telephony-turn) para el canal web,
que cubre las llamadas del widget web y las llamadas de prueba de micrófono del creador. Tiene la misma estructura de carga útil,
con `origin_domain` en lugar de `from_number` / `to_number`.
Al igual que `telephony.turn`, requiere una **suscripción explícita**:
nunca se entrega mediante un arreglo `events` vacío.

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `agent_id` | integer \| null | Agente que atendió la llamada, cuando había uno asignado |
| `agent_name` | string \| null | Agente que atendió la llamada, cuando había uno asignado |

### `call.transfer_completed`

Se envía una vez por llamada con el resultado final de una
[transferencia de llamada](/es/guides/call-transfers): una transferencia en frío o el
puente entre personas que deja una transferencia asistida después de que el agente se retira.
No bloquea y se entrega solo a endpoints (nunca al webhook heredado de una
sola URL). Llega después de `telephony.complete` de la llamada:
una vez que el operador se hace cargo de la llamada o la transferencia falla, o, cuando
ThunderPhone establece un puente para la llamada, una vez que el puente termina.

```json
{
  "data": {
    "agent_id": 12,
    "agent_name": "Front desk",
    "bridge_kind": "cold_fallback",
    "bridge_seconds": 255,
    "bridge_total_cents": 5,
    "call_id": 987654321,
    "destination": "+15555550102",
    "sip_status": 403,
    "transfer_outcome": "bridged"
  },
  "event_id": "5b0f6c1e-9d2a-5e7b-8c4d-1a2b3c4d5e6f",
  "type": "call.transfer_completed"
}
```

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `call_id` | integer | La llamada que se transfirió |
| `agent_id` | integer \| null | Agente que gestionó la llamada, cuando se asignó uno |
| `agent_name` | string \| null | Agente que gestionó la llamada, cuando se asignó uno |
| `transfer_outcome` | string | `refer_accepted`: el operador se hizo cargo de la llamada. `bridged`: ThunderPhone mantuvo conectados por sí mismo a quien llama y al destino, ya sea porque el operador rechazó una transferencia en frío o porque el agente de una transferencia asistida se retiró. `bridge_unanswered`: el operador rechazó una transferencia en frío y el destino no respondió la propia llamada de ThunderPhone. `failed`: la transferencia no se completó y la llamada terminó |
| `destination` | string \| null | El número al que se transfirió la llamada |
| `sip_status` | integer \| null | El estado SIP del operador cuando rechazó la transferencia en frío: `403` (por ejemplo, una troncal de Twilio sin transferencia de llamadas), `405` o `501`. `null` cuando el operador no la rechazó |
| `bridge_kind` | string \| null | Solo `bridged`: `cold_fallback` (una transferencia en frío rechazada que ThunderPhone conectó) o `warm` (las dos personas de una transferencia asistida después de que el agente se retiró) |
| `bridge_seconds` | integer \| null | Solo `bridged`: el tiempo del puente que ThunderPhone facturó, en segundos |
| `bridge_total_cents` | integer \| null | Solo `bridged`: el costo del puente en centavos de USD después de aplicar cualquier crédito de llamadas gratis, facturado por separado de `billing_total_cents` de la llamada |

Los mismos valores están en el [objeto de llamada](/api-reference/calls#call-object)
como `transfer_outcome`, `transfer_destination`, `transfer_sip_status`,
`transfer_bridge_kind`, `transfer_bridge_seconds` y
`transfer_bridge_total_cents`. `event_id` es el mismo en cada entrega
de una llamada, por lo que un informe repetido nunca genera un segundo evento.

---

## Eventos de SMS

Los eventos de SMS describen los mensajes enviados a través de la cuenta de
Twilio o Telnyx conectada de la organización. El cuerpo del mensaje se omite y
`to` se redacciona. Recupera el mensaje con alcance de organización desde
[`GET /v1/sms/{message_id}`](/api-reference/sms#retrieve-a-message) cuando se
necesite su contenido o destinatario.

### `sms.sent`

Se envía cuando el operador del cliente acepta una solicitud de SMS saliente.
No es un comprobante de entrega al dispositivo.

```json
{
  "data": {
    "accepted_at": "2026-09-19T15:42:10.113Z",
    "agent_id": 12,
    "call_id": 987654321,
    "created_at": "2026-09-19T15:42:09.901Z",
    "direction": "outbound",
    "error_code": null,
    "error_message": null,
    "from_number": "+15551234567",
    "id": "313a3117-827b-46e9-a01a-ac1493838724",
    "provider": "twilio",
    "provider_id": "SM0123456789abcdef0123456789abcdef",
    "segment_count": 1,
    "source": "agent_tool",
    "status": "accepted",
    "to": "[REDACTED]"
  },
  "event_id": "0ae347b4-1716-45bb-9a0e-b52373586e37",
  "type": "sms.sent"
}
```

### `sms.failed`

Se envía después de un rechazo permanente del operador, cuando se agotan los
reintentos transitorios o cuando el resultado es terminalmente desconocido.
`error_code` es un código estable de ThunderPhone y `error_message` indica la
acción del cliente cuando está disponible. `status: "unknown"` significa que
el operador pudo haber aceptado el SMS; concílialo en el registro del operador,
ya que ThunderPhone no lo reenviará automáticamente.

### `sms.received`

Reservado para la compatibilidad con SMS entrantes. No se emiten eventos
`sms.received` en la versión actual. La configuración de webhooks entrantes del
operador y la verificación de firmas se lanzarán por separado.

---

## Eventos de voz

La creación de voces personalizadas es asíncrona. Estos eventos no bloqueantes
te permiten reaccionar a un resultado final en lugar de consultar
periódicamente el [endpoint de detalles del clon](/api-reference/custom-voices#list-and-retrieve-clones).

`voice.ready` y `voice.failed` se entregan únicamente a un endpoint de toda la
organización con `events: []`. No pueden seleccionarse como filtros de eventos
explícitos.

### `voice.ready`

Se envía cuando una voz personalizada termina de procesarse y puede asignarse
a un agente.

```json
{
  "data": {
    "voice": {
      "created_at": "2026-07-30T14:12:08.317Z",
      "display_name": "Support voice",
      "failure_reason": "",
      "gender": "female",
      "id": "cv_2f6f90b0e9a34ee8b39be7d1",
      "language": "en",
      "name": "custom:cv_2f6f90b0e9a34ee8b39be7d1",
      "status": "ready",
      "updated_at": "2026-07-30T14:13:31.605Z"
    }
  },
  "event_id": "2d5f0a61-e9b5-4a3c-b684-29d7d9e4b214",
  "type": "voice.ready"
}
```

### `voice.failed`

Se envía cuando el procesamiento de una voz personalizada alcanza un fallo
permanente.

```json
{
  "data": {
    "reason": "audio sample could not be processed",
    "voice": {
      "created_at": "2026-07-30T14:12:08.317Z",
      "display_name": "Support voice",
      "failure_reason": "audio sample could not be processed",
      "gender": "female",
      "id": "cv_2f6f90b0e9a34ee8b39be7d1",
      "language": "en",
      "name": "custom:cv_2f6f90b0e9a34ee8b39be7d1",
      "status": "failed",
      "updated_at": "2026-07-30T14:13:31.605Z"
    }
  },
  "event_id": "3493e985-1a75-4f77-a10a-e74af440cd31",
  "type": "voice.failed"
}
```

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `voice.id` | cadena | ID público de la voz personalizada |
| `voice.name` | cadena | Valor de voz del agente con el formato `custom:<public_id>` |
| `voice.display_name` | cadena | Nombre de la voz visible para la organización |
| `voice.language` | cadena | Código de idioma único del clon |
| `voice.gender` | cadena | `male`, `female` o una cadena vacía |
| `voice.status` | cadena | `ready` para `voice.ready`; `failed` para `voice.failed` |
| `voice.failure_reason` | cadena | Vacío en caso de éxito; detalle del fallo de procesamiento en caso de error |
| `voice.created_at`, `voice.updated_at` | marca de tiempo | Marcas de tiempo ISO 8601 |
| `reason` | cadena | Detalle del fallo; presente solo en `voice.failed` |

---

## Eventos de calidad

### `call.graded`

Se envía cada vez que se completa una [ejecución de calificación con IA](/api-reference/calls#ai-call-grading)
para una llamada. No bloqueante.

```json
{
  "data": {
    "call_id": 987654321,
    "grade": {
      "call_outcome": "success",
      "created_at": "2026-04-20T18:25:11.002Z",
      "detected_issues": [],
      "graded_at": "2026-04-20T18:25:11.002Z",
      "grader_model": "heuristic-v1",
      "id": 5512,
      "score": 92,
      "status": "completed",
      "summary": "Caller asked about their policy and got a full answer…"
    },
    "report": null
  },
  "event_id": "7c1d2e3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
  "type": "call.graded"
}
```

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `agent_id` | entero \| nulo | Agente que gestionó la llamada, cuando se asignó uno |
| `agent_name` | cadena \| nulo | Agente que gestionó la llamada, cuando se asignó uno |
| `grade.id` | entero | ID de la calificación |
| `grade.score` | entero \| nulo | 0–100 |
| `grade.call_outcome` | cadena | `success`, `failure`, `unknown` o `no_conversation` |
| `grade.summary` | cadena | Resumen de un párrafo |
| `grade.detected_issues` | arreglo | Cadenas de problemas detectados por el evaluador |
| `grade.status` | cadena | Siempre `completed` — solo las ejecuciones finalizadas emiten eventos |
| `grade.grader_model` | cadena | Qué evaluador produjo el resultado (p. ej., `heuristic-v1`) |
| `grade.graded_at`, `grade.created_at` | marca de tiempo | |
| `report` | objeto \| nulo | El [informe de llamada](/api-reference/calls#call-report) orientado al cliente: etiqueta de resultado, indicador y motivo de atención necesaria, quien llama, resumen de una línea, experiencia de quien llama. En una calificación heurística inicial, es `null` o un informe `pending` cuyos campos son marcadores de posición; el evento de la calificación completa contiene el informe finalizado |

<Note>
  Una llamada puede calificarse más de una vez: una calificación heurística
  rápida suele ir seguida de una calificación completa del modelo cuando la
  grabación está disponible, y es posible recalificarla manualmente. Cada
  ejecución completada emite su propio evento `call.graded`; considera el
  `graded_at` más reciente como la fuente de autoridad.
</Note>

### `call.data_extracted`

Se envía cada vez que una extracción de datos estructurados se completa correctamente, incluido un
reintento tardío después de `telephony.complete` / `web.complete` o una nueva ejecución manual mediante
[`POST /v1/calls/{call_id}/extract`](/api-reference/calls#run-data-extraction).
No bloqueante.

En un modo de extracción bloqueante, el evento de finalización normalmente espera no más
que el presupuesto de extracción de 75 segundos. Si se pierde el proceso de trabajo de extracción,
el drenaje de finalización de producción (cada cinco minutos) libera una
finalización cuyo `blocking_deadline_at` haya vencido antes de iniciar otro
intento de extracción. Un éxito posterior se entrega por separado mediante este evento.

<Warning>
  Los valores extraídos son la interpretación de la conversación por parte de un modelo. Cada
  valor distinto de null incluye la cita, el rol de quien habla y el índice de turno en los que se basó,
  además de una marca de verificación a nivel de ejecución. Verifica el resultado antes de tomar decisiones
  relacionadas con dinero, identidad o acceso.
</Warning>

```json
{
  "data": {
    "call_id": 987654321,
    "agent_id": 12,
    "agent_name": "Acme intake",
    "extracted_data": {
      "status": "completed",
      "fields": {
        "customer_name": "Alex Morgan",
        "appointment_date": "2026-04-23"
      },
      "evidence": {
        "customer_name": {
          "quote": "My name is Alex Morgan",
          "speaker_role": "caller",
          "turn_index": 4
        },
        "appointment_date": {
          "quote": "April 23 works for me",
          "speaker_role": "caller",
          "turn_index": 7
        }
      },
      "verification": "verified",
      "field_reasons": {},
      "schema_version": "92850758e231a3c95a..."
    },
    "extracted_at": "2026-04-20T18:25:11.002Z",
    "model": "<model id>",
    "summary": "Alex Morgan called to book a follow-up visit and confirmed Thursday, April 23."
  },
  "event_id": "4d79ef1d-c2b1-4ed6-85b8-8326bd2895ef",
  "type": "call.data_extracted"
}
```

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `agent_id` | integer \| null | Agente que atendió la llamada, cuando se asignó uno |
| `agent_name` | string \| null | Agente que atendió la llamada, cuando se asignó uno |
| `summary` | string \| null | El [resumen de llamada](/es/webhooks/call-complete) completado cuando el agente tiene activada la opción **Generar un resumen de llamada**, por lo que un evento de extracción tardío también entrega un resumen que no alcanzó la ventana de finalización; de lo contrario, `null`. Los agentes con un resumen pero sin campos de extracción nunca emiten este evento: obtén un resumen tardío con `GET /v1/calls/{call_id}` |
| `extracted_data.status` | string | Siempre `completed` para este evento |
| `extracted_data.fields` | object | Valores indexados por las claves de campo de extracción configuradas; los valores no disponibles son `null` |
| `extracted_data.evidence` | object | Evidencia indexada por campo de extracción. Un valor distinto de null contiene la cita exacta validada estructuralmente (como máximo 1,000 caracteres), `speaker_role` (`caller` o `agent`) y `turn_index`; las citas más largas devueltas por el modelo se rechazan en lugar de truncarse, y la evidencia es `null` siempre que su campo sea `null` |
| `extracted_data.verification` | string | `verified` solo cuando la pasada de evidencia independiente devolvió exactamente un veredicto válido para cada campo candidato. `unavailable` significa que la pasada falló, agotó el tiempo, no tuvo presupuesto suficiente o devolvió resultados malformados o parciales. Una pasada completamente no disponible conserva los valores fundamentados estructuralmente para la revisión del cliente. Para resultados parciales, se aplican los veredictos válidos y cada candidato sin exactamente un veredicto válido se establece en null |
| `extracted_data.field_reasons` | object | Motivos indexados por los campos que se establecieron en null mediante fundamentación estructural o el verificador independiente |
| `extracted_data.schema_version` | string | Hash del esquema de campos exacto usado para esta extracción |
| `extracted_at` | timestamp | Hora de finalización en ISO 8601 |
| `model` | string | Modelo usado para la extracción |

### `campaign.completed`

Se envía una vez cuando una campaña pasa de `running` a `completed`, ya sea porque su
programación terminó o porque todos los contactos alcanzaron estados terminales. Los reintentos del ejecutor no
emiten otro evento. Este evento de ciclo de vida a nivel de organización se entrega solo
a endpoints con alcance de organización, no a endpoints con alcance de agente. No bloqueante.

```json
{
  "data": {
    "campaign_id": "3f6b2c9e-2a0d-4c63-b6d6-a708dc98f403",
    "name": "May win-back",
    "agent_id": 12,
    "status": "completed",
    "started_at": "2026-04-20T17:00:00Z",
    "completed_at": "2026-04-20T18:25:11Z",
    "counts": {
      "contacts_total": 150,
      "completed": 121,
      "failed": 11,
      "no_answer": 18,
      "remaining": 0
    }
  },
  "event_id": "8d8f52ce-6b46-423f-9dde-cea0b91ec135",
  "type": "campaign.completed"
}
```

Los cuatro conteos de resultados son mutuamente excluyentes y suman `contacts_total`:
`completed` contiene contactos exitosos; `no_answer` contiene contactos fallidos o agotados
en estado terminal cuyo resultado final fue sin respuesta; `failed` contiene
todos los demás contactos fallidos o agotados en estado terminal; y `remaining` contiene contactos pendientes,
programados o a los que se está llamando actualmente. Un contacto que espera un reintento es
`remaining`, incluso si su intento más reciente no recibió respuesta. Las llamadas en curso
se concilian antes de la instantánea única de finalización. `started_at` es el
inicio configurado de la campaña o la hora de creación de la campaña cuando no se configuró ningún inicio.

### `issue.reported`

Se envía cuando se crea un [reporte de problema](/api-reference/issue-reports):
ya sea presentado por un usuario desde el dashboard (`source: "user"`) o
automáticamente mediante la calificación de llamadas (`source: "system"`). No bloqueante.

```json
{
  "data": {
    "call_id": 987654321,
    "issue_report": {
      "created_at": "2026-04-20T18:25:11.002Z",
      "description": "Five-second silence before responding to the main question.",
      "id": 4321,
      "severity": "warning",
      "source": "system",
      "status": "open",
      "title": "Agent paused too long"
    }
  },
  "event_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
  "type": "issue.reported"
}
```

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `agent_id` | integer \| null | Agente que atendió la llamada, cuando se asignó uno |
| `agent_name` | string \| null | Agente que atendió la llamada, cuando se asignó uno |
| `issue_report.severity` | string | `critical`, `warning` o `info` |
| `issue_report.status` | string | `open` o `resolved` |
| `issue_report.source` | string | `user` (presentado desde el dashboard) o `system` (creado mediante calificación) |

<Note>
  Recalificar una llamada reconstruye sus reportes de problemas generados por el sistema,
  lo que vuelve a emitir `issue.reported` para los reportes recreados. Deduplica por
  `call_id` + `title` si solo quieres una notificación por cada
  problema subyacente.
</Note>

### `issue.escalated`

Se envía cuando se envía un patrón de problemas a ThunderPhone para revisión del personal: después de
**Reportar a ThunderPhone**, o cuando Corregir con IA no puede confirmar una solución del lado del cliente
y enruta el problema automáticamente. Solo los endpoints de toda la organización
reciben este evento.

```json
{
  "data": {
    "automatic": false,
    "cluster_id": "7ac2844c-2df0-4fa8-a560-7378da649e19",
    "escalation_id": "ec99f52b-c8c0-41dd-a4f2-dd8a07b10894",
    "status": "open"
  },
  "event_id": "d8f8f420-7a42-45ba-bcf1-b747f9bbecda",
  "type": "issue.escalated"
}
```

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `automatic` | boolean | `true` para una escalación automática; `false` para una escalación manual |
| `cluster_id` | UUID | Patrón de problemas escalado |
| `escalation_id` | UUID | Registro de escalación |
| `status` | string | `open` cuando se emite el evento |

Esta es una notificación, no el paquete de evidencia. Usa `cluster_id` para correlacionarla
con el patrón de problemas. Consulta [Reportar a ThunderPhone](/es/guides/issues).

---

## Eventos de llamadas de prueba

### `test-call.completed`

Se envía cuando una
[ejecución de llamada de prueba](/api-reference/test-calls#test-call-run-object)
alcanza un estado terminal: `completed` o `failed`, incluidas las ejecuciones
que fallaron al iniciarse y nunca generaron una llamada. No bloqueante. Útil
para integrar ejecuciones de CI por lotes con tus sistemas de chat/notificaciones.

```json
{
  "data": {
    "test_call_run": {
      "call_id": 987654321,
      "completed_at": "2026-04-20T18:25:04.822Z",
      "error_message": "",
      "id": 7110,
      "status": "completed",
      "target_id": 12,
      "target_type": "agent"
    }
  },
  "event_id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
  "type": "test-call.completed"
}
```

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `agent_id` | integer \| null | Agente que atendió la llamada, cuando se asignó uno |
| `agent_name` | string \| null | Agente que atendió la llamada, cuando se asignó uno |
| `test_call_run.target_type` | string | `agent` o `phone_number` |
| `test_call_run.target_id` | integer | El ID del agente o del número de teléfono al que se dirigió la ejecución, según `target_type` |
| `test_call_run.status` | string | `completed` o `failed` |
| `test_call_run.call_id` | integer \| null | `null` cuando la ejecución falló antes de realizar una llamada |
| `test_call_run.error_message` | string | Vacío si se realiza correctamente |

---

## Eventos de alertas

### `alert.triggered`

Se envía cuando una [regla de alerta](/es/guides/alerts) con el canal
**Enviar a webhooks de desarrollador** activado supera su umbral.
No bloqueante. Una regla se activa una vez y luego respeta su período
de espera, por lo que una infracción sostenida genera un evento por cada
ventana de período de espera.

```json
{
  "data": {
    "comparator": "lt",
    "event_id": "b8e6a1d4-2c3f-4a5b-9c8d-7e6f5a4b3c2d",
    "fired_at": "2026-04-20T18:00:00+00:00",
    "metric": "success_rate",
    "metric_value": 71.4,
    "rule_id": "d2c3b4a5-6f7e-4d8c-9b0a-1c2d3e4f5a6b",
    "rule_name": "Success rate below 80%",
    "threshold": 80.0,
    "window_hours": 24
  },
  "event_id": "4d5e6f7a-8b9c-4d0e-9f1a-2b3c4d5e6f7a",
  "type": "alert.triggered"
}
```

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `event_id` (en `data`) | UUID | El ID de **activación** de la alerta, distinto del `event_id` de entrega del sobre |
| `rule_id`, `rule_name` | UUID, string | La regla que se activó |
| `metric` | string | `success_rate`, `failure_rate`, `avg_score`, `call_volume` o `suite_regression` |
| `comparator` | string | `lt`, `lte`, `gt` o `gte` |
| `metric_value` | number | El valor de la métrica durante la ventana cuando se activó la regla |
| `threshold` | number | El umbral configurado |
| `window_hours` | integer | Ventana de evaluación retrospectiva |
| `fired_at` | timestamp | |

Consulta la [guía de alertas](/es/guides/alerts) para crear reglas, métricas,
períodos de espera y los canales de correo electrónico / Slack.

---

## Eventos de migración

### `migration.call.complete`

Se envía cuando finaliza una llamada realizada mediante
[`POST /v1/migrations/{id}/calls`](/api-reference/migration-calls),
sin importar qué plataforma la atendió. No bloquea. Llega hasta unos 5 minutos
después de que termina la llamada. `data` incluye todos los campos de
[`telephony.complete`](/es/webhooks/call-complete) más un bloque `migration`. Una
llamada realizada por ThunderPhone también envía `telephony.complete` como de costumbre, con el
mismo `call_id`. Una llamada en la otra plataforma que no haya terminado después de 2
horas, o cuya migración haya finalizado o perdido su clave primero, se envía con
`status: "unknown"` y sin transcripción.

```json
{
  "data": {
    "agent_id": 42,
    "agent_name": "Front desk",
    "billable_minutes": null,
    "billing_total_cents": null,
    "call_id": null,
    "direction": "outbound",
    "duration_seconds": 94,
    "end_reason": "user_hangup",
    "end_time": "2026-10-06T17:01:34+00:00",
    "extracted_data": null,
    "from_number": "+15551234567",
    "migration": {
      "arm": "vendor",
      "id": "5f0c2a9e-7b1d-4c3e-8a6f-2d9e1b4c7a30",
      "metadata": {
        "lead_id": "8812"
      },
      "routing_id": "9b2f8c1e-4d3a-4f6b-8e2d-1a7c5b9e0f34",
      "vendor": "retell",
      "vendor_call_id": "call_6a1f0e",
      "vendor_cost_usd": "0.1240"
    },
    "product": "",
    "recording_mode": "vendor",
    "recording_url": "https://…",
    "start_time": "2026-10-06T17:00:00+00:00",
    "status": "completed",
    "summary": "The callee booked a table for two on Thursday.",
    "thunderphone_retained_call_content": true,
    "to_number": "+14155552671",
    "transcripts": [
      {
        "content": "Hi Ada, this is Rosa's calling about your booking.",
        "role": "agent"
      },
      {
        "content": "Yes, Thursday works.",
        "role": "user"
      }
    ],
    "transfer_number": null,
    "unresolved_variables": [],
    "variables": {
      "first_name": "Ada"
    },
    "voice": ""
  },
  "event_id": "7c1e2f3a-4b5c-4d6e-8f7a-9b0c1d2e3f4a",
  "type": "migration.call.complete"
}
```

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `migration.id` | UUID | La migración |
| `migration.routing_id` | UUID | La llamada enrutada, tal como se devolvió cuando la realizaste |
| `migration.arm` | string | `ours` (ThunderPhone la realizó) o `vendor` (la otra plataforma la realizó) |
| `migration.vendor` | string | `retell`, `vapi`, `elevenlabs` o `bland` |
| `migration.vendor_call_id` | string \| null | El ID de llamada de la otra plataforma; `null` para `ours` |
| `migration.metadata` | object | Los `metadata` que enviaste al realizar la llamada |
| `migration.vendor_cost_usd` | string \| null | El costo de la llamada que informó la otra plataforma, cuando lo informó |
| `call_id` | integer \| null | El ID de llamada de ThunderPhone; `null` para `vendor` |
| `status` | string | Para `vendor`: `completed` cuando la persona llamada habló, `failed` cuando no lo hizo, `unknown` como se indicó anteriormente. Para `ours`: como en `telephony.complete` |

Para una llamada `vendor`, ThunderPhone no tiene datos de facturación ni de extracción:
`billable_minutes`, `billing_total_cents` y `extracted_data` son `null`,
`product` y `voice` están vacíos, `recording_mode` es `vendor` y
`recording_url` es el enlace de la otra plataforma, cuando proporcionó uno.

### `migration.rollback`

Se envía cuando el cambio gradual de una migración devuelve automáticamente todas las llamadas a la otra
plataforma: 3 llamadas no lograron conectarse a ThunderPhone en un plazo de 15
minutos, 2 llamadas enviadas a ThunderPhone terminaron antes de que el agente respondiera o
la tasa de resultados de tareas de ThunderPhone cayó más que la diferencia configurada por debajo de la
de la otra plataforma. No bloqueante, una vez por reversión. Después de esto, la proporción de
ThunderPhone es del 0 % y la migración queda pausada hasta que alguien la reanude en la pestaña Migración
del agente. La carga útil no incluye números de teléfono, transcripciones ni ID de llamadas.

```json
{
  "data": {
    "agent_id": 42,
    "agent_name": "Front desk",
    "dropped": 0,
    "migration_id": "5f0c2a9e-7b1d-4c3e-8a6f-2d9e1b4c7a30",
    "ours": {
      "graded": 20,
      "success_rate": 0.8
    },
    "ours_failed": 3,
    "ours_percent": 0,
    "previous_percent": 25,
    "reason": "ours_failed_burst",
    "rolled_back_at": "2026-10-10T12:05:00+00:00",
    "step": 1,
    "theirs": {
      "graded": 20,
      "success_rate": 0.8
    },
    "vendor": "retell"
  },
  "event_id": "3d1f6b2a-8c4e-5a7b-9d0e-2f1a3b4c5d6e",
  "type": "migration.rollback"
}
```

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `migration_id` | UUID | La migración |
| `vendor` | cadena | `retell`, `vapi`, `elevenlabs` o `bland` |
| `reason` | cadena | `ours_failed_burst` (3 conexiones fallidas en 15 minutos), `dropped_calls` (2 llamadas terminaron antes de que el agente respondiera, en un plazo de 30 minutos) o `rate_gap` (diferencia en la tasa de resultados de tareas en al menos `rollback_min_calls` llamadas evaluadas por cada lado) |
| `previous_percent` | entero | Proporción de llamadas de ThunderPhone antes de la reversión |
| `ours_percent` | entero | Siempre `0` |
| `step` | entero \| nulo | El paso de programación en el que estaba (basado en 0) |
| `ours` / `theirs` | objeto \| nulo | Llamadas evaluadas en el paso actual y su tasa de éxito; `theirs` puede corresponder a los últimos 30 días cuando el paso tiene muy pocas llamadas. `null` cuando no hubo ninguna |
| `ours_failed` | entero | Conexiones fallidas contabilizadas en los últimos 15 minutos |
| `dropped` | entero | Llamadas contabilizadas como abandonadas en los últimos 30 minutos |
| `rolled_back_at` | marca de tiempo | |

---

## Recursos relacionados

<CardGroup cols={2}>
  <Card title="telephony.incoming / web.incoming" icon="phone" href="/es/webhooks/call-incoming">
    La carga útil bloqueante de llamadas entrantes a la que debes responder.
  </Card>
  <Card title="telephony.complete / web.complete" icon="phone-slash" href="/es/webhooks/call-complete">
    Transcripción y métricas posteriores a la llamada.
  </Card>
  <Card title="Endpoints de webhooks" icon="bolt" href="/es/webhooks/endpoints">
    Suscribe una URL a un subconjunto de estos eventos.
  </Card>
  <Card title="Herramientas de funciones" icon="screwdriver-wrench" href="/es/tools/overview">
    Cómo se generan los eventos `telephony.tool` / `web.tool`.
  </Card>
</CardGroup>
