Skip to navigation

Webhooks na gesprekken

Ontvang meldingen via webhooks wanneer gesprekken zijn beëindigd en de analyse is voltooid.

Overzicht

Met webhooks na gesprekken kun je gedetailleerde informatie over een gesprek ontvangen nadat de analyse is voltooid. Wanneer deze zijn ingeschakeld, stuurt ElevenLabs een POST-request naar je opgegeven endpoint met uitgebreide gespreksgegevens.

ElevenLabs ondersteunt drie typen webhooks na gesprekken:

  • Transcriptiewebhooks (post_call_transcription): Bevatten volledige gespreksgegevens, waaronder transcripties, analyseresultaten en metadata
  • Audiowebhooks (post_call_audio): Bevatten minimale gegevens met base64-gecodeerde audio van het volledige gesprek
  • Webhooks voor mislukte gespreksinitiatie (call_initiation_failure): Bevatten informatie over mislukte pogingen om gesprekken te starten, waaronder redenen voor mislukking en metadata

Webhooks na gesprekken inschakelen

Webhooks na gesprekken kunnen voor alle agents in je workspace worden ingeschakeld via de instellingenpagina van ElevenAgents.

Instellingen voor webhooks na gesprekken

Webhooks na gesprekken moeten een statuscode 200 retourneren om als geslaagd te worden beschouwd. Webhooks die herhaaldelijk mislukken, worden automatisch uitgeschakeld als er 10 of meer opeenvolgende fouten zijn en de laatste succesvolle aflevering meer dan 7 dagen geleden was, of als de webhook nog nooit succesvol is afgeleverd.

Webhooks na gesprekken kunnen automatisch opnieuw worden geprobeerd als ze mislukken. Zie opnieuw proberen van webhooks.

Authenticatie

Het is belangrijk dat de listener alle binnenkomende webhooks valideert. Webhooks ondersteunen momenteel authenticatie via HMAC-handtekeningen. Stel HMAC-authenticatie als volgt in:

  • Sla het gedeelde geheim dat is gegenereerd bij het maken van de webhook veilig op
  • Verifieer de header ElevenLabs-Signature in je endpoint met de SDK

De JavaScript SDK biedt constructEvent; de Python SDK biedt construct_event met rawBody, sig_header en secret (in Python heten deze niet payload / signature). Beide verifiëren de handtekening, valideren de timestamp en parsen de JSON-payload.

Voorbeeld van een webhookhandler met 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"}

Aangepaste requestheaders

Naast HMAC-handtekeningvalidatie kun je bij elke webhookaflevering aangepaste headers opnemen. Dit is handig om API-sleutels, bearer tokens of andere authenticatieheaders door te geven die je ontvangende endpoint vereist.

Aangepaste headers kunnen momenteel niet via het ElevenAgents-dashboard worden geconfigureerd. Gebruik de SDK of API om aangepaste headers in te stellen.

Aangepaste headers werken naast HMAC-authenticatie. auth_type moet nog steeds zijn ingesteld op hmac, en aangepaste headers worden verzonden naast de header ElevenLabs-Signature.

Aangepaste headers zijn alleen schrijfbaar. Het endpoint voor het weergeven van webhooks retourneert request_headers niet in het antwoord, dus je kunt niet teruglezen welke headers voor een webhook zijn geconfigureerd.

IP-allowlisting

Voor extra beveiliging kun je de statische uitgaande IP’s van ElevenLabs aan je allowlist toevoegen. Zie IP-allowlisting voor de volledige lijst met IP-adressen.

Door IP-allowlisting te combineren met HMAC-handtekeningvalidatie voeg je meerdere lagen beveiliging toe.

Webhookoverschrijvingen per agent

Standaard gebruiken alle agents in je workspace de webhook die in je workspace-instellingen is geconfigureerd. Je kunt dit per agent overschrijven om gegevens na gesprekken voor verschillende agents naar verschillende endpoints te sturen.

1

Agentinstellingen openen

Ga naar je agent in het ElevenAgents-dashboard en open Instellingen.

2

Webhookoverschrijving configureren

Selecteer in het gedeelte Webhooks na gesprekken een andere webhook in de dropdown om de standaardinstelling van de workspace voor deze agent te overschrijven. Je kunt ook configureren welke eventtypen je wilt verzenden.

Instellingen voor webhookoverschrijving van agent

post_call_webhook_id moet verwijzen naar een webhook die al in je workspace bestaat. Je kunt ook met de array events opgeven welke eventtypen je wilt verzenden:

EventtypeBeschrijving
transcriptTranscriptiegegevens met gespreksdetails en analyse
audioBase64-gecodeerde MP3-audio van het volledige gesprek
call_initiation_failureInformatie over mislukte pogingen om een gesprek te starten
answering_machine_detectionResultaten van detectie van antwoordapparaten (alleen uitgaande gesprekken)
unredacted_transcriptTranscript met intacte PII (vereist passende gegevensbewaring)
unredacted_audioAudio zonder PII-redactie (vereist passende gegevensbewaring)

Structuur van webhookantwoorden

ElevenLabs verstuurt drie verschillende typen webhooks na gesprekken, elk met een andere gegevensstructuur:

Transcriptiewebhooks (post_call_transcription)

Bevatten uitgebreide gespreksgegevens, waaronder volledige transcripties, analyseresultaten en metadata.

Velden op hoofdniveau

VeldTypeBeschrijving
typestringType event (altijd post_call_transcription)
dataobjectGespreksgegevens met de structuur ConversationHistoryCommonModel
event_timestampnumberWanneer dit event plaatsvond, in Unix-tijd UTC

Structuur van data-object

Het object data bevat:

VeldTypeBeschrijving
agent_idstringDe ID van de agent die het gesprek afhandelde
agent_namestringDe naam van de agent op het moment van het gesprek
conversation_idstringUnieke identificatie voor het gesprek
statusstringStatus van het gesprek (bijv. “done”)
user_idstringGebruikersidentificatie, indien beschikbaar
branch_idstringDe agentbranch die voor het gesprek is gebruikt, indien van toepassing
version_idstringDe ID van de agentversie (snapshot) die actief was tijdens het gesprek
environmentstringDe omgeving die is gebruikt om omgevingsvariabelen op te lossen
transcriptarrayVolledig gesprekstranscript met beurten
metadataobjectGesprekstiming, kosten en telefoongegevens
analysisobjectEvaluatieresultaten en gesprekssamenvatting
conversation_initiation_client_dataobjectConfiguratieoverschrijvingen en dynamische variabelen
has_audiobooleanOf er audio voor het gesprek beschikbaar is
has_user_audiobooleanOf gebruikersaudio voor het gesprek beschikbaar is
has_response_audiobooleanOf audio van de agentrespons voor het gesprek beschikbaar is

Audiowebhooks (post_call_audio)

Bevatten minimale gegevens met de audio van het volledige gesprek als base64-gecodeerde MP3.

Velden op hoofdniveau

VeldTypeBeschrijving
typestringType event (altijd post_call_audio)
dataobjectMinimale audiogegevens
event_timestampnumberWanneer dit event plaatsvond, in Unix-tijd UTC

Structuur van data-object

Het object data bevat alleen:

VeldTypeBeschrijving
agent_idstringDe ID van de agent die het gesprek afhandelde
conversation_idstringUnieke identificatie voor het gesprek
full_audiostringBase64-gecodeerde string met de volledige gespreksaudio in MP3-formaat

Audiowebhooks bevatten alleen de drie hierboven vermelde velden. Ze bevatten GEEN transcriptiegegevens, metadata, analyseresultaten of andere gespreksdetails.

Webhooks voor mislukte gespreksinitiatie (call_initiation_failure)

Bevatten informatie over pogingen om telefonische gesprekken te starten, waaronder redenen voor mislukking en metadata van de telefonieprovider.

Webhookevents voor mislukte gespreksinitiatie worden verzonden wanneer een gesprek niet kan worden gestart vanwege verbindingsfouten, omdat de gebruiker het gesprek weigert of niet opneemt. Als een gesprek naar voicemail gaat of wordt opgenomen door een geautomatiseerde service, wordt er geen webhook voor mislukte gespreksinitiatie verzonden, omdat het gesprek succesvol is gestart.

Velden op hoofdniveau

VeldTypeBeschrijving
typestringType event (altijd call_initiation_failure)
dataobjectGegevens over mislukte gespreksinitiatie
event_timestampnumberWanneer dit event plaatsvond, in Unix-tijd UTC

Structuur van data-object

Het object data bevat:

VeldTypeBeschrijving
agent_idstringDe ID van de agent die was toegewezen om het gesprek af te handelen
conversation_idstringUnieke identificatie voor het gesprek
failure_reasonstringDe reden voor mislukking (“busy”, “no-answer”, “unknown”)
metadataobjectAanvullende gegevens van de telefonieprovider.

Voor uitgaande gesprekken via Twilio wordt failure_reason afgeleid van de Twilio-CallStatus: busy en no-answer worden ongewijzigd doorgegeven en failed wordt gerapporteerd als unknown. Voor andere Twilio-gespreksstatussen wordt geen webhook voor mislukte gespreksinitiatie verzonden.

Structuur van metadata-object

De structuur van het object metadata verschilt afhankelijk van of het uitgaande gesprek via Twilio of via SIP-trunking is gevoerd. Het object bevat een veld type dat onderscheid maakt tussen de twee, en een veld body met providerspecifieke details.

SIP-metadata (type: "sip"):

VeldTypeVereistBeschrijving
typestringJaProvidertype (altijd sip)
bodyobjectJaSIP-specifieke informatie over mislukte gesprekken

Het object body voor SIP-metadata bevat:

VeldTypeVereistBeschrijving
from_numbernumberJaHet telefoonnummer van de partij die het gesprek startte.
to_numbernumberJaHet telefoonnummer van de gebelde partij.
sip_status_codenumberJaSIP-responsstatuscode (bijv. 486 voor bezet)
error_reasonstringJaVoor mensen leesbare foutbeschrijving
call_sidstringJaIdentificatie van de SIP-gesprekssessie
twirp_codestringNeeTwirp-foutcode, indien van toepassing
sip_statusstringNeeSIP-statustekst die overeenkomt met de statuscode

Twilio-metadata (type: "twilio"):

VeldTypeVereistBeschrijving
typestringJaProvidertype (altijd twilio)
bodyobjectJaTwilio StatusCallback-body met gespreksdetails, gedocumenteerd hier

Voorbeelden van webhookpayloads

Voorbeeld van transcriptiewebhook

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

Voorbeeld van audiowebhook

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

Voorbeelden van webhooks voor mislukte gespreksinitiatie

Voorbeeld van Twilio-metadata

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

Voorbeeld van SIP-metadata

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

Aflevering van audiowebhooks

Audiowebhooks worden afzonderlijk van transcriptiewebhooks afgeleverd en bevatten alleen de essentiële velden om het gesprek te identificeren, samen met de base64-gecodeerde audiogegevens.

Audiowebhooks kunnen worden in- of uitgeschakeld met de schakelaar “Audiogegevens verzenden” in je webhookinstellingen. Deze instelling kan zowel op workspace-niveau (in de ElevenAgents-instellingen) als op agentniveau (in webhookoverschrijvingen van afzonderlijke agents) worden geconfigureerd.

Streamingaflevering

Audiowebhooks worden geleverd als streaming HTTP-requests met de header transfer-encoding: chunked om grote audiobestanden efficiënt te verwerken. Elke request krijgt na 5 minuten een time-out.

Opnieuw proberen

Wanneer opnieuw proberen voor de webhook is ingeschakeld, worden mislukte audioafleveringen volgens hetzelfde schema opnieuw geprobeerd als transcriptiewebhooks. Bij een nieuwe poging wordt de volledige audiopayload opnieuw verzonden; dedupliceer daarom op conversation_id. Zie opnieuw proberen van webhooks voor het schema, fouten die opnieuw kunnen worden geprobeerd en limieten voor audiogrootte.

Audiowebhooks verwerken

Omdat audiowebhooks via chunked transfer encoding worden afgeleverd, moet je streaminggegevens correct verwerken:

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

Audiowebhooks kunnen grote bestanden zijn. Zorg er daarom voor dat je webhookendpoint streaming requests kan verwerken en voldoende geheugen- en opslagcapaciteit heeft. De audio wordt afgeleverd in MP3-formaat.

Gebruiksscenario’s

Geautomatiseerde opvolging na gesprekken

Met webhooks na gesprekken kun je geautomatiseerde workflows maken die direct worden geactiveerd nadat een gesprek is beëindigd. Hier zijn enkele praktische toepassingen:

CRM-integratie

Werk je systeem voor klantrelatiebeheer bij met gespreksgegevens zodra een gesprek is voltooid:

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

Stateful gesprekken

Behoud gesprekscontext tussen meerdere interacties door status op te slaan en op te halen:

  1. Geef bij de start van een gesprek je gebruikers-ID door als dynamische variabele.
  2. Stel bij het einde van een gesprek je webhookendpoint in om gespreksgegevens in je database op te slaan op basis van de uit dynamic_variables geëxtraheerde gebruikers-ID.
  3. Wanneer de gebruiker opnieuw belt, kun je deze context ophalen en aan het nieuwe gesprek doorgeven in een dynamische variabele {{previous_topics}}.
  4. Zo creëer je een naadloze ervaring waarin de agent eerdere interacties “onthoudt”.
// 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(", "),
},
});
}