callback_url you configure on each resource. On your server, read the body and branch on event_type (and message_type where noted). This page covers five callback areas: conversation events (below), guardrails, objectives, face training, and video generation - payload shapes differ, and most event-specific fields live under properties.
Signed callbacks
Setcallback_secret alongside a PAL/persona or conversation callback_url to authenticate its conversation callbacks. Tavus adds an X-Tavus-Signature header containing the lowercase hexadecimal HMAC-SHA256 digest of the exact HTTP request body bytes, using your shared secret encoded as UTF-8.
This field applies to conversation callbacks, with optional defaults saved on the PAL/persona. Configure LLM tool callbacks separately through their delivery.api.auth settings; legacy guardrail and objective callback settings do not use this field.
Secrets must be nonempty strings of at most 256 UTF-8 bytes. They are write-only and are not returned in resource GET or list responses. Omitting a secret on a conversation with no signing default keeps the existing unsigned payload and headers. Signing adds a header without changing the JSON payload shape.
PAL defaults and conversation overrides
Save a default URL and secret together on Create PAL:callback_url also inherits the defaults.
- To change only the signing secret for one conversation, supply
callback_secretand keep the PAL’s default URL. - To override the URL, supply a conversation
callback_url(or itswebhook_urlalias). Supply acallback_secretwith that override to sign its callbacks. An explicit URL without a secret receives unsigned callbacks; the PAL’s secret is not forwarded to the override endpoint. - Without a URL or signing secret at either level, behavior remains unchanged.
POST /v2/conversations/fast.
To set or rotate the PAL’s default secret, Patch PAL using an add operation on /callback_secret. To disable signing while keeping its URL, set /callback_secret to null or remove that field:
Verify the raw request body
Configure your receiver to require signatures once signing is enabled. Verify the signature before parsing or acting on the JSON. Re-serializing parsed JSON can change whitespace or Unicode encoding and invalidate the signature. Keep your shared secret in server-side configuration.Conversation Callbacks
Set a defaultcallback_url on a PAL to receive conversation webhooks for conversations created with that PAL. You can also set callback_url on Create Conversation to choose a URL for an individual conversation. These callbacks report system events (such as the PAL joining and the room shutting down) and application events (such as final transcripts and recording availability).
PAL default and conversation override
The callback URL is resolved when you create the conversation:- A non-empty
callback_urlon Create Conversation takes priority. - If the conversation omits
callback_urlor passes an empty string, the PAL’scallback_urlis used. - If neither has a URL, no conversation callbacks are sent.
pal_id and omit callback_url to use that default:
/webhooks/conversation. Future conversations that omit callback_url still use /webhooks/pal.
To change the PAL’s default later, Patch PAL with an add operation at /callback_url (which also replaces an existing value). To clear the default, replace /callback_url with null.
Callback payload
All Conversation callbacks share the following basic structure. Differences will occur in theproperties object.
Types
Our callbacks are split into two main categories:System Callbacks
These callbacks are to provide insight into system-related events in a conversation. They are:- system.pal_joined: Fired when the PAL is ready for a conversation. Use this in new integrations.
- system.replica_joined: Legacy duplicate of
system.pal_joinedwith an identical payload. Still sent so existing integrations keep working; prefersystem.pal_joinedfor new code. - system.shutdown: This is fired when the room shuts down, for any of the following reasons:
max_call_duration reachedparticipant_left_timeout reachedparticipant_absent_timeout reachedbot_could_not_join_meeting_it_was_probably_endeddaily_room_has_been_deletedexception_encountered_during_conversation_startupend_conversation_endpoint_hitinternal error occurred at step x
Application Callbacks
These callbacks are to inform developers about logical events that take place. They are:- application.transcription_ready: This is fired after ending a conversation, where the chat history is saved and returned. Each transcript entry includes
role,content,timestamp(Unix epoch float, seconds, when the turn began - same field name as live interaction events),seconds_from_start,duration(seconds, float - same field name as thedurationonconversation.stopped_speaking), andinference_id(on assistant turns). When the conversation knows who spoke, a user turn also hasparticipant_name. It is the same name as onconversation.utterance.contentnever includes the name. The same payload is also available on the verbose GET conversation response underevents(look for the event withevent_type: "application.transcription_ready"). - application.recording_ready: This is fired once your recording is durably written to your storage destination. Includes
storage_provider(s3/gcs/azure_blob) and a fully-qualifiedstorage_uriso the same handler works across providers. See Recording Storage to set up GCS, Azure, or S3-in-any-region destinations. - application.recording_copy_failed: This is fired only on the worker-copy path (GCS, Azure, or S3 in regions Daily doesn’t support natively) when Tavus is unable to deliver the recording into your bucket after retries. Daily’s default storage retains the recording for ~30 days as a manual recovery window. Common causes: customer IAM trust policy mismatch, federated credential drift, bucket region typo. Use this as the canary for misconfiguration in your end.
- application.perception_analysis: This is fired after ending a conversation, when the PAL has finished summarizing the visual artifacts that were detected throughout the call. This is a feature that is only available when the PAL has
raven-1specified in the Perception Layer. - application.post_call_action_executed: This is fired once for each post-call action Tavus runs after the conversation ends, recording the outcome (
tool_name,status, and therequest/response). The same payload is also available on the verbose GET conversation response underevents.
Guardrail Callbacks
If acallback_url is provided on a guardrail, a callback is sent when that guardrail is triggered during a conversation.
Objective Callbacks
If acallback_url is provided on an objective, a callback is sent when that objective is completed during a conversation.
Face Training Callbacks
If acallback_url is provided in the POST /faces call, you will receive a callback on face training completion or on face training error.
Dual delivery during the
replica → face rename. While the API transitions from replica to face, each training event triggers two POSTs to the same callback_url: first the legacy payload with replica_id (for backward compatibility), then a second payload with face_id. Both POSTs share the same UUID value, the same status, and, on error, the same error_message - only the id field name differs.To handle this cleanly:- Distinguish the two payloads by which id field is present (
replica_idvs.face_id). - De-duplicate on your side by treating
replica_idandface_idas the same identifier for a given training event. - New integrations should key off
face_id; thereplica_idtwin will be removed once the legacy field is retired.
- Face Training Completed
- Face Training Error
Legacy payload (replica_id)
New payload (face_id)
Video Generation Callbacks
If acallback_url is provided in the POST /videos call, you will receive callbacks on video generation completed and on video error.
- Video Generation Completed
- Video Generation Error
Sample Webhook Setup
Create a sample webhook endpoint using Python Flask, and expose it publicly with ngrok.Prerequisites
1
Step 1: Install Python Dependencies
Install the Python dependencies needed to create the server.
2
Step 2: Make a Webhook Server
Set up a webhook server and save it as The server will receive and process webhook callbacks from Tavus, handle different event types, store transcripts in memory, and analyze conversation data for each session.
server.py.3
Step 3: Run the Server
Run the app using the following command in the terminal:The server should run on port
5000.4
Step 4: Forward the Port Using Ngrok
With ngrok installed and on your PATH, forward the port from a terminal (on Windows you can run The command will generate a forwarding link (e.g., https://1234567890.ngrok-free.app), which can be used as the callback URL.
ngrok.exe from the install folder instead).5
Step 5: Use the Callback URL
Include the callback URL in your request to Tavus by appending
/webhook to the forwarding link and setting it in the callback_url field.Create conversation with callback_url
- Replace
<api-key>with your actual API key. You can generate one in the PAL Maker. - Replace
<face_id>with the Face ID you want to use. - Replace
<pal_id>with the PAL ID you want to use.

