Webhooks pós-chamada

Receba notificações por webhook quando as chamadas terminarem e a análise for concluída.

Visão geral

Os webhooks pós-chamada permitem receber informações detalhadas sobre uma chamada após a conclusão da análise. Quando ativados, a ElevenLabs enviará uma solicitação POST ao endpoint especificado com dados completos da chamada.

A ElevenLabs oferece suporte a três tipos de webhooks pós-chamada:

  • Webhooks de transcrição (post_call_transcription): Contêm dados completos da conversa, incluindo transcrições, resultados de análises e metadados
  • Webhooks de áudio (post_call_audio): Contêm dados mínimos com o áudio da conversa completa codificado em base64
  • Webhooks de falha ao iniciar chamada (call_initiation_failure): Contêm informações sobre tentativas de início de chamada que falharam, incluindo motivos da falha e metadados

Como ativar webhooks pós-chamada

Os webhooks pós-chamada podem ser ativados para todos os agentes no seu espaço de trabalho pela página de configurações do ElevenAgents.

Configurações de webhook pós-chamada

Os webhooks pós-chamada precisam retornar um código de status 200 para serem considerados bem-sucedidos. Webhooks que falham repetidamente são desativados automaticamente se houver 10 ou mais falhas consecutivas e a última entrega bem-sucedida tiver ocorrido há mais de 7 dias ou nunca tiver sido entregue com sucesso.

Os webhooks pós-chamada podem ser repetidos automaticamente se falharem. Consulte as tentativas de webhook.

Autenticação

É importante que o listener valide todos os webhooks recebidos. Atualmente, os webhooks oferecem suporte à autenticação por assinaturas HMAC. Configure a autenticação HMAC:

  • Armazenando com segurança o segredo compartilhado gerado na criação do webhook
  • Verificando o cabeçalho ElevenLabs-Signature no seu endpoint usando o SDK

O SDK JavaScript disponibiliza constructEvent; o SDK Python disponibiliza construct_event com rawBody, sig_header e secret (em Python, eles não se chamam payload / signature). Ambos verificam a assinatura, validam o carimbo de data e hora e analisam o payload JSON.

Exemplo de manipulador de webhook usando 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"}

Lista de IPs permitidos

Para mais segurança, você pode adicionar os IPs de saída estáticos da ElevenLabs à sua lista de permissões. Consulte lista de IPs permitidos para ver a lista completa de endereços IP.

Usar uma lista de IPs permitidos junto com a validação de assinatura HMAC oferece várias camadas de segurança.

Estrutura da resposta do webhook

A ElevenLabs envia três tipos distintos de webhooks pós-chamada, cada um com estruturas de dados diferentes:

Webhooks de transcrição (post_call_transcription)

Contém dados completos da conversa, incluindo transcrições integrais, resultados de análise e metadados.

Campos de nível superior

CampoTipoDescrição
typestringTipo de evento (sempre post_call_transcription)
dataobjectDados da conversa usando a estrutura ConversationHistoryCommonModel
event_timestampnumberQuando este evento ocorreu, em horário Unix UTC

Estrutura do objeto de dados

O objeto data contém:

CampoTipoDescrição
agent_idstringO ID do agente que atendeu a chamada
agent_namestringO nome do agente no momento da conversa
conversation_idstringIdentificador único da conversa
statusstringStatus da conversa (por exemplo, “done”)
user_idstringIdentificador do usuário, se disponível
branch_idstringA ramificação do agente usada na conversa, se aplicável
version_idstringO ID da versão do agente (snapshot) que estava ativa durante a chamada
environmentstringO ambiente usado para resolver variáveis de ambiente
transcriptarrayTranscrição completa da conversa com turnos
metadataobjectDuração da chamada, custos e detalhes do telefone
analysisobjectResultados da avaliação e resumo da conversa
conversation_initiation_client_dataobjectSubstituições de configuração e variáveis dinâmicas
has_audiobooleanSe há algum áudio disponível para a conversa
has_user_audiobooleanSe o áudio do usuário está disponível para a conversa
has_response_audiobooleanSe o áudio da resposta do agente está disponível para a conversa

Webhooks de áudio (post_call_audio)

Contém dados mínimos com o áudio completo da conversa como MP3 codificado em base64.

Campos de nível superior

CampoTipoDescrição
typestringTipo de evento (sempre post_call_audio)
dataobjectDados mínimos de áudio
event_timestampnumberQuando este evento ocorreu, em horário Unix UTC

Estrutura do objeto de dados

O objeto data contém apenas:

CampoTipoDescrição
agent_idstringO ID do agente que atendeu a chamada
conversation_idstringIdentificador único da conversa
full_audiostringString codificada em base64 contendo o áudio completo da conversa em formato MP3

Os webhooks de áudio contêm apenas os três campos listados acima. Eles NÃO incluem dados de transcrição, metadados, resultados de análise nem outros detalhes da conversa.

Webhooks de falha ao iniciar chamada (call_initiation_failure)

Contém informações sobre tentativas de iniciar chamadas telefônicas, incluindo os motivos da falha e metadados do provedor de telefonia.

Os eventos de webhook de falha ao iniciar chamada são enviados quando uma chamada não consegue ser iniciada devido a erros de conexão, à recusa do usuário em atender ou porque o usuário não atendeu. Se uma chamada cair na caixa postal ou for atendida por um serviço automatizado, nenhum webhook de falha ao iniciar chamada será enviado, pois a chamada foi iniciada com sucesso.

Campos de nível superior

CampoTipoDescrição
typestringTipo de evento (sempre call_initiation_failure)
dataobjectDados da falha ao iniciar chamada
event_timestampnumberQuando este evento ocorreu, em horário Unix UTC

Estrutura do objeto de dados

O objeto data contém:

CampoTipoDescrição
agent_idstringO ID do agente atribuído para atender a chamada
conversation_idstringIdentificador único da conversa
failure_reasonstringO motivo da falha (“busy”, “no-answer”, “unknown”)
metadataobjectDados adicionais fornecidos pelo provedor de telefonia.

Estrutura do objeto de metadados

A estrutura do objeto metadata varia conforme a chamada de saída foi feita via Twilio ou via tronco SIP. O objeto inclui um campo type que diferencia os dois e um campo body com detalhes específicos do provedor.

Metadados SIP (type: "sip"):

CampoTipoObrigatórioDescrição
typestringSimTipo de provedor (sempre sip)
bodyobjectSimInformações de falha da chamada específicas de SIP

O objeto body dos metadados SIP contém:

CampoTipoObrigatórioDescrição
from_numbernumberSimO número de telefone da parte que iniciou a chamada.
to_numbernumberSimO número de telefone da parte chamada.
sip_status_codenumberSimCódigo de status da resposta SIP (por exemplo, 486 para ocupado)
error_reasonstringSimDescrição do erro legível por humanos
call_sidstringSimIdentificador da sessão de chamada SIP
twirp_codestringNãoCódigo de erro Twirp, se aplicável
sip_statusstringNãoTexto de status SIP correspondente ao código de status

Metadados Twilio (type: "twilio"):

CampoTipoObrigatórioDescrição
typestringSimTipo de provedor (sempre twilio)
bodyobjectSimCorpo do StatusCallback da Twilio com detalhes da chamada, documentado aqui

Exemplos de payloads de webhook

Exemplo de webhook de transcrição

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

Exemplo de webhook de áudio

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

Exemplos de webhook de falha ao iniciar chamada

Exemplo de metadados 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"
}
}
}
}

Exemplo de metadados 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"
}
}
}
}

Entrega de webhook de áudio

Os webhooks de áudio são entregues separadamente dos webhooks de transcrição e contêm apenas os campos essenciais para identificar a conversa, juntamente com os dados de áudio codificados em base64.

Os webhooks de áudio podem ser ativados ou desativados usando a opção “Send audio data” nas configurações do webhook. Essa configuração pode ser definida tanto no nível do workspace (nas configurações do ElevenAgents) quanto no nível do agente (nas substituições de webhook de cada agente).

Entrega por streaming

Os webhooks de áudio são entregues como solicitações HTTP por streaming com o cabeçalho transfer-encoding: chunked para processar arquivos de áudio grandes com eficiência.

Processar webhooks de áudio

Como os webhooks de áudio são entregues por meio de codificação de transferência em blocos, você precisará processar os dados de streaming corretamente:

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

Os webhooks de áudio podem ser arquivos grandes; portanto, verifique se seu endpoint de webhook consegue processar solicitações de streaming e se tem capacidade suficiente de memória e armazenamento. O áudio é entregue em formato MP3.

Casos de uso

Acompanhamentos automatizados após chamadas

Os webhooks pós-chamada permitem criar workflows automatizados que são acionados imediatamente após o término de uma chamada. Veja algumas aplicações práticas:

Integração com CRM

Atualize seu sistema de gestão de relacionamento com clientes com dados da conversa assim que uma chamada for concluída:

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

Conversas com estado

Mantenha o contexto da conversa em várias interações armazenando e recuperando o estado:

  1. Quando uma chamada começar, passe o ID do usuário como uma variável dinâmica.
  2. Quando uma chamada terminar, configure seu endpoint de webhook para armazenar os dados da conversa no banco de dados com base no ID do usuário extraído de dynamic_variables.
  3. Quando o usuário ligar novamente, você poderá recuperar esse contexto e passá-lo para a nova conversa em uma variável dinâmica {{previous_topics}}.
  4. Isso cria uma experiência contínua, em que o agente “lembra” das interações anteriores.
// 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(", "),
},
});
}