Passer à la navigation

Webhooks post-appel

Recevez une notification lorsque les appels se terminent et que l’analyse est terminée, grâce aux webhooks.

Vue d’ensemble

Les 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 vers le point de terminaison indiqué, 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 l’intégralité des données de conversation, y compris les transcriptions, les résultats d’analyse et les métadonnées
  • Webhooks audio (post_call_audio) : contiennent des données minimales avec l’audio encodé en base64 de l’intégralité de la conversation
  • Webhooks d’échec d’initialisation d’appel (call_initiation_failure) : contiennent des informations sur les tentatives d’initialisation 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 d’ElevenAgents.

Paramètres des webhooks post-appel

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 en cas d’au moins 10 échecs consécutifs et si la dernière livraison réussie remonte à plus de 7 jours, ou si aucune livraison n’a jamais abouti.

Les webhooks post-appel peuvent être relancés automatiquement en cas d’échec. Consultez les nouvelles tentatives de webhooks.

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.

Exemple de gestionnaire de webhook utilisant FastAPI :

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

En-têtes de requête personnalisés

En plus de la validation des signatures HMAC, vous pouvez inclure des en-têtes personnalisés à chaque livraison de webhook. Cela permet de transmettre les clés API, les jetons bearer ou les autres en-têtes d’authentification requis par votre point de terminaison de réception.

Les en-têtes personnalisés ne sont actuellement pas configurables depuis le Dashboard d’ElevenAgents. Utilisez le SDK ou l’API pour définir des en-têtes personnalisés.

Les en-têtes personnalisés fonctionnent avec l’authentification HMAC. auth_type doit toujours être défini sur hmac, et les en-têtes personnalisés sont envoyés en plus de l’en-tête ElevenLabs-Signature.

Les en-têtes personnalisés sont accessibles en écriture uniquement. Le point de terminaison de liste des webhooks ne renvoie pas request_headers dans sa réponse. Vous ne pouvez donc pas relire les en-têtes configurés pour un webhook.

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 la liste d’autorisation d’adresses IP pour obtenir la liste complète des adresses IP.

L’utilisation d’une liste d’autorisation d’adresses IP associée à la validation des signatures HMAC offre plusieurs niveaux de sécurité.

Remplacements de webhook par agent

Par défaut, tous les agents de votre Workspace utilisent le webhook configuré dans les paramètres de votre Workspace. Vous pouvez le remplacer pour chaque agent afin d’envoyer les données post-appel de différents agents vers différents points de terminaison.

1

Ouvrir les paramètres de l’agent

Accédez à votre agent dans le Dashboard ElevenAgents, puis ouvrez Paramètres.

2

Configurer le remplacement du webhook

Dans la section Webhooks post-appel, sélectionnez un autre webhook dans la liste déroulante afin de remplacer la configuration par défaut du Workspace pour cet agent. Vous pouvez également configurer les types d’événements à envoyer.

Paramètres de remplacement du webhook de l’agent

post_call_webhook_id doit référencer un webhook qui existe déjà dans votre Workspace. Vous pouvez également préciser les types d’événements à envoyer à l’aide du tableau events :

Type d’événementDescription
transcriptDonnées de transcription avec les détails et l’analyse de la conversation
audioAudio MP3 encodé en base64 de l’intégralité de la conversation
call_initiation_failureInformations sur les tentatives d’initialisation d’appel échouées
answering_machine_detectionRésultats de détection de répondeur (appels sortants uniquement)
unredacted_transcriptTranscription avec les données personnelles intactes (nécessite une conservation des données appropriée)
unredacted_audioAudio sans masquage des données personnelles (nécessite une conservation des données appropriée)

Structure des réponses de 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

ChampTypeDescription
typestringType d’événement (toujours post_call_transcription)
dataobjectDonnées de conversation utilisant la structure ConversationHistoryCommonModel
event_timestampnumberDate de survenue de cet événement au format Unix UTC

Structure de l’objet de données

L’objet data contient :

ChampTypeDescription
agent_idstringID de l’agent qui a traité l’appel
agent_namestringNom de l’agent au moment de la conversation
conversation_idstringIdentifiant unique de la conversation
statusstringStatut de la conversation (par exemple, “done”)
user_idstringIdentifiant de l’utilisateur, s’il est disponible
branch_idstringBranche de l’agent utilisée pour la conversation, le cas échéant
version_idstringID de la version de l’agent (instantané) active pendant l’appel
environmentstringEnvironnement utilisé pour résoudre les variables d’environnement
transcriptarrayTranscription complète de la conversation avec les tours de parole
metadataobjectHorodatage de l’appel, coûts et détails téléphoniques
analysisobjectRésultats de l’évaluation et résumé de la conversation
conversation_initiation_client_dataobjectRemplacements de configuration et variables dynamiques
has_audiobooleanIndique si un audio est disponible pour la conversation
has_user_audiobooleanIndique si l’audio de l’utilisateur est disponible pour la conversation
has_response_audiobooleanIndique 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’intégralité de l’audio de la conversation en MP3 encodé en base64.

Champs de premier niveau

ChampTypeDescription
typestringType d’événement (toujours post_call_audio)
dataobjectDonnées audio minimales
event_timestampnumberDate de survenue de cet événement au format Unix UTC

Structure de l’objet de données

L’objet data contient uniquement :

ChampTypeDescription
agent_idstringID de l’agent qui a traité l’appel
conversation_idstringIdentifiant unique de la conversation
full_audiostringChaîne encodée en base64 contenant l’intégralité de l’audio de la conversation au format MP3

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 tout autre détail de la conversation.

Webhooks d’échec d’initialisation d’appel (call_initiation_failure)

Contiennent des informations sur les tentatives d’initialisation d’appels téléphoniques, y compris les raisons de l’échec et les métadonnées du fournisseur de téléphonie.

Les événements de webhook d’échec d’initialisation d’appel sont envoyés lorsqu’un appel ne peut être initialisé 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 pris par un service automatisé, aucun webhook d’échec d’initialisation d’appel n’est envoyé, car l’appel a été initialisé avec succès.

Champs de premier niveau

ChampTypeDescription
typestringType d’événement (toujours call_initiation_failure)
dataobjectDonnées d’échec d’initialisation d’appel
event_timestampnumberDate de survenue de cet événement au format Unix UTC

Structure de l’objet de données

L’objet data contient :

ChampTypeDescription
agent_idstringID de l’agent affecté au traitement de l’appel
conversation_idstringIdentifiant unique de la conversation
failure_reasonstringRaison de l’échec (“busy”, “no-answer”, “unknown”)
metadataobjectDonné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 de Twilio : busy et no-answer sont transmis tels quels, et failed est signalé comme unknown. Les autres statuts d’appel Twilio n’envoient pas de webhook d’échec d’initialisation 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 une liaison SIP. L’objet inclut 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") :

ChampTypeObligatoireDescription
typestringOuiType de fournisseur (toujours sip)
bodyobjectOuiInformations sur l’échec d’appel propres à SIP

L’objet body des métadonnées SIP contient :

ChampTypeObligatoireDescription
from_numbernumberOuiNuméro de téléphone de la partie à l’origine de l’appel.
to_numbernumberOuiNuméro de téléphone de la partie appelée.
sip_status_codenumberOuiCode d’état de réponse SIP (par exemple, 486 pour occupé)
error_reasonstringOuiDescription d’erreur lisible par un humain
call_sidstringOuiIdentifiant de session d’appel SIP
twirp_codestringNonCode d’erreur Twirp, le cas échéant
sip_statusstringNonTexte d’état SIP correspondant au code d’état

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

ChampTypeObligatoireDescription
typestringOuiType de fournisseur (toujours twilio)
bodyobjectOuiCorps StatusCallback de Twilio contenant les détails de l’appel, documenté ici

Exemples de charges utiles de webhook

Exemple de webhook de transcription

{
"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

{
"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’initialisation d’appel

Exemple de métadonnées Twilio

{
"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

{
"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 livrés séparément des webhooks de transcription et contiennent uniquement les champs essentiels permettant d’identifier la conversation, ainsi que les données audio encodées en base64.

Les webhooks audio peuvent être activés ou désactivés à l’aide du bouton « Send audio data » dans les paramètres de votre 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 individuels.

Livraison en streaming

Les webhooks audio sont livré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 échouées sont relancées selon la même planification que les webhooks de transcription. Une nouvelle tentative renvoie l’intégralité de la charge utile audio. Dédupliquez donc à l’aide de conversation_id. Consultez les nouvelles tentatives de webhooks pour connaître la planification, les erreurs pouvant faire l’objet d’une nouvelle tentative et les limites de taille audio.

Traiter les webhooks audio

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

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

Les webhooks audio peuvent contenir des fichiers volumineux. Assurez-vous donc que votre point de terminaison de webhook peut gérer les requêtes en streaming et dispose d’une capacité de mémoire et de stockage suffisante. L’audio est livré au format MP3.

Cas d’usage

Suivis d’appel automatisés

Les webhooks post-appel vous permettent de concevoir des workflows automatisés qui se déclenchent immédiatement après la fin d’un appel. Voici quelques applications pratiques :

Intégration CRM

Mettez à jour votre système de gestion de la relation client avec les données de conversation dès la fin d’un appel :

// 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 la conversation entre plusieurs interactions en enregistrant et en récupérant son état :

  1. Lorsqu’un appel commence, transmettez votre ID utilisateur sous forme de variable dynamique.
  2. Lorsqu’un appel se termine, configurez votre point de terminaison de 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 de nouveau, vous pouvez récupérer ce contexte et le transmettre à la nouvelle conversation dans une variable dynamique {{previous_topics}}.
  4. L’agent peut ainsi « se souvenir » des interactions précédentes, pour une expérience fluide.
// 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(", "),
},
});
}