> This is a page from the ElevenLabs documentation. For a complete page index, fetch https://elevenlabs.io/docs/llms.txt. For the full documentation in a single file, fetch https://elevenlabs.io/docs/llms-full.txt.

# Webhooks post-appel

## Vue d’ensemble

Les [webhooks](/docs/fr/eleven-api/resources/webhooks) post-appel vous permettent de recevoir des informations détaillées sur un appel une fois l’analyse terminée. Lorsqu’ils sont activés, ElevenLabs envoie une requête POST à votre point de terminaison spécifié avec des données complètes sur l’appel.

ElevenLabs prend en charge trois types de webhooks post-appel :

* **Webhooks de transcription** (`post_call_transcription`) : contiennent les données complètes de la conversation, y compris les transcriptions, les résultats d’analyse et les métadonnées
* **Webhooks audio** (`post_call_audio`) : contiennent un minimum de données, avec l’audio de l’intégralité de la conversation encodé en base64
* **Webhooks d’échec d’initiation d’appel** (`call_initiation_failure`) : contiennent des informations sur les tentatives d’initiation d’appel échouées, y compris les raisons de l’échec et les métadonnées

## Activer les webhooks post-appel

Les webhooks post-appel peuvent être activés pour tous les agents de votre Workspace depuis la [page des paramètres](https://elevenlabs.io/app/agents/settings) d’ElevenAgents.

![Paramètres des webhooks post-appel](/docs/_fern-files/_img/eb5d768612d6461a21bc3127611f60724be3e1a55005af43faf48f2d7bf23807.webp)

> **Warning**
>
> Les webhooks post-appel doivent renvoyer un code d’état 200 pour être considérés comme réussis. Les webhooks qui
> échouent de manière répétée sont automatiquement désactivés lorsqu’ils comptent au moins 10 échecs consécutifs et que la dernière
> livraison réussie remonte à plus de 7 jours, ou qu’aucune livraison n’a jamais réussi.

> **Note**
>
> Les webhooks post-appel peuvent faire l’objet de nouvelles tentatives automatiques en cas d’échec. Consultez [nouvelles tentatives de webhook ](/docs/fr/eleven-api/resources/webhooks#retries).

### Authentification

Il est important que le récepteur valide tous les webhooks entrants. Les webhooks prennent actuellement en charge l’authentification par signatures HMAC. Configurez l’authentification HMAC en :

* Stockant de manière sécurisée le secret partagé généré lors de la création du webhook
* Vérifiant l’en-tête ElevenLabs-Signature dans votre endpoint à l’aide du SDK

Le SDK JavaScript expose `constructEvent` ; le SDK Python expose `construct_event` avec **`rawBody`**, **`sig_header`** et **`secret`** (ils ne s’appellent pas `payload` / `signature` en Python). Les deux vérifient la signature, valident l’horodatage et analysent la charge utile JSON.

#### Python

Exemple de gestionnaire de webhook utilisant FastAPI :

```python
from dotenv import load_dotenv
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from elevenlabs.client import ElevenLabs
from elevenlabs.errors import BadRequestError
import os

load_dotenv()

app = FastAPI()
elevenlabs = ElevenLabs(
    api_key=os.getenv("ELEVENLABS_API_KEY"),
)

WEBHOOK_SECRET = os.getenv("WEBHOOK_SECRET")

@app.post("/webhook")
async def receive_message(request: Request):
    payload = await request.body()
    signature = request.headers.get("elevenlabs-signature")

    try:
        event = elevenlabs.webhooks.construct_event(
            rawBody=payload.decode("utf-8"),
            sig_header=signature,
            secret=WEBHOOK_SECRET,
        )
    except BadRequestError as e:
        return JSONResponse(content={"error": "Invalid signature"}, status_code=401)

    # construct_event returns a dict (parsed JSON), not an object with attributes
    if event.get("type") == "post_call_transcription":
        print(f"Received transcription: {event.get('data')}")

    return {"status": "received"}
```

#### JavaScript

#### Express

Exemple de gestionnaire de webhook utilisant Express :

```javascript
import { ElevenLabsClient } from '@elevenlabs/elevenlabs-js';
import express from 'express';

const app = express();

const elevenlabs = new ElevenLabsClient();
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

// Use express.text() to preserve raw body for signature verification
app.post('/webhook', express.text({ type: 'application/json' }), async (req, res) => {
  const signature = req.headers['elevenlabs-signature'];
  const payload = req.body; // Raw string body

  let event;
  try {
    event = await elevenlabs.webhooks.constructEvent(payload, signature, WEBHOOK_SECRET);
  } catch (error) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Process the webhook event
  if (event.type === 'post_call_transcription') {
    console.log('Received transcription:', event.data);
  }

  res.status(200).json({ received: true });
});
```

#### Next.js

Exemple de gestionnaire de webhook utilisant une route API Next.js :

**`app/api/webhook/route.ts`**

```typescript app/api/webhook/route.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { ElevenLabsClient } from '@elevenlabs/elevenlabs-js';

const elevenlabs = new ElevenLabsClient();
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

export async function POST(req: NextRequest) {
  const body = await req.text();
  const signature = req.headers.get('elevenlabs-signature');

  let event;
  try {
    event = await elevenlabs.webhooks.constructEvent(body, signature, WEBHOOK_SECRET);
  } catch (error) {
    return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
  }

  // Process the webhook event
  if (event.type === 'post_call_transcription') {
    console.log('Received transcription:', event.data);
  }

  return NextResponse.json({ received: true }, { status: 200 });
}
```

### Liste d’autorisation d’adresses IP

Pour renforcer la sécurité, vous pouvez ajouter les adresses IP de sortie statiques d’ElevenLabs à votre liste d’autorisation. Consultez [Liste d’autorisation d’adresses IP](/docs/fr/eleven-api/resources/ip-allowlisting) pour obtenir la liste complète des adresses IP.

> **Tip**
>
> L’utilisation d’une liste d’autorisation d’adresses IP combinée à la validation de signature HMAC offre plusieurs couches de
> sécurité.

## Structure des réponses webhook

ElevenLabs envoie trois types distincts de webhooks post-appel, chacun avec une structure de données différente :

### Webhooks de transcription (`post_call_transcription`)

Contiennent des données complètes sur la conversation, notamment les transcriptions intégrales, les résultats d'analyse et les métadonnées.

#### Champs de premier niveau

| Champ             | Type   | Description                                                                     |
| ----------------- | ------ | ------------------------------------------------------------------------------- |
| `type`            | string | Type d'événement (toujours `post_call_transcription`)                           |
| `data`            | object | Données de conversation utilisant la structure `ConversationHistoryCommonModel` |
| `event_timestamp` | number | Moment où cet événement s'est produit, au format d'heure Unix UTC               |

#### Structure de l'objet de données

L'objet `data` contient :

| Champ                                 | Type    | Description                                                                  |
| ------------------------------------- | ------- | ---------------------------------------------------------------------------- |
| `agent_id`                            | string  | ID de l'agent qui a géré l'appel                                             |
| `agent_name`                          | string  | Nom de l'agent au moment de la conversation                                  |
| `conversation_id`                     | string  | Identifiant unique de la conversation                                        |
| `status`                              | string  | Statut de la conversation (par exemple, "done")                              |
| `user_id`                             | string  | Identifiant de l'utilisateur, s'il est disponible                            |
| `branch_id`                           | string  | Branche de l'agent utilisée pour la conversation, le cas échéant             |
| `version_id`                          | string  | ID de la version de l'agent (instantané) active pendant l'appel              |
| `environment`                         | string  | Environnement utilisé pour résoudre les variables d'environnement            |
| `transcript`                          | array   | Transcription complète de la conversation avec les tours de parole           |
| `metadata`                            | object  | Horaires de l'appel, coûts et détails téléphoniques                          |
| `analysis`                            | object  | Résultats de l'évaluation et résumé de la conversation                       |
| `conversation_initiation_client_data` | object  | Remplacements de configuration et variables dynamiques                       |
| `has_audio`                           | boolean | Indique si un audio est disponible pour la conversation                      |
| `has_user_audio`                      | boolean | Indique si l'audio de l'utilisateur est disponible pour la conversation      |
| `has_response_audio`                  | boolean | Indique si l'audio de réponse de l'agent est disponible pour la conversation |

### Webhooks audio (`post_call_audio`)

Contiennent des données minimales avec l'audio complet de la conversation au format MP3 encodé en base64.

#### Champs de premier niveau

| Champ             | Type   | Description                                                       |
| ----------------- | ------ | ----------------------------------------------------------------- |
| `type`            | string | Type d'événement (toujours `post_call_audio`)                     |
| `data`            | object | Données audio minimales                                           |
| `event_timestamp` | number | Moment où cet événement s'est produit, au format d'heure Unix UTC |

#### Structure de l'objet de données

L'objet `data` contient uniquement :

| Champ             | Type   | Description                                                                         |
| ----------------- | ------ | ----------------------------------------------------------------------------------- |
| `agent_id`        | string | ID de l'agent qui a géré l'appel                                                    |
| `conversation_id` | string | Identifiant unique de la conversation                                               |
| `full_audio`      | string | Chaîne encodée en base64 contenant l'audio complet de la conversation au format MP3 |

> **Warning**
>
> Les webhooks audio contiennent uniquement les trois champs indiqués ci-dessus. Ils n'incluent PAS les données de transcription,
> les métadonnées, les résultats d'analyse ni aucun autre détail de la conversation.

### Webhooks d'échec d'initiation d'appel (`call_initiation_failure`)

Contiennent des informations sur les tentatives d'initiation d'appels téléphoniques, notamment les raisons de l'échec et les métadonnées du fournisseur de téléphonie.

> **Note**
>
> Les événements webhook d'échec d'initiation d'appel sont envoyés lorsqu'un appel ne peut pas être initié en raison d'erreurs de connexion,
> du refus de l'appel par l'utilisateur ou de l'absence de réponse de l'utilisateur. Si un appel est transféré vers une messagerie vocale ou est pris
> en charge par un service automatisé, aucun webhook d'échec d'initiation d'appel n'est envoyé, car l'appel a été
> initié avec succès.

#### Champs de premier niveau

| Champ             | Type   | Description                                                       |
| ----------------- | ------ | ----------------------------------------------------------------- |
| `type`            | string | Type d'événement (toujours `call_initiation_failure`)             |
| `data`            | object | Données d'échec d'initiation d'appel                              |
| `event_timestamp` | number | Moment où cet événement s'est produit, au format d'heure Unix UTC |

#### Structure de l'objet de données

L'objet `data` contient :

| Champ             | Type   | Description                                                        |
| ----------------- | ------ | ------------------------------------------------------------------ |
| `agent_id`        | string | ID de l'agent affecté à la gestion de l'appel                      |
| `conversation_id` | string | Identifiant unique de la conversation                              |
| `failure_reason`  | string | Raison de l'échec ("busy", "no-answer", "unknown")                 |
| `metadata`        | object | Données supplémentaires fournies par le fournisseur de téléphonie. |

Pour les appels sortants effectués via Twilio, `failure_reason` est dérivé du `CallStatus` Twilio : `busy` et `no-answer` sont transmis tels quels, tandis que `failed` est signalé comme `unknown`. Les autres statuts d'appel Twilio n'envoient pas de webhook d'échec d'initiation d'appel.

#### Structure de l'objet de métadonnées

La structure de l'objet `metadata` varie selon que l'appel sortant a été effectué via Twilio ou via la jonction SIP. L'objet comprend un champ `type` qui distingue les deux cas, ainsi qu'un champ `body` contenant les détails propres au fournisseur.

**Métadonnées SIP** (`type: "sip"`) :

| Champ  | Type   | Obligatoire | Description                                    |
| ------ | ------ | ----------- | ---------------------------------------------- |
| `type` | string | Oui         | Type de fournisseur (toujours `sip`)           |
| `body` | object | Oui         | Informations sur l'échec d'appel propres à SIP |

L'objet `body` des métadonnées SIP contient :

| Champ             | Type   | Obligatoire | Description                                                                                           |
| ----------------- | ------ | ----------- | ----------------------------------------------------------------------------------------------------- |
| `from_number`     | number | Oui         | Numéro de téléphone de la partie ayant initié l'appel.                                                |
| `to_number`       | number | Oui         | Numéro de téléphone de la partie appelée.                                                             |
| `sip_status_code` | number | Oui         | Code d'état de la réponse SIP (par exemple, 486 pour occupé)                                          |
| `error_reason`    | string | Oui         | Description de l'erreur lisible par un humain                                                         |
| `call_sid`        | string | Oui         | Identifiant de session de l'appel SIP                                                                 |
| `twirp_code`      | string | Non         | [Code d'erreur Twirp](https://twitchtv.github.io/twirp/docs/spec_v7.html#error-codes), le cas échéant |
| `sip_status`      | string | Non         | Texte d'état SIP correspondant au code d'état                                                         |

**Métadonnées Twilio** (`type: "twilio"`) :

| Champ  | Type   | Obligatoire | Description                                                                                                                                          |
| ------ | ------ | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type` | string | Oui         | Type de fournisseur (toujours `twilio`)                                                                                                              |
| `body` | object | Oui         | Corps StatusCallback de Twilio contenant les détails de l'appel, documenté [ici](https://www.twilio.com/docs/voice/api/call-resource#statuscallback) |

## Exemples de charges utiles webhook

### Exemple de webhook de transcription

```json
{
  "type": "post_call_transcription",
  "event_timestamp": 1739537297,
  "data": {
    "agent_id": "xyz",
    "conversation_id": "abc",
    "status": "done",
    "user_id": "user123",
    "transcript": [
      {
        "role": "agent",
        "message": "Hey there angelo. How are you?",
        "tool_calls": null,
        "tool_results": null,
        "feedback": null,
        "time_in_call_secs": 0,
        "conversation_turn_metrics": null
      },
      {
        "role": "user",
        "message": "Hey, can you tell me, like, a fun fact about 11 Labs?",
        "tool_calls": null,
        "tool_results": null,
        "feedback": null,
        "time_in_call_secs": 2,
        "conversation_turn_metrics": null
      },
      {
        "role": "agent",
        "message": "I do not have access to fun facts about Eleven Labs. However, I can share some general information about the company. Eleven Labs is an AI voice technology platform that specializes in voice cloning and text-to-speech...",
        "tool_calls": null,
        "tool_results": null,
        "feedback": null,
        "time_in_call_secs": 9,
        "conversation_turn_metrics": {
          "convai_llm_service_ttfb": {
            "elapsed_time": 0.3704247010173276
          },
          "convai_llm_service_ttf_sentence": {
            "elapsed_time": 0.5551181449554861
          }
        }
      }
    ],
    "metadata": {
      "start_time_unix_secs": 1739537297,
      "call_duration_secs": 22,
      "cost": 296,
      "deletion_settings": {
        "deletion_time_unix_secs": 1802609320,
        "deleted_logs_at_time_unix_secs": null,
        "deleted_audio_at_time_unix_secs": null,
        "deleted_transcript_at_time_unix_secs": null,
        "delete_transcript_and_pii": true,
        "delete_audio": true
      },
      "feedback": {
        "overall_score": null,
        "likes": 0,
        "dislikes": 0
      },
      "authorization_method": "authorization_header",
      "charging": {
        "dev_discount": true
      },
      "termination_reason": ""
    },
    "analysis": {
      "evaluation_criteria_results": {},
      "data_collection_results": {},
      "call_successful": "success",
      "transcript_summary": "The conversation begins with the agent asking how Angelo is, but Angelo redirects the conversation by requesting a fun fact about 11 Labs. The agent acknowledges they don't have specific fun facts about Eleven Labs but offers to provide general information about the company. They briefly describe Eleven Labs as an AI voice technology platform specializing in voice cloning and text-to-speech technology. The conversation is brief and informational, with the agent adapting to the user's request despite not having the exact information asked for."
    },
    "conversation_initiation_client_data": {
      "conversation_config_override": {
        "agent": {
          "prompt": null,
          "first_message": null,
          "language": "en"
        },
        "tts": {
          "voice_id": null
        }
      },
      "custom_llm_extra_body": {},
      "dynamic_variables": {
        "user_name": "angelo"
      },
      "branch_id": null,
      "environment": null
    }
  }
}
```

### Exemple de webhook audio

```json
{
  "type": "post_call_audio",
  "event_timestamp": 1739537319,
  "data": {
    "agent_id": "xyz",
    "conversation_id": "abc",
    "full_audio": "SUQzBAAAAAAA...base64_encoded_mp3_data...AAAAAAAAAA=="
  }
}
```

### Exemples de webhooks d'échec d'initiation d'appel

#### Exemple de métadonnées Twilio

```json
{
  "type": "call_initiation_failure",
  "event_timestamp": 1759931652,
  "data": {
    "agent_id": "xyz",
    "conversation_id": "abc",
    "failure_reason": "busy",
    "metadata": {
      "type": "twilio",
      "body": {
        "Called": "+441111111111",
        "ToState": "",
        "CallerCountry": "US",
        "Direction": "outbound-api",
        "Timestamp": "Wed, 08 Oct 2025 13:54:12 +0000",
        "CallbackSource": "call-progress-events",
        "SipResponseCode": "487",
        "CallerState": "WA",
        "ToZip": "",
        "SequenceNumber": "2",
        "CallSid": "CA8367245817625617832576245724",
        "To": "+441111111111",
        "CallerZip": "98631",
        "ToCountry": "GB",
        "CalledZip": "",
        "ApiVersion": "2010-04-01",
        "CalledCity": "",
        "CallStatus": "busy",
        "Duration": "0",
        "From": "+11111111111",
        "CallDuration": "0",
        "AccountSid": "AC37682153267845716245762454a",
        "CalledCountry": "GB",
        "CallerCity": "RAYMOND",
        "ToCity": "",
        "FromCountry": "US",
        "Caller": "+11111111111",
        "FromCity": "RAYMOND",
        "CalledState": "",
        "FromZip": "12345",
        "FromState": "WA"
      }
    }
  }
}
```

#### Exemple de métadonnées SIP

```json
{
  "type": "call_initiation_failure",
  "event_timestamp": 1759931652,
  "data": {
    "agent_id": "xyz",
    "conversation_id": "abc",
    "failure_reason": "busy",
    "metadata": {
      "type": "sip",
      "body": {
        "from_number": "+441111111111",
        "to_number": "+11111111111",
        "sip_status_code": 486,
        "error_reason": "INVITE failed: sip status: 486: Busy here (SIP 486)",
        "call_sid": "d8e7f6a5-b4c3-4d5e-8f9a-0b1c2d3e4f5a",
        "sip_status": "Busy here",
        "twirp_code": "unavailable"
      }
    }
  }
}
```

## Livraison des webhooks audio

Les webhooks audio sont envoyés séparément des webhooks de transcription et contiennent uniquement les champs essentiels pour identifier la conversation, ainsi que les données audio encodées en base64.

> **Note**
>
> Les webhooks audio peuvent être activés ou désactivés à l'aide du bouton « Send audio data » dans vos paramètres de webhook.
> Ce paramètre peut être configuré au niveau du Workspace (dans les paramètres d'ElevenAgents)
> et au niveau de l'agent (dans les remplacements de webhook de chaque agent).

### Livraison en streaming

Les webhooks audio sont envoyés sous forme de requêtes HTTP en streaming avec l'en-tête `transfer-encoding: chunked`, afin de gérer efficacement les fichiers audio volumineux. Chaque requête expire après 5 minutes.

### Nouvelles tentatives

Lorsque les nouvelles tentatives sont activées sur le webhook, les livraisons audio ayant échoué sont réessayées selon le même calendrier que les webhooks de transcription. Une nouvelle tentative renvoie la charge utile audio complète ; dédupliquez donc selon `conversation_id`. Consultez les [nouvelles tentatives de webhook](/docs/fr/eleven-api/resources/webhooks#retries) pour connaître le calendrier, les erreurs pouvant faire l'objet d'une nouvelle tentative et les limites de taille des fichiers audio.

### Traitement des webhooks audio

Les webhooks audio étant envoyés via un encodage de transfert par blocs, vous devez gérer correctement les données en streaming :

```python

import base64
import json
from aiohttp import web

async def handle_webhook(request):

    # Check if this is a chunked/streaming request
    if request.headers.get("transfer-encoding", "").lower() == "chunked":
        # Read streaming data in chunks
        chunked_body = bytearray()
        while True:
            chunk = await request.content.read(8192)  # 8KB chunks
            if not chunk:
                break
            chunked_body.extend(chunk)

        # Parse the complete payload
        request_body = json.loads(chunked_body.decode("utf-8"))
    else:
        # Handle regular requests
        body_bytes = await request.read()
        request_body = json.loads(body_bytes.decode('utf-8'))

    # Process different webhook types
    if request_body["type"] == "post_call_transcription":
        # Handle transcription webhook with full conversation data
        handle_transcription_webhook(request_body["data"])
    elif request_body["type"] == "post_call_audio":
        # Handle audio webhook with minimal data
        handle_audio_webhook(request_body["data"])
    elif request_body["type"] == "call_initiation_failure":
        # Handle call initiation failure webhook
        handle_call_initiation_failure_webhook(request_body["data"])

    return web.json_response({"status": "ok"})

def handle_audio_webhook(data):
    # Decode base64 audio data
    audio_bytes = base64.b64decode(data["full_audio"])

    # Save or process the audio file
    conversation_id = data["conversation_id"]
    with open(f"conversation_{conversation_id}.mp3", "wb") as f:
        f.write(audio_bytes)

def handle_call_initiation_failure_webhook(data):
    # Handle call initiation failure events
    agent_id = data["agent_id"]
    conversation_id = data["conversation_id"]
    failure_reason = data.get("failure_reason")
    metadata = data.get("metadata", {})

    # Log the failure for monitoring
    print(f"Call failed for agent {agent_id}, conversation {conversation_id}")
    print(f"Failure reason: {failure_reason}")

    # Access provider-specific metadata
    provider_type = metadata.get("type")
    body = metadata.get("body", {})
    if provider_type == "sip":
        print(f"SIP status code: {body.get('sip_status_code')}")
        print(f"Error reason: {body.get('error_reason')}")
    elif provider_type == "twilio":
        print(f"Twilio CallSid: {body.get('CallSid')}")
        print(f"Call status: {body.get('CallStatus')}")

    # Update your system with the failure information
    # e.g., mark lead as "call_failed" in CRM

```

```javascript
import fs from "fs";

app.post("/webhook/elevenlabs", (req, res) => {
  let body = "";

  // Handle chunked/streaming requests
  req.on("data", (chunk) => {
    body += chunk;
  });

  req.on("end", () => {
    try {
      const requestBody = JSON.parse(body);

      // Process different webhook types
      if (requestBody.type === "post_call_transcription") {
        // Handle transcription webhook with full conversation data
        handleTranscriptionWebhook(requestBody.data);
      } else if (requestBody.type === "post_call_audio") {
        // Handle audio webhook with minimal data
        handleAudioWebhook(requestBody.data);
      } else if (requestBody.type === "call_initiation_failure") {
        // Handle call initiation failure webhook
        handleCallFailureWebhook(requestBody.data);
      }

      res.status(200).json({ status: "ok" });
    } catch (error) {
      console.error("Error processing webhook:", error);
      res.status(400).json({ error: "Invalid JSON" });
    }
  });
});

function handleAudioWebhook(data) {
  // Decode base64 audio data
  const audioBytes = Buffer.from(data.full_audio, "base64");

  // Save or process the audio file
  const conversationId = data.conversation_id;
  fs.writeFileSync(`conversation_${conversationId}.mp3`, audioBytes);
}

function handleCallFailureWebhook(data) {
  // Handle call initiation failure events
  const { agent_id, conversation_id, failure_reason, metadata } = data;

  // Log the failure for monitoring
  console.log(`Call failed for agent ${agent_id}, conversation ${conversation_id}`);
  console.log(`Failure reason: ${failure_reason}`);

  // Access provider-specific metadata
  const body = metadata.body || {};
  if (metadata?.type === "sip") {
    console.log(`SIP status code: ${body.sip_status_code}`);
    console.log(`Error reason: ${body.error_reason}`);
  } else if (metadata?.type === "twilio") {
    console.log(`Twilio CallSid: ${body.CallSid}`);
    console.log(`Call status: ${body.CallStatus}`);
  }

  // Update your system with the failure information
  // e.g., mark lead as "call_failed" in CRM
}
```

> **Note**
>
> Les webhooks audio peuvent contenir des fichiers volumineux. Assurez-vous donc que votre endpoint webhook peut gérer les requêtes en streaming
> et dispose de suffisamment de mémoire et de capacité de stockage. L'audio est envoyé au format MP3.

## Cas d'utilisation

### Suivis d'appel automatisés

Les webhooks post-appel vous permettent de concevoir des workflows automatisés qui se déclenchent dès la fin d'un appel. Voici quelques applications concrètes :

#### Intégration CRM

Mettez à jour votre système de gestion de la relation client avec les données de conversation dès qu'un appel se termine :

```javascript
// Example webhook handler
app.post("/webhook/elevenlabs", async (req, res) => {
  // HMAC validation code

  const { data } = req.body;

  // Extract key information
  const userId = data.metadata.user_id;
  const transcriptSummary = data.analysis.transcript_summary;
  const callSuccessful = data.analysis.call_successful;

  // Update CRM record
  await updateCustomerRecord(userId, {
    lastInteraction: new Date(),
    conversationSummary: transcriptSummary,
    callOutcome: callSuccessful,
    fullTranscript: data.transcript,
  });

  res.status(200).send("Webhook received");
});
```

### Conversations avec état

Conservez le contexte de conversation entre plusieurs interactions en enregistrant et en récupérant l'état :

1. Lorsqu'un appel commence, transmettez votre ID utilisateur comme variable dynamique.
2. Lorsqu'un appel se termine, configurez votre endpoint webhook pour enregistrer les données de conversation dans votre base de données, à partir de l'ID utilisateur extrait de `dynamic_variables`.
3. Lorsque l'utilisateur appelle à nouveau, vous pouvez récupérer ce contexte et le transmettre à la nouvelle conversation dans une variable dynamique `{{previous_topics}}`.
4. Cela crée une expérience fluide dans laquelle l'agent « se souvient » des interactions précédentes.

```javascript
// Store conversation state when call ends
app.post("/webhook/elevenlabs", async (req, res) => {
  // HMAC validation code

  const { data } = req.body;
  const userId = data.metadata.user_id;

  // Store conversation state
  await db.userStates.upsert({
    userId,
    lastConversationId: data.conversation_id,
    lastInteractionTimestamp: data.metadata.start_time_unix_secs,
    conversationHistory: data.transcript,
    previousTopics: extractTopics(data.analysis.transcript_summary),
  });

  res.status(200).send("Webhook received");
});

// When initiating a new call, retrieve and use the state
async function initiateCall(userId) {
  // Get user's conversation state
  const userState = await db.userStates.findOne({ userId });

  // Start new conversation with context from previous calls
  return await elevenlabs.startConversation({
    agent_id: "xyz",
    conversation_id: generateNewId(),
    dynamic_variables: {
      user_name: userState.name,
      previous_conversation_id: userState.lastConversationId,
      previous_topics: userState.previousTopics.join(", "),
    },
  });
}
```