Passer à la navigation

Webhooks post-appel

Recevez des notifications via des webhooks lorsque les appels se terminent et que l’analyse est terminée.

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 à 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 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 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.

Les webhooks post-appel peuvent faire l’objet de nouvelles tentatives automatiques en cas d’échec. Consultez nouvelles tentatives de webhook .

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

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

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

ChampTypeDescription
typestringType d’événement (toujours post_call_transcription)
dataobjectDonnées de conversation utilisant la structure ConversationHistoryCommonModel
event_timestampnumberMoment où cet événement s’est produit, au format d’heure Unix UTC

Structure de l’objet de données

L’objet data contient :

ChampTypeDescription
agent_idstringID de l’agent qui a géré 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
metadataobjectHoraires 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’audio complet de la conversation au format MP3 encodé en base64.

Champs de premier niveau

ChampTypeDescription
typestringType d’événement (toujours post_call_audio)
dataobjectDonnées audio minimales
event_timestampnumberMoment 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 :

ChampTypeDescription
agent_idstringID de l’agent qui a géré l’appel
conversation_idstringIdentifiant unique de la conversation
full_audiostringChaîne encodée en base64 contenant l’audio complet 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 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.

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

ChampTypeDescription
typestringType d’événement (toujours call_initiation_failure)
dataobjectDonnées d’échec d’initiation d’appel
event_timestampnumberMoment où cet événement s’est produit, au format d’heure Unix UTC

Structure de l’objet de données

L’objet data contient :

ChampTypeDescription
agent_idstringID de l’agent affecté à la gestion 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 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") :

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 ayant initié l’appel.
to_numbernumberOuiNuméro de téléphone de la partie appelée.
sip_status_codenumberOuiCode d’état de la réponse SIP (par exemple, 486 pour occupé)
error_reasonstringOuiDescription de l’erreur lisible par un humain
call_sidstringOuiIdentifiant de session de l’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 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’initiation 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 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.

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

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

// 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.
// 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(", "),
},
});
}