Hoppa till navigering

Webhooks efter samtal

Få aviseringar via webhooks när samtal avslutas och analysen är klar.

Översikt

Webhooks efter samtal gör att du kan ta emot detaljerad information om ett samtal när analysen är klar. När de är aktiverade skickar ElevenLabs en POST-begäran till din angivna slutpunkt med omfattande samtalsdata.

ElevenLabs har stöd för tre typer av webhooks efter samtal:

  • Webhooks för transkribering (post_call_transcription): Innehåller fullständig konversationsdata, inklusive transkriberingar, analysresultat och metadata
  • Ljudwebhooks (post_call_audio): Innehåller minimal data med base64-kodat ljud från hela konversationen
  • Webhooks för misslyckad samtalsinitiering (call_initiation_failure): Innehåller information om misslyckade försök att initiera samtal, inklusive orsaker till felet och metadata

Aktivera webhooks efter samtal

Webhooks efter samtal kan aktiveras för alla agenter i din arbetsyta via inställningssidan för ElevenAgents.

Inställningar för webhook efter samtal

Webhooks efter samtal måste returnera statuskoden 200 för att räknas som lyckade. Webhooks som misslyckas upprepade gånger inaktiveras automatiskt om det sker 10 eller fler misslyckanden i rad och den senaste lyckade leveransen var för mer än 7 dagar sedan eller om leveransen aldrig har lyckats.

Webhooks efter samtal kan automatiskt försöka igen om de misslyckas. Se nya webhookförsök .

Autentisering

Det är viktigt att mottagaren validerar alla inkommande webhooks. Webhooks har för närvarande stöd för autentisering via HMAC-signaturer. Konfigurera HMAC-autentisering genom att:

  • Lagra den delade hemligheten som genereras när webhooken skapas på ett säkert sätt
  • Verifiera headern ElevenLabs-Signature i din endpoint med hjälp av SDK:n

JavaScript-SDK:n exponerar constructEvent; Python-SDK:n exponerar construct_event med rawBody, sig_header och secret (dessa heter inte payload / signature i Python). Båda verifierar signaturen, validerar tidsstämpeln och tolkar JSON-payloaden.

Exempel på webhook-hanterare med 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"}

Anpassade begärandehuvuden

Utöver validering av HMAC-signaturer kan du inkludera anpassade headers med varje webhookleverans. Det är användbart för att skicka API-nycklar, bearer-token eller andra autentiseringsheaders som din mottagande slutpunkt behöver.

Anpassade headers kan för närvarande inte konfigureras via ElevenAgents-dashboarden. Använd SDK:t eller API:t för att ange anpassade headers.

Anpassade headers fungerar tillsammans med HMAC-autentisering. auth_type måste fortfarande vara inställt på hmac, och anpassade headers skickas utöver headern ElevenLabs-Signature.

Anpassade headers kan bara skrivas. Slutpunkten för att lista webhooks returnerar inte request_headers i sitt svar, så du kan inte läsa tillbaka vilka headers som har konfigurerats för en webhook.

Tillåtlistning av IP-adresser

För ökad säkerhet kan du lägga till ElevenLabs statiska utgående IP-adresser i din tillåtlista. Se tillåtlistning av IP-adresser för den fullständiga listan över IP-adresser.

Att använda tillåtlistning av IP-adresser tillsammans med validering av HMAC-signaturer ger flera säkerhetslager.

Åsidosättningar av webhook per agent

Som standard använder alla agenter i din arbetsyta webhooken som har konfigurerats i arbetsytans inställningar. Du kan åsidosätta detta för enskilda agenter för att skicka data efter samtal till olika slutpunkter för olika agenter.

1

Öppna agentinställningarna

Gå till din agent i ElevenAgents-dashboarden och välj Inställningar.

2

Konfigurera åsidosättning av webhook

I avsnittet Webhooks efter samtal väljer du en annan webhook i rullgardinsmenyn för att åsidosätta arbetsytans standard för den här agenten. Du kan också konfigurera vilka händelsetyper som ska skickas.

Inställningar för åsidosättning av agentens webhook

post_call_webhook_id måste hänvisa till en webhook som redan finns i din arbetsyta. Du kan också ange vilka händelsetyper som ska skickas med arrayen events:

HändelsetypBeskrivning
transcriptTranskriberingsdata med konversationsinformation och analys
audioBase64-kodat MP3-ljud från hela konversationen
call_initiation_failureInformation om misslyckade försök att initiera samtal
answering_machine_detectionResultat från identifiering av telefonsvarare (endast utgående samtal)
unredacted_transcriptTranskribering med personuppgifter intakta (kräver lämplig datalagring)
unredacted_audioLjud utan maskering av personuppgifter (kräver lämplig datalagring)

Struktur för webhooksvar

ElevenLabs skickar tre olika typer av webhooks efter samtal, var och en med olika datastrukturer:

Webhooks för transkribering (post_call_transcription)

Innehåller omfattande konversationsdata, inklusive fullständiga transkriberingar, analysresultat och metadata.

Fält på toppnivå

FältTypBeskrivning
typestringHändelsetyp (alltid post_call_transcription)
dataobjectKonversationsdata med strukturen ConversationHistoryCommonModel
event_timestampnumberNär händelsen inträffade, i Unix-tid UTC

Datastruktur för objektet

Objektet data innehåller:

FältTypBeskrivning
agent_idstringID för agenten som hanterade samtalet
agent_namestringAgentens namn vid tidpunkten för konversationen
conversation_idstringUnik identifierare för konversationen
statusstringKonversationens status (t.ex. “done”)
user_idstringAnvändaridentifierare, om tillgängligt
branch_idstringAgentgrenen som användes för konversationen, om tillämpligt
version_idstringID för agentversionen (ögonblicksbilden) som var aktiv under samtalet
environmentstringMiljön som användes för att lösa miljövariabler
transcriptarrayFullständig konversationstranskribering med turer
metadataobjectSamtalstider, kostnader och telefoninformation
analysisobjectUtvärderingsresultat och konversationssammanfattning
conversation_initiation_client_dataobjectKonfigurationsåsidosättningar och dynamiska variabler
has_audiobooleanOm konversationen har tillgängligt ljud
has_user_audiobooleanOm användarljud är tillgängligt för konversationen
has_response_audiobooleanOm agentens svarsljud är tillgängligt för konversationen

Ljudwebhooks (post_call_audio)

Innehåller minimal data med ljudet från hela konversationen som base64-kodad MP3.

Fält på toppnivå

FältTypBeskrivning
typestringHändelsetyp (alltid post_call_audio)
dataobjectMinimal ljuddata
event_timestampnumberNär händelsen inträffade, i Unix-tid UTC

Datastruktur för objektet

Objektet data innehåller endast:

FältTypBeskrivning
agent_idstringID för agenten som hanterade samtalet
conversation_idstringUnik identifierare för konversationen
full_audiostringBase64-kodad sträng som innehåller hela konversationsljudet i MP3-format

Ljudwebhooks innehåller endast de tre fält som anges ovan. De innehåller INTE transkriberingsdata, metadata, analysresultat eller någon annan konversationsinformation.

Webhooks för misslyckad samtalsinitiering (call_initiation_failure)

Innehåller information om försök att initiera telefonsamtal, inklusive orsaker till fel och metadata från telefonioperatören.

Webhookhändelser för misslyckad samtalsinitiering skickas när ett samtal inte kan initieras på grund av anslutningsfel, att användaren avböjer samtalet eller att användaren inte svarar. Om ett samtal går till röstbrevlådan eller besvaras av en automatiserad tjänst skickas ingen webhook för misslyckad samtalsinitiering, eftersom samtalet initierades korrekt.

Fält på toppnivå

FältTypBeskrivning
typestringHändelsetyp (alltid call_initiation_failure)
dataobjectData om misslyckad samtalsinitiering
event_timestampnumberNär händelsen inträffade, i Unix-tid UTC

Datastruktur för objektet

Objektet data innehåller:

FältTypBeskrivning
agent_idstringID för agenten som tilldelades att hantera samtalet
conversation_idstringUnik identifierare för konversationen
failure_reasonstringOrsaken till felet (“busy”, “no-answer”, “unknown”)
metadataobjectYtterligare data från telefonioperatören.

För utgående samtal via Twilio härleds failure_reason från Twilios CallStatus: busy och no-answer skickas vidare som de är, medan failed rapporteras som unknown. Andra Twilio-samtalsstatusar skickar ingen webhook för misslyckad samtalsinitiering.

Metadatastruktur för objektet

Strukturen för objektet metadata varierar beroende på om det utgående samtalet gjordes via Twilio eller SIP-trunking. Objektet innehåller fältet type, som skiljer mellan de två, och fältet body, som innehåller leverantörsspecifik information.

SIP-metadata (type: "sip"):

FältTypObligatorisktBeskrivning
typestringJaLeverantörstyp (alltid sip)
bodyobjectJaSIP-specifik information om samtalsfel

Objektet body för SIP-metadata innehåller:

FältTypObligatorisktBeskrivning
from_numbernumberJaTelefonnummer för parten som initierade samtalet.
to_numbernumberJaTelefonnummer för den uppringda parten.
sip_status_codenumberJaSIP-svarsstatuskod (t.ex. 486 för upptaget)
error_reasonstringJaFelbeskrivning i klartext
call_sidstringJaSessionsidentifierare för SIP-samtalet
twirp_codestringNejTwirp-felkod, om tillämpligt
sip_statusstringNejSIP-statustext som motsvarar statuskoden

Twilio-metadata (type: "twilio"):

FältTypObligatorisktBeskrivning
typestringJaLeverantörstyp (alltid twilio)
bodyobjectJaTwilio StatusCallback-body med samtalsinformation, dokumenterad här

Exempel på webhook-payloads

Exempel på webhook för transkribering

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

Exempel på ljudwebhook

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

Exempel på webhooks för misslyckad samtalsinitiering

Exempel på 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"
}
}
}
}

Exempel på 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"
}
}
}
}

Leverans av ljudwebhooks

Ljudwebhooks levereras separat från webhooks för transkribering och innehåller bara de viktigaste fälten för att identifiera konversationen samt base64-kodad ljuddata.

Ljudwebhooks kan aktiveras eller inaktiveras med reglaget “Skicka ljuddata” i dina webhookinställningar. Inställningen kan konfigureras både på arbetsytenivå (i ElevenAgents-inställningarna) och på agentnivå (i åsidosättningar av webhook för enskilda agenter).

Strömmande leverans

Ljudwebhooks levereras som strömmande HTTP-begäranden med headern transfer-encoding: chunked för att hantera stora ljudfiler effektivt. Varje begäran får timeout efter 5 minuter.

Nya försök

När nya försök är aktiverade för webhooken görs nya försök med misslyckade ljudleveranser enligt samma schema som för webhooks för transkribering. Ett nytt försök skickar hela ljud-payloaden igen, så deduplicera med conversation_id. Se nya webhookförsök för schemat, fel som kan försökas igen och gränser för ljudstorlek.

Bearbeta ljudwebhooks

Eftersom ljudwebhooks levereras via chunked transfer encoding måste du hantera strömmande data korrekt:

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

Ljudwebhooks kan vara stora filer, så se till att din webhookslutpunkt kan hantera strömmande begäranden och har tillräcklig minnes- och lagringskapacitet. Ljudet levereras i MP3-format.

Användningsfall

Automatiserad uppföljning av samtal

Webhooks efter samtal gör att du kan skapa automatiserade workflow som utlöses direkt efter att ett samtal avslutas. Här är några praktiska tillämpningar:

CRM-integrering

Uppdatera ditt system för kundrelationshantering med konversationsdata så snart ett samtal är klart:

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

Tillståndsbaserade konversationer

Behåll konversationskontext mellan flera interaktioner genom att lagra och hämta tillstånd:

  1. När ett samtal startar skickar du med ditt användar-id som en dynamisk variabel.
  2. När ett samtal avslutas konfigurerar du din webhookslutpunkt för att lagra konversationsdata i din databas, baserat på användar-id:t som extraherats från dynamic_variables.
  3. När användaren ringer igen kan du hämta den här kontexten och skicka den till den nya konversationen i en dynamisk variabel, {{previous_topics}}.
  4. Det skapar en sömlös upplevelse där agenten “minns” tidigare interaktioner.
// 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(", "),
},
});
}