Post-call webhooks

Get notified when calls end and analysis is complete through webhooks.

Overview

Post-call Webhooks allow you to receive detailed information about a call after analysis is complete. When enabled, ElevenLabs will send a POST request to your specified endpoint with comprehensive call data, including transcripts, analysis results, and metadata. The data that is returned is the same data that is returned from the Conversation API.

Enabling post-call webhooks

Post-call webhooks can be enabled for all agents in your workspace through the Conversational AI settings page.

Post-call webhook settings

Post call webhooks must return a 200 status code to be considered successful. Webhooks that repeatedly fail are auto disabled if there are 10 or more consecutive failures and the last successful delivery was more than 7 days ago or has never been successfully delivered.

For HIPAA compliance, if a webhook fails we can not retry the webhook.

Authentication

It is important for the listener to validate all incoming webhooks. Webhooks currently support authentication via HMAC signatures. Set up HMAC authentication by:

  • Securely storing the shared secret generated upon creation of the webhook
  • Verifying the ElevenLabs-Signature header in your endpoint using the shared secret

The ElevenLabs-Signature takes the following format:

1t=timestamp,v0=hash

The hash is equivalent to the hex encoded sha256 HMAC signature of timestamp.request_body. Both the hash and timestamp should be validated, an example is shown here:

Example python webhook handler using FastAPI:

1from fastapi import FastAPI, Request
2import time
3import hmac
4from hashlib import sha256
5
6app = FastAPI()
7
8# Example webhook handler
9@app.post("/webhook")
10async def receive_message(request: Request):
11 payload = await request.body()
12 headers = request.headers.get("elevenlabs-signature")
13 if headers is None:
14 return
15 timestamp = headers.split(",")[0][2:]
16 hmac_signature = headers.split(",")[1]
17
18 # Validate timestamp
19 tolerance = int(time.time()) - 30 * 60
20 if int(timestamp) < tolerance
21 return
22
23 # Validate signature
24 full_payload_to_sign = f"{timestamp}.{payload.decode('utf-8')}"
25 mac = hmac.new(
26 key=secret.encode("utf-8"),
27 msg=full_payload_to_sign.encode("utf-8"),
28 digestmod=sha256,
29 )
30 digest = 'v0=' + mac.hexdigest()
31 if hmac_signature != digest:
32 return
33
34 # Continue processing
35
36 return {"status": "received"}

Webhook response structure

The webhook payload contains the same data you would receive from a GET request to the Conversation API endpoint, with additional fields for event timing and type information.

Top-level fields

FieldTypeDescription
typestringType of event (always post_call_transcription in this case)
dataobjectData for the conversation, what would be returned from the API
event_timestampstringWhen this event occurred

Example webhook payload

1{
2 "type": "post_call_transcription",
3 "event_timestamp": 1739537297,
4 "data": {
5 "agent_id": "xyz",
6 "conversation_id": "abc",
7 "status": "done",
8 "transcript": [
9 {
10 "role": "agent",
11 "message": "Hey there angelo. How are you?",
12 "tool_calls": null,
13 "tool_results": null,
14 "feedback": null,
15 "time_in_call_secs": 0,
16 "conversation_turn_metrics": null
17 },
18 {
19 "role": "user",
20 "message": "Hey, can you tell me, like, a fun fact about 11 Labs?",
21 "tool_calls": null,
22 "tool_results": null,
23 "feedback": null,
24 "time_in_call_secs": 2,
25 "conversation_turn_metrics": null
26 },
27 {
28 "role": "agent",
29 "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...",
30 "tool_calls": null,
31 "tool_results": null,
32 "feedback": null,
33 "time_in_call_secs": 9,
34 "conversation_turn_metrics": {
35 "convai_llm_service_ttfb": {
36 "elapsed_time": 0.3704247010173276
37 },
38 "convai_llm_service_ttf_sentence": {
39 "elapsed_time": 0.5551181449554861
40 }
41 }
42 }
43 ],
44 "metadata": {
45 "start_time_unix_secs": 1739537297,
46 "call_duration_secs": 22,
47 "cost": 296,
48 "deletion_settings": {
49 "deletion_time_unix_secs": 1802609320,
50 "deleted_logs_at_time_unix_secs": null,
51 "deleted_audio_at_time_unix_secs": null,
52 "deleted_transcript_at_time_unix_secs": null,
53 "delete_transcript_and_pii": true,
54 "delete_audio": true
55 },
56 "feedback": {
57 "overall_score": null,
58 "likes": 0,
59 "dislikes": 0
60 },
61 "authorization_method": "authorization_header",
62 "charging": {
63 "dev_discount": true
64 },
65 "termination_reason": ""
66 },
67 "analysis": {
68 "evaluation_criteria_results": {},
69 "data_collection_results": {},
70 "call_successful": "success",
71 "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."
72 },
73 "conversation_initiation_client_data": {
74 "conversation_config_override": {
75 "agent": {
76 "prompt": null,
77 "first_message": null,
78 "language": "en"
79 },
80 "tts": {
81 "voice_id": null
82 }
83 },
84 "custom_llm_extra_body": {},
85 "dynamic_variables": {
86 "user_name": "angelo"
87 }
88 }
89 }
90}

Use cases

Automated call follow-ups

Post-call webhooks enable you to build automated workflows that trigger immediately after a call ends. Here are some practical applications:

CRM integration

Update your customer relationship management system with conversation data as soon as a call completes:

1// Example webhook handler
2app.post('/webhook/elevenlabs', async (req, res) => {
3 // HMAC validation code
4
5 const { data } = req.body;
6
7 // Extract key information
8 const userId = data.metadata.user_id;
9 const transcriptSummary = data.analysis.transcript_summary;
10 const callSuccessful = data.analysis.call_successful;
11
12 // Update CRM record
13 await updateCustomerRecord(userId, {
14 lastInteraction: new Date(),
15 conversationSummary: transcriptSummary,
16 callOutcome: callSuccessful,
17 fullTranscript: data.transcript,
18 });
19
20 res.status(200).send('Webhook received');
21});

Stateful conversations

Maintain conversation context across multiple interactions by storing and retrieving state:

  1. When a call starts, pass in your user id as a dynamic variable.
  2. When a call ends, set up your webhook endpoint to store conversation data in your database, based on the extracted user id from the dynamic_variables.
  3. When the user calls again, you can retrieve this context and pass it to the new conversation into a {{previous_topics}} dynamic variable.
  4. This creates a seamless experience where the agent “remembers” previous interactions
1// Store conversation state when call ends
2app.post('/webhook/elevenlabs', async (req, res) => {
3 // HMAC validation code
4
5 const { data } = req.body;
6 const userId = data.metadata.user_id;
7
8 // Store conversation state
9 await db.userStates.upsert({
10 userId,
11 lastConversationId: data.conversation_id,
12 lastInteractionTimestamp: data.metadata.start_time_unix_secs,
13 conversationHistory: data.transcript,
14 previousTopics: extractTopics(data.analysis.transcript_summary),
15 });
16
17 res.status(200).send('Webhook received');
18});
19
20// When initiating a new call, retrieve and use the state
21async function initiateCall(userId) {
22 // Get user's conversation state
23 const userState = await db.userStates.findOne({ userId });
24
25 // Start new conversation with context from previous calls
26 return await elevenlabs.startConversation({
27 agent_id: 'xyz',
28 conversation_id: generateNewId(),
29 dynamic_variables: {
30 user_name: userState.name,
31 previous_conversation_id: userState.lastConversationId,
32 previous_topics: userState.previousTopics.join(', '),
33 },
34 });
35}
Built with