Vai alla navigazione

Webhook post-chiamata

Ricevi notifiche tramite webhook quando le chiamate terminano e l’analisi è completa.

Panoramica

I webhook post-chiamata ti consentono di ricevere informazioni dettagliate su una chiamata al termine dell’analisi. Quando sono abilitati, ElevenLabs invia una richiesta POST all’endpoint specificato con dati completi sulla chiamata.

ElevenLabs supporta tre tipi di webhook post-chiamata:

  • Webhook di trascrizione (post_call_transcription): contengono i dati completi della conversazione, incluse trascrizioni, risultati dell’analisi e metadati
  • Webhook audio (post_call_audio): contengono dati minimi con l’audio dell’intera conversazione codificato in base64
  • Webhook di errore nell’avvio della chiamata (call_initiation_failure): contengono informazioni sui tentativi di avvio della chiamata non riusciti, incluse le cause dell’errore e i metadati

Abilitare i webhook post-chiamata

Puoi abilitare i webhook post-chiamata per tutti gli agenti nel tuo workspace dalla pagina impostazioni di ElevenAgents.

Impostazioni dei webhook post-chiamata

Per essere considerati riusciti, i webhook post-chiamata devono restituire un codice di stato 200. I webhook che continuano a non riuscire vengono disabilitati automaticamente se si verificano 10 o più errori consecutivi e l’ultima consegna riuscita risale a più di 7 giorni fa o non è mai stata effettuata correttamente.

Se non riescono, i webhook post-chiamata possono essere ritentati automaticamente. Consulta i tentativi dei webhook.

Autenticazione

È importante che il listener convalidi tutti i webhook in arrivo. I webhook supportano attualmente l’autenticazione tramite firme HMAC. Configura l’autenticazione HMAC:

  • Archiviando in modo sicuro il segreto condiviso generato alla creazione del webhook
  • Verificando l’header ElevenLabs-Signature nel tuo endpoint tramite l’SDK

L’SDK JavaScript espone constructEvent; l’SDK Python espone construct_event con rawBody, sig_header e secret (in Python non si chiamano payload / signature). Entrambi verificano la firma, convalidano il timestamp e analizzano il payload JSON.

Esempio di gestore webhook con 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"}

Header di richiesta personalizzati

Oltre alla convalida della firma HMAC, puoi includere header personalizzati in ogni consegna del webhook. È utile per trasmettere chiavi API, bearer token o altri header di autenticazione richiesti dall’endpoint ricevente.

Al momento non puoi configurare gli header personalizzati dalla dashboard di ElevenAgents. Usa l’SDK o l’API per impostarli.

Gli header personalizzati funzionano insieme all’autenticazione HMAC. auth_type deve comunque essere impostato su hmac, e gli header personalizzati vengono inviati in aggiunta all’header ElevenLabs-Signature.

Gli header personalizzati sono in sola scrittura. L’endpoint per elencare i webhook non restituisce request_headers nella risposta, quindi non puoi rileggere gli header configurati per un webhook.

Allowlist degli IP

Per una maggiore sicurezza, puoi aggiungere gli IP di uscita statici di ElevenLabs alla tua allowlist. Consulta l’allowlist degli IP per l’elenco completo degli indirizzi IP.

L’uso dell’allowlist degli IP insieme alla convalida della firma HMAC offre più livelli di sicurezza.

Override dei webhook per agente

Per impostazione predefinita, tutti gli agenti nel tuo workspace usano il webhook configurato nelle impostazioni del workspace. Puoi eseguire l’override per ogni agente, per inviare i dati post-chiamata a endpoint diversi per agenti diversi.

1

Apri le impostazioni dell’agente

Vai al tuo agente nella Dashboard di ElevenAgents e apri Impostazioni.

2

Configura l’override del webhook

Nella sezione Webhook post-chiamata, seleziona un webhook diverso dal menu a discesa per sostituire quello predefinito del workspace per questo agente. Puoi anche configurare i tipi di evento da inviare.

Impostazioni di override del webhook dell'agente

post_call_webhook_id deve fare riferimento a un webhook già esistente nel tuo workspace. Puoi anche specificare i tipi di evento da inviare usando l’array events:

Tipo di eventoDescrizione
transcriptDati della trascrizione con dettagli della conversazione e analisi
audioAudio MP3 dell’intera conversazione codificato in base64
call_initiation_failureInformazioni sui tentativi di avvio della chiamata non riusciti
answering_machine_detectionRisultati del rilevamento della segreteria telefonica (solo chiamate in uscita)
unredacted_transcriptTrascrizione con PII intatte (richiede una conservazione dati appropriata)
unredacted_audioAudio senza oscuramento dei PII (richiede una conservazione dati appropriata)

Struttura della risposta del webhook

ElevenLabs invia tre tipi distinti di webhook post-chiamata, ciascuno con strutture dati diverse:

Webhook di trascrizione (post_call_transcription)

Contengono dati completi sulla conversazione, incluse trascrizioni integrali, risultati dell’analisi e metadati.

Campi di primo livello

CampoTipoDescrizione
typestringTipo di evento (sempre post_call_transcription)
dataobjectDati della conversazione che usano la struttura ConversationHistoryCommonModel
event_timestampnumberMomento in cui si è verificato questo evento, in orario Unix UTC

Struttura dell’oggetto data

L’oggetto data contiene:

CampoTipoDescrizione
agent_idstringL’ID dell’agente che ha gestito la chiamata
agent_namestringIl nome dell’agente al momento della conversazione
conversation_idstringIdentificatore univoco della conversazione
statusstringStato della conversazione (ad es. “done”)
user_idstringIdentificatore dell’utente, se disponibile
branch_idstringIl ramo dell’agente usato per la conversazione, se applicabile
version_idstringL’ID della versione dell’agente (snapshot) attiva durante la chiamata
environmentstringL’ambiente usato per risolvere le variabili d’ambiente
transcriptarrayTrascrizione completa della conversazione con i turni
metadataobjectTempistiche, costi e dettagli telefonici della chiamata
analysisobjectRisultati della valutazione e riepilogo della conversazione
conversation_initiation_client_dataobjectOverride della configurazione e variabili dinamiche
has_audiobooleanIndica se è disponibile dell’audio per la conversazione
has_user_audiobooleanIndica se l’audio dell’utente è disponibile per la conversazione
has_response_audiobooleanIndica se l’audio della risposta dell’agente è disponibile per la conversazione

Webhook audio (post_call_audio)

Contengono dati minimi con l’audio dell’intera conversazione in formato MP3 codificato in base64.

Campi di primo livello

CampoTipoDescrizione
typestringTipo di evento (sempre post_call_audio)
dataobjectDati audio minimi
event_timestampnumberMomento in cui si è verificato questo evento, in orario Unix UTC

Struttura dell’oggetto data

L’oggetto data contiene solo:

CampoTipoDescrizione
agent_idstringL’ID dell’agente che ha gestito la chiamata
conversation_idstringIdentificatore univoco della conversazione
full_audiostringStringa codificata in base64 contenente l’audio completo della conversazione in formato MP3

I webhook audio contengono solo i tre campi elencati sopra. NON includono dati della trascrizione, metadati, risultati dell’analisi o altri dettagli della conversazione.

Webhook di errore nell’avvio della chiamata (call_initiation_failure)

Contengono informazioni sui tentativi di avvio delle chiamate telefoniche, incluse le cause dell’errore e i metadati del provider di telefonia.

Gli eventi webhook relativi agli errori nell’avvio della chiamata vengono inviati quando non è possibile avviare una chiamata per errori di connessione, perché l’utente rifiuta la chiamata o perché non risponde. Se una chiamata viene inoltrata alla segreteria telefonica o riceve risposta da un servizio automatizzato, non viene inviato alcun webhook di errore nell’avvio della chiamata poiché la chiamata è stata avviata correttamente.

Campi di primo livello

CampoTipoDescrizione
typestringTipo di evento (sempre call_initiation_failure)
dataobjectDati dell’errore nell’avvio della chiamata
event_timestampnumberMomento in cui si è verificato questo evento, in orario Unix UTC

Struttura dell’oggetto data

L’oggetto data contiene:

CampoTipoDescrizione
agent_idstringL’ID dell’agente assegnato a gestire la chiamata
conversation_idstringIdentificatore univoco della conversazione
failure_reasonstringLa causa dell’errore (“busy”, “no-answer”, “unknown”)
metadataobjectDati aggiuntivi forniti dal provider di telefonia.

Per le chiamate in uscita effettuate tramite Twilio, failure_reason deriva da CallStatus di Twilio: busy e no-answer vengono trasmessi così come sono, mentre failed viene riportato come unknown. Gli altri stati delle chiamate Twilio non inviano un webhook di errore nell’avvio della chiamata.

Struttura dell’oggetto metadata

La struttura dell’oggetto metadata varia a seconda che la chiamata in uscita sia stata effettuata tramite Twilio o SIP trunking. L’oggetto include un campo type che distingue tra i due e un campo body contenente dettagli specifici del provider.

Metadati SIP (type: "sip"):

CampoTipoObbligatorioDescrizione
typestringSìTipo di provider (sempre sip)
bodyobjectSìInformazioni SIP sull’errore della chiamata

L’oggetto body per i metadati SIP contiene:

CampoTipoObbligatorioDescrizione
from_numbernumberSìIl numero di telefono del soggetto che ha avviato la chiamata.
to_numbernumberSìIl numero di telefono del soggetto chiamato.
sip_status_codenumberSìCodice di stato della risposta SIP (ad es. 486 per occupato)
error_reasonstringSìDescrizione dell’errore leggibile dall’utente
call_sidstringSìIdentificatore della sessione di chiamata SIP
twirp_codestringNoCodice di errore Twirp, se applicabile
sip_statusstringNoTesto dello stato SIP corrispondente al codice di stato

Metadati Twilio (type: "twilio"):

CampoTipoObbligatorioDescrizione
typestringSìTipo di provider (sempre twilio)
bodyobjectSìBody di StatusCallback di Twilio contenente i dettagli della chiamata, documentato qui

Esempi di payload webhook

Esempio di webhook di trascrizione

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

Esempio di webhook audio

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

Esempi di webhook di errore nell’avvio della chiamata

Esempio di metadati 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"
}
}
}
}

Esempio di metadati 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"
}
}
}
}

Consegna dei webhook audio

I webhook audio vengono consegnati separatamente dai webhook di trascrizione e contengono solo i campi essenziali per identificare la conversazione, insieme ai dati audio codificati in base64.

Puoi abilitare o disabilitare i webhook audio usando l’interruttore “Invia dati audio” nelle impostazioni del webhook. Puoi configurare questa impostazione sia a livello di workspace (nelle impostazioni di ElevenAgents) sia a livello di agente (negli override dei webhook dei singoli agenti).

Consegna in streaming

I webhook audio vengono consegnati come richieste HTTP in streaming con l’header transfer-encoding: chunked, per gestire in modo efficiente file audio di grandi dimensioni. Ogni richiesta va in timeout dopo 5 minuti.

Tentativi

Quando i tentativi sono abilitati sul webhook, le consegne audio non riuscite vengono ritentate secondo la stessa pianificazione dei webhook di trascrizione. Un tentativo invia nuovamente il payload audio completo, quindi deduplica in base a conversation_id. Consulta i tentativi dei webhook per la pianificazione, gli errori ritentabili e i limiti di dimensione dell’audio.

Elaborare i webhook audio

Poiché i webhook audio vengono consegnati tramite transfer encoding chunked, devi gestire correttamente i dati in 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

I webhook audio possono essere file di grandi dimensioni, quindi assicurati che l’endpoint del webhook possa gestire richieste in streaming e disponga di memoria e capacità di archiviazione sufficienti. L’audio viene consegnato in formato MP3.

Casi d’uso

Follow-up automatici delle chiamate

I webhook post-chiamata ti consentono di creare workflow automatici che si attivano subito dopo il termine di una chiamata. Ecco alcune applicazioni pratiche:

Integrazione CRM

Aggiorna il tuo sistema di gestione delle relazioni con i clienti con i dati della conversazione non appena una chiamata termina:

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

Conversazioni con stato

Mantieni il contesto della conversazione tra più interazioni memorizzando e recuperando lo stato:

  1. Quando inizia una chiamata, passa l’ID utente come variabile dinamica.
  2. Quando termina una chiamata, configura l’endpoint del webhook per memorizzare i dati della conversazione nel database, in base all’ID utente estratto da dynamic_variables.
  3. Quando l’utente richiama, puoi recuperare questo contesto e passarlo alla nuova conversazione in una variabile dinamica {{previous_topics}}.
  4. In questo modo crei un’esperienza fluida in cui l’agente “ricorda” le interazioni precedenti.
// 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(", "),
},
});
}