Przejdź do nawigacji

Webhooki po rozmowie

Otrzymuj powiadomienia przez webhooki, gdy rozmowy się zakończą i analiza będzie gotowa.

Omówienie

Webhooki po rozmowie pozwalają otrzymywać szczegółowe informacje o rozmowie po zakończeniu analizy. Po włączeniu ElevenLabs wyśle żądanie POST do wskazanego endpointu z pełnymi danymi rozmowy.

ElevenLabs obsługuje trzy rodzaje webhooków po rozmowie:

  • Webhooki transkrypcji (post_call_transcription): zawierają pełne dane rozmowy, w tym transkrypcje, wyniki analizy i metadane
  • Webhooki audio (post_call_audio): zawierają minimalne dane oraz zakodowane w base64 audio całej rozmowy
  • Webhooki nieudanej inicjacji rozmowy (call_initiation_failure): zawierają informacje o nieudanych próbach inicjacji rozmowy, w tym przyczyny niepowodzenia i metadane

Włączanie webhooków po rozmowie

Webhooki po rozmowie możesz włączyć dla wszystkich agentów w obszarze roboczym na stronie ustawień ElevenAgents.

Ustawienia webhooków po rozmowie

Aby webhook po rozmowie został uznany za udany, musi zwrócić kod statusu 200. Webhooki, które wielokrotnie zawodzą, są automatycznie wyłączane, jeśli wystąpiło co najmniej 10 kolejnych błędów, a ostatnie udane dostarczenie miało miejsce ponad 7 dni temu lub nigdy nie zostało pomyślnie dostarczone.

Webhooki po rozmowie mogą być automatycznie ponawiane po niepowodzeniu. Zobacz ponawianie webhooków .

Uwierzytelnianie

Ważne, by odbiornik weryfikował wszystkie przychodzące webhooki. Webhooki obecnie obsługują uwierzytelnianie za pomocą podpisów HMAC. Aby skonfigurować uwierzytelnianie HMAC:

  • Bezpiecznie przechowuj współdzielony sekret wygenerowany podczas tworzenia webhooka
  • Zweryfikuj nagłówek ElevenLabs-Signature w swoim endpointzie za pomocą SDK

SDK JavaScript udostępnia constructEvent, a SDK Python construct_event z parametrami rawBody, sig_header i secret (w Pythonie nie nazywają się one payload / signature). Oba weryfikują podpis, sprawdzają znacznik czasu i parsują dane JSON.

Przykładowy handler webhooka z użyciem 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"}

Własne nagłówki żądań

Oprócz walidacji podpisu HMAC możesz dołączyć własne nagłówki do każdego dostarczenia webhooka. Jest to przydatne do przekazywania kluczy API, tokenów bearer lub innych nagłówków uwierzytelniających wymaganych przez endpoint odbierający.

Własnych nagłówków nie można obecnie skonfigurować w panelu ElevenAgents. Użyj SDK lub API, aby ustawić własne nagłówki.

Własne nagłówki działają razem z uwierzytelnianiem HMAC. auth_type nadal musi mieć wartość hmac, a własne nagłówki są wysyłane dodatkowo do nagłówka ElevenLabs-Signature.

Własne nagłówki są tylko do zapisu. Endpoint listy webhooków nie zwraca request_headers w odpowiedzi, więc nie możesz odczytać, jakie nagłówki są skonfigurowane dla webhooka.

Lista dozwolonych adresów IP

Dla większego bezpieczeństwa możesz dodać statyczne wychodzące adresy IP ElevenLabs do listy dozwolonych. Pełną listę adresów IP znajdziesz w liście dozwolonych adresów IP.

Używanie listy dozwolonych adresów IP razem z walidacją podpisu HMAC zapewnia wiele warstw zabezpieczeń.

Zastępowanie webhooków dla poszczególnych agentów

Domyślnie wszyscy agenci w obszarze roboczym używają webhooka skonfigurowanego w ustawieniach obszaru roboczego. Możesz zastąpić go dla konkretnego agenta, aby wysyłać dane po rozmowie do różnych endpointów dla różnych agentów.

1

Otwórz ustawienia agenta

Przejdź do agenta w panelu ElevenAgents i otwórz Ustawienia.

2

Skonfiguruj zastąpienie webhooka

W sekcji Webhooki po rozmowie wybierz z listy inny webhook, aby zastąpić domyślny webhook obszaru roboczego dla tego agenta. Możesz też skonfigurować, które typy zdarzeń mają być wysyłane.

Ustawienia zastąpienia webhooka agenta

post_call_webhook_id musi wskazywać webhook, który już istnieje w obszarze roboczym. Możesz też określić, które typy zdarzeń mają być wysyłane, za pomocą tablicy events:

Typ zdarzeniaOpis
transcriptDane transkrypcji ze szczegółami rozmowy i analizą
audioZakodowane w base64 audio MP3 całej rozmowy
call_initiation_failureInformacje o nieudanych próbach inicjacji rozmowy
answering_machine_detectionWyniki wykrywania automatycznej sekretarki (tylko rozmowy wychodzące)
unredacted_transcriptTranskrypcja z niezanonimizowanymi danymi osobowymi (wymaga odpowiedniego przechowywania danych)
unredacted_audioAudio bez anonimizacji danych osobowych (wymaga odpowiedniego przechowywania danych)

Struktura odpowiedzi webhooka

ElevenLabs wysyła trzy różne typy webhooków po rozmowie, każdy z inną strukturą danych:

Webhooki transkrypcji (post_call_transcription)

Zawierają pełne dane rozmowy, w tym kompletne transkrypcje, wyniki analizy i metadane.

Pola najwyższego poziomu

PoleTypOpis
typestringTyp zdarzenia (zawsze post_call_transcription)
dataobjectDane rozmowy w strukturze ConversationHistoryCommonModel
event_timestampnumberCzas wystąpienia zdarzenia w formacie unix UTC

Struktura obiektu danych

Obiekt data zawiera:

PoleTypOpis
agent_idstringID agenta, który obsłużył rozmowę
agent_namestringNazwa agenta w czasie rozmowy
conversation_idstringUnikalny identyfikator rozmowy
statusstringStatus rozmowy (np. “done”)
user_idstringIdentyfikator użytkownika, jeśli jest dostępny
branch_idstringGałąź agenta użyta w rozmowie, jeśli dotyczy
version_idstringID wersji agenta (migawki) aktywnej podczas rozmowy
environmentstringŚrodowisko używane do rozwiązywania zmiennych środowiskowych
transcriptarrayPełna transkrypcja rozmowy z turami
metadataobjectCzas rozmowy, koszty i dane telefoniczne
analysisobjectWyniki oceny i podsumowanie rozmowy
conversation_initiation_client_dataobjectNadpisania konfiguracji i zmienne dynamiczne
has_audiobooleanCzy dla rozmowy dostępne jest jakiekolwiek audio
has_user_audiobooleanCzy audio użytkownika jest dostępne dla rozmowy
has_response_audiobooleanCzy audio odpowiedzi agenta jest dostępne dla rozmowy

conversation_initiation_client_data.dynamic_variables mapuje nazwy zmiennych na dowolne wartości JSON, w tym listy i obiekty. Obejmuje wbudowane zmienne system__, a nowe mogą być dodawane, więc nie odrzucaj nierozpoznanych kluczy ani typów wartości. Zobacz zmienne dynamiczne.

Webhooki audio (post_call_audio)

Zawierają minimalne dane oraz audio całej rozmowy w formacie MP3 zakodowane w base64.

Pola najwyższego poziomu

PoleTypOpis
typestringTyp zdarzenia (zawsze post_call_audio)
dataobjectMinimalne dane audio
event_timestampnumberCzas wystąpienia zdarzenia w formacie unix UTC

Struktura obiektu danych

Obiekt data zawiera tylko:

PoleTypOpis
agent_idstringID agenta, który obsłużył rozmowę
conversation_idstringUnikalny identyfikator rozmowy
branch_idstringGałąź agenta użyta w rozmowie, jeśli dotyczy
version_idstringID wersji agenta (migawki) aktywnej podczas rozmowy
environmentstringŚrodowisko używane do rozwiązywania zmiennych środowiskowych
full_audiostringCiąg zakodowany w base64 zawierający pełne audio rozmowy w formacie MP3

Webhooki audio zawierają wyłącznie pola wymienione powyżej. NIE obejmują danych transkrypcji, metadanych, wyników analizy ani żadnych innych szczegółów rozmowy.

Webhooki nieudanej inicjacji rozmowy (call_initiation_failure)

Zawierają informacje o próbach inicjacji rozmów telefonicznych, w tym przyczyny niepowodzenia i metadane dostawcy telefonii.

Zdarzenia webhooka nieudanej inicjacji rozmowy są wysyłane, gdy nie można zainicjować rozmowy z powodu błędów połączenia, odrzucenia rozmowy przez użytkownika lub nieodebrania jej przez użytkownika. Jeśli rozmowa trafia na pocztę głosową lub zostaje odebrana przez automatyczny system, webhook nieudanej inicjacji rozmowy nie jest wysyłany, ponieważ rozmowa została pomyślnie zainicjowana.

Pola najwyższego poziomu

PoleTypOpis
typestringTyp zdarzenia (zawsze call_initiation_failure)
dataobjectDane nieudanej inicjacji rozmowy
event_timestampnumberCzas wystąpienia zdarzenia w formacie unix UTC

Struktura obiektu danych

Obiekt data zawiera:

PoleTypOpis
agent_idstringID agenta przypisanego do obsługi rozmowy
conversation_idstringUnikalny identyfikator rozmowy
failure_reasonstringPrzyczyna niepowodzenia (“busy”, “no-answer”, “unknown”)
metadataobjectDodatkowe dane przekazane przez dostawcę telefonii.

W przypadku rozmów wychodzących realizowanych przez Twilio wartość failure_reason pochodzi ze statusu Twilio CallStatus: busy i no-answer są przekazywane bez zmian, a failed jest raportowane jako unknown. Pozostałe statusy rozmów Twilio nie wysyłają webhooka nieudanej inicjacji rozmowy.

Struktura obiektu metadanych

Struktura obiektu metadata różni się zależnie od tego, czy rozmowa wychodząca została wykonana przez Twilio, czy przez trunking SIP. Obiekt zawiera pole type, które rozróżnia te dwa przypadki, oraz pole body ze szczegółami specyficznymi dla dostawcy.

Metadane SIP (type: "sip"):

PoleTypWymaganeOpis
typestringTakTyp dostawcy (zawsze sip)
bodyobjectTakInformacje o nieudanej rozmowie specyficzne dla SIP

Obiekt body dla metadanych SIP zawiera:

PoleTypWymaganeOpis
from_numbernumberTakNumer telefonu strony, która zainicjowała rozmowę.
to_numbernumberTakNumer telefonu strony wywoływanej.
sip_status_codenumberTakKod statusu odpowiedzi SIP (np. 486 dla zajętej linii)
error_reasonstringTakOpis błędu czytelny dla człowieka
call_sidstringTakIdentyfikator sesji rozmowy SIP
twirp_codestringNieKod błędu Twirp, jeśli dotyczy
sip_statusstringNieTekst statusu SIP odpowiadający kodowi statusu

Metadane Twilio (type: "twilio"):

PoleTypWymaganeOpis
typestringTakTyp dostawcy (zawsze twilio)
bodyobjectTakTreść StatusCallback Twilio zawierająca szczegóły rozmowy, opisana tutaj

Przykładowe dane webhooków

Przykład webhooka transkrypcji

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

Przykład webhooka audio

{
"type": "post_call_audio",
"event_timestamp": 1739537319,
"data": {
"agent_id": "xyz",
"conversation_id": "abc",
"branch_id": "agtbrch_xyz",
"version_id": "agtvrsn_xyz",
"environment": "production",
"full_audio": "SUQzBAAAAAAA...base64_encoded_mp3_data...AAAAAAAAAA=="
}
}

Przykłady webhooków błędów inicjowania połączenia

Przykład metadanych 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"
}
}
}
}

Przykład metadanych 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"
}
}
}
}

Dostarczanie webhooków audio

Webhooki audio są dostarczane osobno od webhooków transkrypcji i zawierają tylko kluczowe pola potrzebne do identyfikacji rozmowy oraz dane audio zakodowane w base64.

Webhooki audio możesz włączyć lub wyłączyć przełącznikiem „Send audio data” w ustawieniach webhooka. Ustawienie to można skonfigurować zarówno na poziomie przestrzeni roboczej (w ustawieniach ElevenAgents), jak i na poziomie agenta (w indywidualnych ustawieniach webhooków agenta).

Dostarczanie strumieniowe

Webhooki audio są dostarczane jako strumieniowe żądania HTTP z nagłówkiem transfer-encoding: chunked, aby sprawnie obsługiwać duże pliki audio. Limit czasu każdego żądania wynosi 5 minut.

Ponowienia

Gdy ponowienia są włączone dla webhooka, nieudane dostarczenia audio są ponawiane według tego samego harmonogramu co webhooki transkrypcji. Ponowienie wysyła pełne dane audio, więc usuwaj duplikaty według conversation_id. Harmonogram, błędy kwalifikujące się do ponowienia i limity rozmiaru audio znajdziesz w sekcji ponowienia webhooków.

Przetwarzanie webhooków audio

Ponieważ webhooki audio są dostarczane przez kodowanie transferu chunked, musisz odpowiednio obsłużyć dane strumieniowe:

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

Webhooki audio mogą zawierać duże pliki, więc upewnij się, że punkt końcowy webhooka obsługuje żądania strumieniowe i ma wystarczającą pamięć oraz przestrzeń dyskową. Audio jest dostarczane w formacie MP3.

Zastosowania

Automatyczne działania po rozmowie

Webhooki po rozmowie pozwalają tworzyć automatyczne workflow, które uruchamiają się zaraz po zakończeniu połączenia. Oto kilka praktycznych zastosowań:

Integracja z CRM

Aktualizuj system zarządzania relacjami z klientami danymi rozmowy od razu po zakończeniu połączenia:

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

Rozmowy ze stanem

Zachowuj kontekst rozmowy między kolejnymi interakcjami, zapisując i pobierając stan:

  1. Gdy rozpoczyna się połączenie, przekaż identyfikator użytkownika jako zmienną dynamiczną.
  2. Gdy połączenie się kończy, skonfiguruj punkt końcowy webhooka, aby zapisywał dane rozmowy w bazie danych na podstawie identyfikatora użytkownika wyodrębnionego z dynamic_variables.
  3. Gdy użytkownik zadzwoni ponownie, możesz pobrać ten kontekst i przekazać go do nowej rozmowy w zmiennej dynamicznej {{previous_topics}}.
  4. Dzięki temu agent „pamięta” wcześniejsze interakcje, a doświadczenie jest płynne.
// 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(", "),
},
});
}