Ir para a navegação

Webhooks pós-chamada

Receba notificações quando as chamadas terminarem e a análise estiver concluída por meio de webhooks.

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 abrangentes 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 da análise 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 malsucedidas de iniciar chamadas, 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 workspace pela página de configurações do ElevenAgents.

Configurações de webhooks 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 em caso de falha. 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"}

Cabeçalhos de solicitação personalizados

Além da validação de assinatura HMAC, você pode incluir cabeçalhos personalizados em cada entrega de webhook. Isso é útil para enviar chaves de API, tokens bearer ou outros cabeçalhos de autenticação exigidos pelo endpoint receptor.

Os cabeçalhos personalizados não podem ser configurados atualmente pelo painel do ElevenAgents. Use o SDK ou a API para definir cabeçalhos personalizados.

Os cabeçalhos personalizados funcionam junto com a autenticação HMAC. O auth_type ainda precisa ser definido como hmac, e os cabeçalhos personalizados são enviados além do cabeçalho ElevenLabs-Signature.

Os cabeçalhos personalizados são somente para gravação. O endpoint para listar webhooks não retorna request_headers na resposta, portanto, você não pode consultar quais cabeçalhos estão configurados para um webhook.

Lista de permissões de IP

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

Usar uma lista de permissões de IP junto com a validação de assinatura HMAC oferece várias camadas de segurança.

Substituições de webhook por agente

Por padrão, todos os agentes do seu workspace usam o webhook configurado nas configurações do workspace. Você pode substituir essa configuração por agente para enviar dados pós-chamada a endpoints diferentes para agentes diferentes.

1

Abra as configurações do agente

Acesse seu agente no Painel do ElevenAgents e vá para Configurações.

2

Configure a substituição de webhook

Na seção Webhooks pós-chamada, selecione um webhook diferente no menu suspenso para substituir o padrão do workspace para este agente. Você também pode configurar quais tipos de evento enviar.

Configurações de substituição de webhook do agente

O post_call_webhook_id precisa fazer referência a um webhook que já exista no seu workspace. Você também pode especificar quais tipos de evento enviar usando o array events:

Tipo de eventoDescrição
transcriptDados de transcrição com detalhes da conversa e análise
audioÁudio MP3 da conversa completa codificado em base64
call_initiation_failureInformações sobre tentativas malsucedidas de iniciar chamadas
answering_machine_detectionResultados da detecção de secretária eletrônica (somente chamadas de saída)
unredacted_transcriptTranscrição com PII intacta (requer retenção de dados adequada)
unredacted_audioÁudio sem redação de PII (requer retenção de dados adequada)

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 abrangentes da conversa, incluindo transcrições completas, resultados da 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 no 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
metadataobjectHorários, custos e detalhes telefônicos da chamada
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 de 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 em 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 no 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 com o áudio completo da conversa no 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 da análise ou quaisquer 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 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 pode ser iniciada devido a erros de conexão, à recusa da chamada pelo usuário ou à falta de atendimento. 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 de falha ao iniciar chamada
event_timestampnumberQuando este evento ocorreu no 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.

Para chamadas de saída feitas pelo Twilio, failure_reason é derivado de CallStatus do Twilio: busy e no-answer são transmitidos como estão, e failed é informado como unknown. Outros status de chamadas do Twilio não enviam um webhook de falha ao iniciar chamada.

Estrutura do objeto de metadados

A estrutura do objeto metadata varia conforme a chamada de saída foi feita pelo Twilio ou por trunking SIP. O objeto inclui um campo type que diferencia os dois, além de 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 de chamada específicas do 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 da chamada SIP
twirp_codestringNãoCódigo de erro do Twirp, se aplicável
sip_statusstringNãoTexto de status SIP correspondente ao código de status

Metadados do Twilio (type: "twilio"):

CampoTipoObrigatórioDescrição
typestringSimTipo de provedor (sempre twilio)
bodyobjectSimCorpo do StatusCallback do 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 do 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, junto com os dados de áudio codificados em base64.

Os webhooks de áudio podem ser ativados ou desativados usando a opção “Enviar dados de áudio” 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 agentes individuais).

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 de forma eficiente. Cada solicitação expira após 5 minutos.

Tentativas

Quando as tentativas estão ativadas no webhook, as entregas de áudio com falha são repetidas na mesma programação dos webhooks de transcrição. Uma nova tentativa reenvia o payload de áudio completo, portanto, elimine duplicatas usando conversation_id. Consulte as tentativas de webhook para ver a programação, os erros que permitem nova tentativa e os limites de tamanho de áudio.

Processamento de webhooks de áudio

Como os webhooks de áudio são entregues por codificação de transferência em blocos, você precisará processar corretamente os dados de 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

Os webhooks de áudio podem ser arquivos grandes. Portanto, garanta que seu endpoint de webhook possa processar solicitações de streaming e tenha memória/capacidade de armazenamento suficiente. O áudio é entregue no formato MP3.

Casos de uso

Acompanhamentos automatizados de 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 entre várias interações armazenando e recuperando o estado:

  1. Quando uma chamada começar, envie o ID do usuário como 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 de 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 fluida, em que o agente “lembra” as 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(", "),
},
});
}