---
title: "Catalogue des événements"
description: "Tous les types d’événements webhook émis par ThunderPhone."
---

Chaque corps de webhook contient un champ `type` dont la valeur correspond à l’un des types d’événements de cette page. Lorsque vous vous abonnez à un [endpoint](/fr/webhooks/endpoints), le tableau `events` doit contenir les types d’événements souhaités (ou être vide pour vous abonner à tous les événements — sauf les événements par tour [`telephony.turn`](#telephony-turn) / [`web.turn`](#web-turn), qui sont envoyés uniquement aux endpoints qui les indiquent explicitement).

Ces événements sont transmis selon deux modes :

* Les **livraisons d'endpoint** sont toujours des notifications **non bloquantes** avec des [nouvelles tentatives](/fr/webhooks/overview) : répondez avec n'importe quel code 2xx ; l'enveloppe contient un `event_id` pour la déduplication.
* Les échanges **bloquants** s'exécutent uniquement sur le [webhook monourl hérité](/fr/webhooks/overview) : la requête de configuration [`telephony.incoming` / `web.incoming`](/fr/webhooks/call-incoming) (numéros en mode webhook : 13 s, dont une nouvelle tentative ; clés de widget en mode webhook : 10 s) et la [répartition d'outil](/fr/tools/overview) en mode webhook. Votre réponse façonne l'appel en direct.

Les exemples de payload ci-dessous présentent l’enveloppe d’endpoint dans son ordre sur le réseau (clés triées par ordre alphabétique : `data`, `event_id`, `type`) ; les livraisons historiques contiennent les mêmes `data` sans `event_id`.

## Événements d'appel

### `telephony.incoming`

Envoyé lorsqu'un appel entrant atteint l'un de vos
[numéros de téléphone](/api-reference/phone-numbers). Les livraisons aux endpoints sont
des notifications fire-and-forget envoyées pour **chaque** appel entrant, que
le numéro soit configuré avec un agent ou avec un webhook. Les numéros sans
agent attribué reçoivent également la requête de configuration **bloquante**
sur le webhook hérité — consultez
[`telephony.incoming` / `web.incoming`](/fr/webhooks/call-incoming) pour
le schéma complet de requête / réponse.

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

| Champ | Type | Description |
|-------|------|-------------|
| `agent_id` | integer \| null | Agent qui a traité l'appel, lorsqu'un agent était attribué |
| `agent_name` | string \| null | Agent qui a traité l'appel, lorsqu'un agent était attribué |

### `telephony.complete`

Envoyé lorsqu'un appel téléphonique entrant ou sortant se termine. Non bloquant.
Inclut la transcription, l'URL de l'enregistrement lorsqu'elle est disponible, ainsi que le récapitulatif de facturation. Consultez
[`telephony.complete` / `web.complete`](/fr/webhooks/call-complete) pour
le schéma de la charge utile.

| Champ | Type | Description |
|-------|------|-------------|
| `agent_id` | integer \| null | Agent qui a traité l'appel, lorsqu'un agent était attribué |
| `agent_name` | string \| null | Agent qui a traité l'appel, lorsqu'un agent était attribué |

### `telephony.tool`

Envoyé après qu'un appel téléphonique a invoqué un
[outil de fonction](/fr/tools/overview). Notification d'audit non bloquante —
l'outil a déjà été exécuté lorsque cet événement est livré ; il couvre
vos propres outils de fonction et l'outil intégré `send_sms` (mais pas les autres outils intégrés,
outils de base de connaissances, de connexion d'application ou MCP). Pour `send_sms`,
`arguments.to` et `arguments.body` sont tous deux `[REDACTED]`. Les transcriptions
de fin d'appel les masquent également ; récupérez le contenu et le destinataire depuis la
ressource SMS à l'échelle de l'organisation à l'aide du `message_id` de la réponse.

```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` correspond au résultat exécuté : `{"status": <http status>,
"response": <your endpoint's JSON>}` en cas de réussite, ou
`{"status": <status>, "error": "<message>"}` en cas d'échec.

| Champ | Type | Description |
|-------|------|-------------|
| `agent_id` | integer \| null | Agent qui a traité l'appel, lorsqu'un agent était attribué |
| `agent_name` | string \| null | Agent qui a traité l'appel, lorsqu'un agent était attribué |

### `telephony.turn`

Envoyé pendant qu'un appel téléphonique est **en cours**, une fois pour chaque
tour contenant de la parole, au moment où il se produit — les réponses orales de
l'agent et les tours transcrits de l'appelant. Permet de suivre la conversation
en direct via de simples webhooks au lieu d'interroger
[`GET /v1/calls/{call_id}/transcript`](/api-reference/calls#get-transcript).
Non bloquant.

<Note>
  **Abonnement explicite requis.** Contrairement à tous les autres types d'événements,
  les événements de tour ne sont jamais livrés via un tableau `events` vide
  (abonnement à tous). Un endpoint les reçoit uniquement lorsque sa liste
  `events` contient explicitement `telephony.turn` / `web.turn`.
</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"
}
```

| Champ | Type | Description |
|-------|------|-------------|
| `agent_id` | integer \| null | Agent qui a traité l'appel, lorsqu'un agent était attribué |
| `agent_name` | string \| null | Agent qui a traité l'appel, lorsqu'un agent était attribué |
| `position` | integer | Index du tour dans l'historique de l'appel — identifiant stable pour l'ordonnancement |
| `role` | string | `assistant` (parole de l'agent) ou `user` (parole de l'appelant) |
| `text` | string | Texte transcrit du tour tel qu'il est connu au moment de l'émission |
| `entry_type` | string | Type d'entrée d'historique sous-jacent : `completion` (agent), ou `user_turn` / `span` (appelant) |
| `start_ms`, `end_ms` | integer | Décalages audio en ms depuis le début de l'appel ; présents uniquement lorsque le timing de lecture était déjà connu au moment de l'émission |

<Note>
  La livraison est effectuée au moins une fois et les tours peuvent arriver **dans le désordre** :
  dédupliquez à l'aide de l'`event_id` de l'enveloppe et ordonnez selon `position`. Les
  transcriptions de l'appelant peuvent encore être affinées après leur émission ; la transcription
  [`telephony.complete`](/fr/webhooks/call-complete) après l'appel fait autorité. Seuls les
  tours parlés sont émis — les appels d'outils, chuchotements et autres entrées internes
  n'apparaissent jamais dans ce flux.
</Note>

### `web.incoming`

Équivalent sur le canal web de `telephony.incoming`, envoyé lorsqu'une session de
[widget web](/fr/widget/overview) ou un appel de test du micro du builder
démarre. Les livraisons aux endpoints sont fire-and-forget pour chaque session web.
Les clés publiables en `mode="webhook"` reçoivent également la requête de
configuration **bloquante** sur le webhook hérité — cette requête bloquante
a une structure différente (`origin_domain`,
`publishable_key_prefix` ; sans numéros de téléphone). Consultez
[`telephony.incoming` / `web.incoming`](/fr/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` est toujours la valeur littérale `"web"`. Pour les sessions de widget
en mode webhook, `to_number` est vide (le numéro de l'agent de la session est attribué
après la configuration) ; pour les appels de test du micro du builder, `origin_domain` et
`publishable_key_prefix` sont vides.

| Champ | Type | Description |
|-------|------|-------------|
| `agent_id` | integer \| null | Agent qui a traité l'appel, lorsqu'un agent était attribué |
| `agent_name` | string \| null | Agent qui a traité l'appel, lorsqu'un agent était attribué |

### `web.complete`

Équivalent sur le canal web de `telephony.complete`, couvrant les appels de widget
web (`direction: "web"`) et les appels de test du micro du builder
(`direction: "test"`). Non bloquant. Même structure de charge utile que
[`telephony.complete`](/fr/webhooks/call-complete), avec `origin_domain` en plus,
et `from_number` défini sur `"web"`.

| Champ | Type | Description |
|-------|------|-------------|
| `agent_id` | integer \| null | Agent qui a traité l'appel, lorsqu'un agent était attribué |
| `agent_name` | string \| null | Agent qui a traité l'appel, lorsqu'un agent était attribué |

<Note>
  Sur le webhook hérité à URL unique, les appels de test du micro du builder
  étaient historiquement signalés comme `telephony.complete` — seuls les appels avec `direction:
  "web"` y utilisent le type `web.complete`. Le système d'endpoints
  associe les appels web et de test à `web.*`. Les charges utiles historiques peuvent
  contenir les valeurs héritées `widget` ou `mic` pour `direction`.
</Note>

### `web.tool`

Équivalent sur le canal web de `telephony.tool`. Les données `data` contiennent
`origin_domain` au lieu de `from_number` / `to_number`.

| Champ | Type | Description |
|-------|------|-------------|
| `agent_id` | integer \| null | Agent qui a traité l'appel, lorsqu'un agent était attribué |
| `agent_name` | string \| null | Agent qui a traité l'appel, lorsqu'un agent était attribué |

### `web.turn`

Équivalent sur le canal web de [`telephony.turn`](#telephony-turn),
couvrant les appels de widget web et les appels de test du micro du builder. Même structure
de charge utile, avec `origin_domain` au lieu de `from_number` / `to_number`.
Comme `telephony.turn`, il nécessite un **abonnement explicite** — il
n'est jamais livré via un tableau `events` vide.

| Champ | Type | Description |
|-------|------|-------------|
| `agent_id` | integer \| null | Agent qui a traité l'appel, lorsqu'un agent était attribué |
| `agent_name` | string \| null | Agent qui a traité l'appel, lorsqu'un agent était attribué |

### `call.transfer_completed`

Envoyé une fois par appel avec le résultat final d’un
[transfert d’appel](/fr/guides/call-transfers) : un transfert à froid ou le
pont entre personnes qu’un transfert à chaud laisse après le retrait de l’agent.
Non bloquant et envoyé uniquement aux endpoints (jamais au webhook historique
à URL unique). Il arrive après le `telephony.complete` de l’appel :
lorsque l’opérateur prend en charge l’appel ou que le transfert échoue, ou,
lorsque ThunderPhone a mis l’appel en pont, lorsque le pont se termine.

```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"
}
```

| Champ | Type | Description |
|-------|------|-------------|
| `call_id` | integer | L’appel qui a été transféré |
| `agent_id` | integer \| null | Agent qui a traité l’appel, lorsqu’un agent était attribué |
| `agent_name` | string \| null | Agent qui a traité l’appel, lorsqu’un agent était attribué |
| `transfer_outcome` | string | `refer_accepted` : l’opérateur a pris en charge l’appel. `bridged` : ThunderPhone a lui-même maintenu la connexion entre l’appelant et la destination, soit parce que l’opérateur a refusé un transfert à froid, soit parce que l’agent d’un transfert à chaud s’est retiré. `bridge_unanswered` : l’opérateur a refusé un transfert à froid et la destination n’a pas répondu à l’appel émis par ThunderPhone. `failed` : le transfert n’a pas abouti et l’appel s’est terminé |
| `destination` | string \| null | Le numéro vers lequel l’appel a été transféré |
| `sip_status` | integer \| null | Statut SIP de l’opérateur lorsqu’il a refusé le transfert à froid : `403` (par exemple, un trunk Twilio sans transfert d’appel), `405` ou `501`. `null` lorsque l’opérateur ne l’a pas refusé |
| `bridge_kind` | string \| null | Uniquement pour `bridged` : `cold_fallback` (un transfert à froid refusé que ThunderPhone a connecté) ou `warm` (les deux personnes d’un transfert à chaud après le retrait de l’agent) |
| `bridge_seconds` | integer \| null | Uniquement pour `bridged` : durée du pont facturée par ThunderPhone, en secondes |
| `bridge_total_cents` | integer \| null | Uniquement pour `bridged` : coût du pont en cents USD après déduction de tout crédit d’appel gratuit, facturé séparément du `billing_total_cents` de l’appel |

Les mêmes valeurs figurent sur l’[objet d’appel](/api-reference/calls#call-object)
sous les noms `transfer_outcome`, `transfer_destination`, `transfer_sip_status`,
`transfer_bridge_kind`, `transfer_bridge_seconds` et
`transfer_bridge_total_cents`. `event_id` est identique pour chaque livraison
d’un appel ; un rapport répété ne produit donc jamais un second événement.

---

## Événements SMS

Les événements SMS décrivent les messages envoyés via le compte Twilio ou
Telnyx connecté de l'organisation. Le corps du message est omis et `to` est
masqué. Récupérez le message limité à l'organisation depuis
[`GET /v1/sms/{message_id}`](/api-reference/sms#retrieve-a-message) lorsque son
contenu ou son destinataire est nécessaire.

### `sms.sent`

Envoyé lorsque l'opérateur du client accepte une demande d'envoi de SMS. Il ne
s'agit pas d'un accusé de réception sur le téléphone.

```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`

Envoyé après un rejet permanent par l'opérateur, après épuisement des nouvelles
tentatives temporaires, ou lorsque le résultat est définitivement inconnu.
`error_code` est un code ThunderPhone stable et `error_message` indique
l'action à effectuer par le client lorsqu'elle est disponible.
`status: "unknown"` signifie que l'opérateur peut avoir accepté le SMS ;
vérifiez-le dans le journal de l'opérateur, car ThunderPhone ne le renverra pas
automatiquement.

### `sms.received`

Réservé à la prise en charge des SMS entrants. Aucun événement `sms.received`
n'est émis dans la version actuelle. La configuration des webhooks entrants de
l'opérateur et la vérification des signatures seront publiées séparément.

---

## Événements vocaux

La création de voix personnalisées est asynchrone. Ces événements non bloquants
vous permettent de réagir à un résultat final au lieu d'interroger le point de
terminaison des [détails du clone](/api-reference/custom-voices#list-and-retrieve-clones).

`voice.ready` et `voice.failed` sont envoyés uniquement à un point de
terminaison global à l'organisation avec `events: []`. Ils ne peuvent pas être
sélectionnés comme filtres d'événements explicites.

### `voice.ready`

Envoyé lorsqu'une voix personnalisée termine son traitement et peut être
attribuée à un agent.

```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`

Envoyé lorsque le traitement d'une voix personnalisée atteint un échec
permanent.

```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"
}
```

| Champ | Type | Description |
|-------|------|-------------|
| `voice.id` | chaîne | Identifiant public de la voix personnalisée |
| `voice.name` | chaîne | Valeur de voix de l'agent au format `custom:<public_id>` |
| `voice.display_name` | chaîne | Nom de la voix visible par l'organisation |
| `voice.language` | chaîne | Code de langue unique du clone |
| `voice.gender` | chaîne | `male`, `female` ou une chaîne vide |
| `voice.status` | chaîne | `ready` pour `voice.ready` ; `failed` pour `voice.failed` |
| `voice.failure_reason` | chaîne | Vide en cas de réussite ; détail de l'échec de traitement en cas d'échec |
| `voice.created_at`, `voice.updated_at` | horodatage | Horodatages ISO 8601 |
| `reason` | chaîne | Détail de l'échec ; présent uniquement sur `voice.failed` |

---

## Événements de qualité

### `call.graded`

Envoyé chaque fois qu’une [exécution d’évaluation par IA](/api-reference/calls#ai-call-grading)
est terminée pour un appel. Non bloquant.

```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"
}
```

| Champ | Type | Description |
|-------|------|-------------|
| `agent_id` | integer \| null | Agent ayant traité l'appel, lorsqu'un agent était assigné |
| `agent_name` | string \| null | Agent ayant traité l'appel, lorsqu'un agent était assigné |
| `grade.id` | integer | Identifiant de l'évaluation |
| `grade.score` | integer \| null | 0–100 |
| `grade.call_outcome` | string | `success`, `failure`, `unknown` ou `no_conversation` |
| `grade.summary` | string | Résumé en un paragraphe |
| `grade.detected_issues` | array | Chaînes décrivant les problèmes détectés par l'évaluateur |
| `grade.status` | string | Toujours `completed` — seuls les traitements terminés émettent un événement |
| `grade.grader_model` | string | Évaluateur ayant produit le résultat (par ex. `heuristic-v1`) |
| `grade.graded_at`, `grade.created_at` | timestamp | |
| `report` | object \| null | [Rapport d'appel](/api-reference/calls#call-report) destiné au client : libellé du résultat, indicateur et motif nécessitant une attention, appelant, résumé sur une ligne, expérience de l'appelant. Lors d'une évaluation heuristique précoce, il est `null` ou correspond à un rapport `pending` dont les champs sont des espaces réservés ; l'événement de l'évaluation complète contient le rapport finalisé |

<Note>
  Un appel peut être évalué plusieurs fois — une évaluation heuristique rapide est
  souvent suivie d’une évaluation complète par modèle une fois l’enregistrement
  disponible, et des réévaluations manuelles sont possibles. Chaque exécution
  terminée émet son propre événement `call.graded` ; considérez le `graded_at` le
  plus récent comme faisant autorité.
</Note>

### `call.data_extracted`

Envoyé chaque fois qu’une extraction de données structurées se termine avec succès, y compris
lors d’une nouvelle tentative tardive après `telephony.complete` / `web.complete` ou d’une nouvelle exécution manuelle via
[`POST /v1/calls/{call_id}/extract`](/api-reference/calls#run-data-extraction).
Non bloquant.

Dans un mode d’extraction bloquant, l’événement de fin attend normalement au maximum
le budget d’extraction de 75 secondes. Si le processus worker d’extraction est
perdu, le traitement de finalisation de production (toutes les cinq minutes) libère un
événement de fin dont le `blocking_deadline_at` est dépassé avant de lancer une autre
tentative d’extraction. Un succès ultérieur est envoyé séparément sous la forme de cet événement.

<Warning>
  Les valeurs extraites sont l’interprétation de la conversation par un modèle. Chaque
  valeur non nulle inclut la citation, le rôle du locuteur et l’index du tour sur lesquels
  elle repose, ainsi qu’un indicateur de vérification au niveau de l’exécution. Vérifiez le résultat
  avant toute décision impliquant de l’argent, une identité ou un accès.
</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"
}
```

| Champ | Type | Description |
|-------|------|-------------|
| `agent_id` | integer \| null | Agent qui a traité l’appel, lorsqu’un agent était attribué |
| `agent_name` | string \| null | Agent qui a traité l’appel, lorsqu’un agent était attribué |
| `summary` | string \| null | Le [résumé d’appel](/fr/webhooks/call-complete) terminé lorsque l’agent a activé **Générer un résumé d’appel**, afin qu’un événement d’extraction tardif fournisse également un résumé qui n’a pas été envoyé pendant la fenêtre de fin ; `null` sinon. Les agents disposant d’un résumé mais d’aucun champ d’extraction n’émettent jamais cet événement : récupérez un résumé tardif avec `GET /v1/calls/{call_id}` |
| `extracted_data.status` | string | Toujours `completed` pour cet événement |
| `extracted_data.fields` | object | Valeurs indexées par les clés de champ d’extraction configurées ; les valeurs indisponibles sont `null` |
| `extracted_data.evidence` | object | Preuves indexées par champ d’extraction. Une valeur non nulle contient la citation exacte vérifiée structurellement (au plus 1 000 caractères), le `speaker_role` (`caller` ou `agent`) et le `turn_index` ; les citations plus longues renvoyées par le modèle sont rejetées plutôt que tronquées, et la preuve est `null` chaque fois que son champ est `null` |
| `extracted_data.verification` | string | `verified` uniquement lorsque le passage indépendant de vérification des preuves a renvoyé exactement un verdict valide pour chaque champ candidat. `unavailable` signifie que le passage a échoué, a expiré, ne disposait pas d’un budget suffisant ou a renvoyé une sortie mal formée ou partielle. Un passage entièrement indisponible conserve les valeurs fondées structurellement pour examen par le client. Pour une sortie partielle, les verdicts valides sont appliqués et chaque candidat sans exactement un verdict valide est défini sur null |
| `extracted_data.field_reasons` | object | Raisons indexées par les champs définis sur null par l’ancrage structurel ou le vérificateur indépendant |
| `extracted_data.schema_version` | string | Hachage du schéma de champ exact utilisé pour cette extraction |
| `extracted_at` | timestamp | Heure de fin au format ISO 8601 |
| `model` | string | Modèle utilisé pour l’extraction |

### `campaign.completed`

Envoyé une fois lorsqu’une campagne passe de `running` à `completed`, que son
planning soit terminé ou que tous les contacts aient atteint un état terminal. Les nouvelles tentatives de l’exécuteur
n’émettent pas d’autre événement. Cet événement de cycle de vie au niveau de l’organisation est envoyé uniquement
aux points de terminaison à portée organisationnelle, et non aux points de terminaison à portée agent. Non bloquant.

```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"
}
```

Les quatre décomptes de résultats sont disjoints et leur somme est égale à `contacts_total` :
`completed` contient les contacts réussis ; `no_answer` contient les contacts en échec/épuisés
terminaux dont le résultat final était une absence de réponse ; `failed` contient
tous les autres contacts en échec/épuisés terminaux ; et `remaining` contient les contacts
en attente, planifiés ou en cours d’appel. Un contact en attente d’une nouvelle tentative est
`remaining`, même lorsque sa tentative la plus récente n’a reçu aucune réponse. Les appels en cours
sont réconciliés avant l’instantané de fin émis une seule fois. `started_at` correspond au
début de campagne configuré, ou à l’heure de création de la campagne lorsqu’aucun début n’a été configuré.

### `issue.reported`

Envoyé lorsqu’un [signalement de problème](/api-reference/issue-reports) est créé —
soit par un utilisateur depuis le tableau de bord (`source: "user"`), soit
automatiquement par l’évaluation d’appel (`source: "system"`). Non bloquant.

```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"
}
```

| Champ | Type | Description |
|-------|------|-------------|
| `agent_id` | integer \| null | Agent qui a traité l’appel, lorsqu’un agent était attribué |
| `agent_name` | string \| null | Agent qui a traité l’appel, lorsqu’un agent était attribué |
| `issue_report.severity` | string | `critical`, `warning` ou `info` |
| `issue_report.status` | string | `open` ou `resolved` |
| `issue_report.source` | string | `user` (signalé depuis le tableau de bord) ou `system` (créé par l’évaluation) |

<Note>
  La réévaluation d’un appel recrée ses signalements de problèmes générés par le système, ce qui
  réémet `issue.reported` pour les signalements recréés. Dédupliquez selon
  `call_id` + `title` si vous ne souhaitez recevoir qu’une notification par problème sous-jacent.
</Note>

### `issue.escalated`

Envoyé lorsqu’un modèle de problème est transmis à ThunderPhone pour examen par le personnel : après
**Signaler à ThunderPhone**, ou lorsque Corriger avec l’IA ne peut pas confirmer une correction côté client
et achemine automatiquement le problème. Seuls les points de terminaison à portée organisationnelle
reçoivent cet événement.

```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"
}
```

| Champ | Type | Description |
|-------|------|-------------|
| `automatic` | boolean | `true` pour une escalade automatique ; `false` pour une escalade manuelle |
| `cluster_id` | UUID | Modèle de problème escaladé |
| `escalation_id` | UUID | Enregistrement d’escalade |
| `status` | string | `open` lorsque l’événement est émis |

Il s’agit d’une notification, pas du lot de preuves. Utilisez `cluster_id` pour le corréler
au modèle de problème. Consultez [Signaler à ThunderPhone](/fr/guides/issues).

---

## Événements d'appels de test

### `test-call.completed`

Envoyé lorsqu'une
[exécution d'appel de test](/api-reference/test-calls#test-call-run-object)
atteint un statut terminal — `completed` ou `failed`, y compris les exécutions
qui ont échoué au lancement et n'ont jamais produit d'appel. Non bloquant. Utile
pour intégrer des exécutions CI par lot à vos systèmes de chat/notifications.

```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"
}
```

| Champ | Type | Description |
|-------|------|-------------|
| `agent_id` | integer \| null | Agent ayant traité l'appel, lorsqu'un agent était attribué |
| `agent_name` | string \| null | Agent ayant traité l'appel, lorsqu'un agent était attribué |
| `test_call_run.target_type` | string | `agent` ou `phone_number` |
| `test_call_run.target_id` | integer | ID de l'agent ou du numéro de téléphone ciblé par l'exécution, correspondant à `target_type` |
| `test_call_run.status` | string | `completed` ou `failed` |
| `test_call_run.call_id` | integer \| null | `null` lorsque l'exécution a échoué avant qu'un appel soit lancé |
| `test_call_run.error_message` | string | Vide en cas de réussite |

---

## Événements d'alerte

### `alert.triggered`

Envoyé lorsqu'une [règle d'alerte](/fr/guides/alerts) dont le canal **Envoyer aux
webhooks développeur** est activé franchit son seuil.
Non bloquant. Une règle se déclenche une fois, puis respecte son délai de
récupération ; un dépassement continu produit donc un événement par fenêtre de
délai de récupération.

```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"
}
```

| Champ | Type | Description |
|-------|------|-------------|
| `event_id` (dans `data`) | UUID | ID de **déclenchement** de l'alerte — distinct de l'`event_id` de livraison de l'enveloppe |
| `rule_id`, `rule_name` | UUID, string | Règle qui s'est déclenchée |
| `metric` | string | `success_rate`, `failure_rate`, `avg_score`, `call_volume` ou `suite_regression` |
| `comparator` | string | `lt`, `lte`, `gt` ou `gte` |
| `metric_value` | number | Valeur de la métrique sur la fenêtre lorsque la règle s'est déclenchée |
| `threshold` | number | Seuil configuré |
| `window_hours` | integer | Fenêtre d'évaluation glissante |
| `fired_at` | timestamp | |

Consultez le [guide des alertes](/fr/guides/alerts) pour créer des règles, des métriques,
des délais de récupération et les canaux e-mail / Slack.

---

## Événements de migration

### `migration.call.complete`

Envoyé lorsqu'un appel passé via
[`POST /v1/migrations/{id}/calls`](/api-reference/migration-calls) se termine,
quelle que soit la plateforme qui l'a pris en charge. Non bloquant. Il arrive jusqu'à environ 5 minutes
après la fin de l'appel. `data` contient tous les champs de
[`telephony.complete`](/fr/webhooks/call-complete), ainsi qu'un bloc `migration`. Un
appel passé par ThunderPhone envoie également `telephony.complete` comme d'habitude, avec le
même `call_id`. Un appel sur l'autre plateforme qui ne s'est pas terminé après 2
heures, ou dont la migration s'est terminée ou a perdu sa clé en premier, est envoyé avec
`status: "unknown"` et sans transcription.

```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"
}
```

| Champ | Type | Description |
|-------|------|-------------|
| `migration.id` | UUID | La migration |
| `migration.routing_id` | UUID | L'appel routé, tel qu'il a été renvoyé lorsque vous l'avez passé |
| `migration.arm` | string | `ours` (ThunderPhone l'a passé) ou `vendor` (l'autre plateforme l'a passé) |
| `migration.vendor` | string | `retell`, `vapi`, `elevenlabs` ou `bland` |
| `migration.vendor_call_id` | string \| null | L'identifiant d'appel de l'autre plateforme ; `null` pour `ours` |
| `migration.metadata` | object | Les `metadata` que vous avez envoyées lors du passage de l'appel |
| `migration.vendor_cost_usd` | string \| null | Le coût de l'appel signalé par l'autre plateforme, lorsqu'elle l'a signalé |
| `call_id` | integer \| null | L'identifiant d'appel ThunderPhone ; `null` pour `vendor` |
| `status` | string | Pour `vendor` : `completed` lorsque le destinataire a parlé, `failed` lorsqu'il n'a pas parlé, `unknown` comme ci-dessus. Pour `ours` : comme dans `telephony.complete` |

Pour un appel `vendor`, ThunderPhone ne dispose d'aucune donnée de facturation ou d'extraction :
`billable_minutes`, `billing_total_cents` et `extracted_data` sont `null`,
`product` et `voice` sont vides, `recording_mode` est `vendor`, et
`recording_url` est le lien de l'autre plateforme, lorsqu'elle en a fourni un.

### `migration.rollback`

Envoyé lorsqu'un basculement progressif de migration renvoie automatiquement tous les appels vers l'autre plateforme : 3 appels n'ont pas réussi à se connecter à ThunderPhone en 15 minutes, 2 appels envoyés à ThunderPhone se sont terminés avant que l'agent ne réponde, ou le taux de réussite des tâches de ThunderPhone est tombé à un niveau inférieur de plus que l'écart configuré à celui de l'autre plateforme. Non bloquant, une fois par annulation. Après cela, la part de ThunderPhone est de 0 % et la migration est suspendue jusqu'à ce qu'une personne la reprenne dans l'onglet Migration de l'agent. La charge utile ne contient aucun numéro de téléphone, transcription ni identifiant d'appel.

```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"
}
```

| Champ | Type | Description |
|-------|------|-------------|
| `migration_id` | UUID | La migration |
| `vendor` | chaîne | `retell`, `vapi`, `elevenlabs` ou `bland` |
| `reason` | chaîne | `ours_failed_burst` (3 échecs de connexion en 15 minutes), `dropped_calls` (2 appels terminés avant que l'agent ne réponde, en 30 minutes) ou `rate_gap` (écart de taux de réussite des tâches sur au moins `rollback_min_calls` appels évalués de chaque côté) |
| `previous_percent` | entier | Part des appels de ThunderPhone avant l'annulation |
| `ours_percent` | entier | Toujours `0` |
| `step` | entier \| null | Étape de planification en cours (indexée à partir de 0) |
| `ours` / `theirs` | objet \| null | Appels évalués à l'étape en cours et leur taux de réussite ; `theirs` peut couvrir les 30 derniers jours lorsque l'étape comporte trop peu d'appels. `null` s'il n'y en avait aucun |
| `ours_failed` | entier | Échecs de connexion comptabilisés au cours des 15 dernières minutes |
| `dropped` | entier | Appels comptabilisés comme abandonnés au cours des 30 dernières minutes |
| `rolled_back_at` | horodatage | |

---

## Contenu associé

<CardGroup cols={2}>
  <Card title="telephony.incoming / web.incoming" icon="phone" href="/fr/webhooks/call-incoming">
    Charge utile bloquante des appels entrants à laquelle vous devez répondre.
  </Card>
  <Card title="telephony.complete / web.complete" icon="phone-slash" href="/fr/webhooks/call-complete">
    Transcription et métriques après appel.
  </Card>
  <Card title="Points de terminaison de webhook" icon="bolt" href="/fr/webhooks/endpoints">
    Abonnez une URL à un sous-ensemble de ces événements.
  </Card>
  <Card title="Outils de fonction" icon="screwdriver-wrench" href="/fr/tools/overview">
    Comment les événements `telephony.tool` / `web.tool` sont générés.
  </Card>
</CardGroup>
