Webhooks nach dem Anruf

Erhalten Sie über Webhooks eine Benachrichtigung, wenn Anrufe enden und die Analyse abgeschlossen ist.

Überblick

Webhooks nach dem Anruf ermöglichen es Ihnen, nach Abschluss der Analyse detaillierte Informationen über einen Anruf zu erhalten. Wenn diese Funktion aktiviert ist, sendet ElevenLabs eine POST-Anfrage mit umfassenden Anrufdaten an Ihren angegebenen Endpunkt.

ElevenLabs unterstützt drei Arten von Webhooks nach dem Anruf:

  • Transkriptions-Webhooks (post_call_transcription): Enthalten vollständige Gesprächsdaten einschließlich Transkripten, Analyseergebnissen und Metadaten
  • Audio-Webhooks (post_call_audio): Enthalten minimale Daten mit Base64-kodiertem Audio des vollständigen Gesprächs
  • Webhooks bei fehlgeschlagener Anrufinitiierung (call_initiation_failure): Enthalten Informationen über fehlgeschlagene Versuche zur Anrufinitiierung, einschließlich Fehlergründen und Metadaten

Webhooks nach dem Anruf aktivieren

Webhooks nach dem Anruf können für alle Agenten in Ihrem Workspace über die Einstellungsseite von ElevenAgents aktiviert werden.

Webhook-Einstellungen nach dem Anruf

Webhooks nach dem Anruf müssen einen Statuscode 200 zurückgeben, um als erfolgreich zu gelten. Webhooks, die wiederholt fehlschlagen, werden automatisch deaktiviert, wenn es 10 oder mehr aufeinanderfolgende Fehler gibt und die letzte erfolgreiche Zustellung mehr als 7 Tage zurückliegt oder noch nie eine erfolgreiche Zustellung erfolgt ist.

Webhooks nach dem Anruf können bei einem Fehler automatisch wiederholt werden. Siehe Webhook- Wiederholungen.

Authentifizierung

Der Listener muss alle eingehenden Webhooks validieren. Webhooks unterstützen derzeit die Authentifizierung über HMAC-Signaturen. So richten Sie die HMAC-Authentifizierung ein:

  • Speichern Sie das beim Erstellen des Webhooks generierte gemeinsame Geheimnis sicher.
  • Verifizieren Sie den Header ElevenLabs-Signature in Ihrem Endpunkt mithilfe des SDK.

Das JavaScript-SDK stellt constructEvent bereit, das Python-SDK construct_event mit rawBody, sig_header und secret (diese heißen in Python nicht payload / signature). Beide verifizieren die Signatur, validieren den Zeitstempel und parsen die JSON-Nutzlast.

Beispiel für einen Webhook-Handler mit 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"}

IP-Allowlisting

Für zusätzliche Sicherheit können Sie die statischen ausgehenden IP-Adressen von ElevenLabs zu Ihrer Allowlist hinzufügen. Die vollständige Liste der IP-Adressen finden Sie unter IP-Allowlisting.

Die Kombination aus IP-Allowlisting und HMAC-Signaturvalidierung bietet mehrere Sicherheitsebenen.

Struktur der Webhook-Antwort

ElevenLabs sendet drei verschiedene Arten von Webhooks nach dem Anruf, jeweils mit unterschiedlichen Datenstrukturen:

Transkriptions-Webhooks (post_call_transcription)

Enthält umfassende Gesprächsdaten, einschließlich vollständiger Transkripte, Analyseergebnisse und Metadaten.

Felder auf oberster Ebene

FeldTypBeschreibung
typestringEreignistyp (immer post_call_transcription)
dataobjectGesprächsdaten in der Struktur ConversationHistoryCommonModel
event_timestampnumberZeitpunkt dieses Ereignisses als Unix-Zeit in UTC

Struktur des Datenobjekts

Das data-Objekt enthält:

FeldTypBeschreibung
agent_idstringDie ID des Agenten, der den Anruf bearbeitet hat
agent_namestringDer Name des Agenten zum Zeitpunkt des Gesprächs
conversation_idstringEindeutige Kennung des Gesprächs
statusstringStatus des Gesprächs, z. B. “done”
user_idstringBenutzerkennung, sofern verfügbar
branch_idstringDer für das Gespräch verwendete Agent-Branch, falls zutreffend
version_idstringDie ID der Agent-Version (Snapshot), die während des Anrufs aktiv war
environmentstringDie Umgebung zum Auflösen von Umgebungsvariablen
transcriptarrayVollständiges Gesprächstranskript mit Gesprächsbeiträgen
metadataobjectAnrufzeiten, Kosten und Telefonnummerdetails
analysisobjectAuswertungsergebnisse und Gesprächszusammenfassung
conversation_initiation_client_dataobjectKonfigurationsüberschreibungen und dynamische Variablen
has_audiobooleanOb Audio für das Gespräch verfügbar ist
has_user_audiobooleanOb Benutzeraudio für das Gespräch verfügbar ist
has_response_audiobooleanOb die Audioantwort des Agenten für das Gespräch verfügbar ist

Audio-Webhooks (post_call_audio)

Enthält minimale Daten sowie das vollständige Gesprächsaudio als Base64-codiertes MP3.

Felder auf oberster Ebene

FeldTypBeschreibung
typestringEreignistyp (immer post_call_audio)
dataobjectMinimale Audiodaten
event_timestampnumberZeitpunkt dieses Ereignisses als Unix-Zeit in UTC

Struktur des Datenobjekts

Das data-Objekt enthält nur:

FeldTypBeschreibung
agent_idstringDie ID des Agenten, der den Anruf bearbeitet hat
conversation_idstringEindeutige Kennung des Gesprächs
full_audiostringBase64-codierter String mit dem vollständigen Gesprächsaudio im MP3-Format

Audio-Webhooks enthalten nur die drei oben aufgeführten Felder. Sie enthalten KEINE Transkriptdaten, Metadaten, Analyseergebnisse oder andere Gesprächsdetails.

Webhooks bei fehlgeschlagener Anrufinitiierung (call_initiation_failure)

Enthält Informationen über Versuche zur Initiierung von Telefonanrufen, einschließlich Fehlergründen und Metadaten des Telefonieanbieters.

Webhook-Ereignisse für fehlgeschlagene Anrufinitiierungen werden gesendet, wenn ein Anruf aufgrund von Verbindungsfehlern, einer Ablehnung durch den Benutzer oder weil der Benutzer nicht abnimmt, nicht initiiert werden kann. Wenn ein Anruf an die Mailbox geht oder von einem automatisierten Dienst angenommen wird, wird kein Webhook für eine fehlgeschlagene Anrufinitiierung gesendet, da der Anruf erfolgreich initiiert wurde.

Felder auf oberster Ebene

FeldTypBeschreibung
typestringEreignistyp (immer call_initiation_failure)
dataobjectDaten zur fehlgeschlagenen Anrufinitiierung
event_timestampnumberZeitpunkt dieses Ereignisses als Unix-Zeit in UTC

Struktur des Datenobjekts

Das data-Objekt enthält:

FeldTypBeschreibung
agent_idstringDie ID des Agenten, der für den Anruf vorgesehen war
conversation_idstringEindeutige Kennung des Gesprächs
failure_reasonstringDer Fehlergrund (“busy”, “no-answer”, “unknown”)
metadataobjectZusätzliche Daten des Telefonieanbieters

Struktur des Metadatenobjekts

Die Struktur des metadata-Objekts variiert je nachdem, ob der ausgehende Anruf über Twilio oder SIP-Trunking erfolgte. Das Objekt enthält ein type-Feld zur Unterscheidung beider Varianten sowie ein body-Feld mit anbieterspezifischen Details.

SIP-Metadaten (type: "sip"):

FeldTypErforderlichBeschreibung
typestringJaAnbietertyp (immer sip)
bodyobjectJaSIP-spezifische Informationen zum Anruffehlerschlag

Das body-Objekt für SIP-Metadaten enthält:

FeldTypErforderlichBeschreibung
from_numbernumberJaDie Telefonnummer der Partei, die den Anruf initiiert hat.
to_numbernumberJaDie Telefonnummer der angerufenen Partei.
sip_status_codenumberJaSIP-Antwortstatuscode, z. B. 486 für besetzt
error_reasonstringJaFür Menschen lesbare Fehlerbeschreibung
call_sidstringJaSitzungskennung des SIP-Anrufs
twirp_codestringNeinTwirp-Fehlercode, falls zutreffend
sip_statusstringNeinDem Statuscode entsprechender SIP-Statustext

Twilio-Metadaten (type: "twilio"):

FeldTypErforderlichBeschreibung
typestringJaAnbietertyp (immer twilio)
bodyobjectJaTwilio-StatusCallback-Body mit Anrufdetails, hier dokumentiert

Beispielhafte Webhook-Payloads

Beispiel für einen Transkriptions-Webhook

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

Beispiel für einen Audio-Webhook

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

Beispiele für Webhooks bei fehlgeschlagener Anrufinitiierung

Beispiel für Twilio-Metadaten

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

Beispiel für SIP-Metadaten

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

Zustellung von Audio-Webhooks

Audio-Webhooks werden getrennt von Transkriptions-Webhooks zugestellt und enthalten nur die wesentlichen Felder zur Identifizierung des Gesprächs sowie die Base64-codierten Audiodaten.

Audio-Webhooks können über den Umschalter “Send audio data” in Ihren Webhook- Einstellungen aktiviert oder deaktiviert werden. Diese Einstellung kann sowohl auf Workspace-Ebene (in den ElevenAgents-Einstellungen) als auch auf Agent-Ebene (in individuellen Webhook-Überschreibungen für Agenten) konfiguriert werden.

Streaming-Zustellung

Audio-Webhooks werden als gestreamte HTTP-Anfragen mit dem Header transfer-encoding: chunked zugestellt, um große Audiodateien effizient zu verarbeiten.

Audio-Webhooks verarbeiten

Da Audio-Webhooks über Chunked Transfer Encoding zugestellt werden, müssen Sie Streaming-Daten korrekt verarbeiten:

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

Audio-Webhooks können große Dateien sein. Stellen Sie daher sicher, dass Ihr Webhook-Endpunkt Streaming-Anfragen verarbeiten kann und über ausreichend Speicher- und Speicherkapazität verfügt. Das Audio wird im MP3-Format zugestellt.

Anwendungsfälle

Automatisierte Nachfassaktionen nach Anrufen

Webhooks nach Anrufen ermöglichen Ihnen, automatisierte Workflows zu erstellen, die unmittelbar nach Ende eines Anrufs ausgelöst werden. Hier sind einige praktische Anwendungen:

CRM-Integration

Aktualisieren Sie Ihr Customer-Relationship-Management-System mit Gesprächsdaten, sobald ein Anruf beendet ist:

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

Zustandsbehaftete Gespräche

Bewahren Sie Gesprächskontext über mehrere Interaktionen hinweg, indem Sie Zustände speichern und abrufen:

  1. Übergeben Sie beim Start eines Anrufs Ihre Benutzer-ID als dynamische Variable.
  2. Richten Sie nach Ende eines Anrufs Ihren Webhook-Endpunkt so ein, dass er Gesprächsdaten anhand der aus dynamic_variables extrahierten Benutzer-ID in Ihrer Datenbank speichert.
  3. Wenn der Benutzer erneut anruft, können Sie diesen Kontext abrufen und ihn der neuen Unterhaltung als dynamische Variable {{previous_topics}} übergeben.
  4. Dadurch entsteht ein nahtloses Erlebnis, bei dem sich der Agent an vorherige Interaktionen „erinnert“.
// 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(", "),
},
});
}