Webhooks
VoiceKit webhooks allow you to receive real-time notifications about call events. Configure webhook URLs in your workflow settings to get notified when calls start, end, or when recordings are finalized.
Overview
Webhooks are HTTP POST requests sent to your specified endpoint when certain events occur during a call. This enables you to:
- Track call progress in real-time
- Update your database when calls complete
- Process recordings immediately after they're available
- Trigger follow-up actions based on call outcomes
Configuration
Configure webhook URLs in your workflow settings:
- Call Events Webhook: Receives notifications for all call lifecycle events
- Call Started Webhook: Optional hook for modifying runtime data when calls begin
{
"eventsWebhookUrl": "https://your-domain.com/webhooks/voicekit",
"callStartedHook": "https://your-domain.com/call-started"
}
Webhook Format
All webhooks follow a consistent format:
{
"event": "event_type",
"timestamp": "2024-01-15T10:30:00.000Z",
"data": {
// Event-specific data
}
}
Event Types
call_started
Triggered when a call begins and the agent connects.
Event Data:
{
"event": "call_started",
"timestamp": "2024-01-15T10:30:00.000Z",
"data": {
"call_id": 123,
"workflow_slug": "customer-support",
"phone_number": "+1234567890"
}
}
Fields:
call_id
: Unique identifier for the callworkflow_slug
: Slug of the workflow being executedphone_number
: Phone number involved in the call (if applicable)
call_ended
Triggered when a call completes and all participants disconnect.
Event Data:
{
"event": "call_ended",
"timestamp": "2024-01-15T10:32:30.000Z",
"data": {
"call_id": 123,
"workflow_slug": "customer-support",
"phone_number": "+1234567890",
"duration_seconds": 150,
"duration_minutes": 3
}
}
Fields:
call_id
: Unique identifier for the callworkflow_slug
: Slug of the workflow that was executedphone_number
: Phone number involved in the call (if applicable)duration_seconds
: Total call duration in secondsduration_minutes
: Total call duration in minutes (minimum 1)
call_finalized
Triggered when call recording is processed and available for download.
Event Data:
{
"event": "call_finalized",
"timestamp": "2024-01-15T10:33:00.000Z",
"data": {
"call_id": 123,
"call_slug": "abc12345",
"recording_url": "https://recordings.voicekit.com/path/to/audio.ogg",
"workflow_id": 456,
"organization_id": 789,
"egress_info": {
"egress_id": "EG_dmi8xHQYNX6r",
"room_id": "RM_kfWfYGzHvVYy",
"room_name": "VOICEKIT_workflow_abc12345_8453",
"status": "EGRESS_COMPLETE",
"started_at": "1749599674369480049",
"ended_at": "1749599765791839485",
"duration": "88756989820",
"file_size": "1442477",
"file_type": "ogg"
}
}
}
Fields:
call_id
: Unique identifier for the callcall_slug
: Short unique identifier for the callrecording_url
: Direct URL to download the call recordingworkflow_id
: Database ID of the workfloworganization_id
: Database ID of the organizationegress_info
: Detailed recording metadata from LiveKit
Security
Webhook Verification
VoiceKit webhooks don't currently include signature verification. Ensure your webhook endpoints:
- Use HTTPS for secure transmission
- Validate the webhook payload structure
- Implement idempotency to handle duplicate deliveries
- Use allowlisting if your endpoint is publicly accessible
Best Practices
// Example webhook handler with validation
app.post('/webhooks/voicekit', (req, res) => {
try {
const { event, timestamp, data } = req.body;
// Validate required fields
if (!event || !timestamp || !data) {
return res.status(400).json({ error: 'Invalid webhook payload' });
}
// Process event based on type
switch (event) {
case 'call_started':
await handleCallStarted(data);
break;
case 'call_ended':
await handleCallEnded(data);
break;
case 'call_finalized':
await handleCallFinalized(data);
break;
default:
console.log(`Unknown event type: ${event}`);
}
res.json({ received: true });
} catch (error) {
console.error('Webhook error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
Error Handling
Retry Logic
VoiceKit automatically retries failed webhook deliveries:
- Timeout: 10 seconds per attempt
- Retry Policy: Currently no automatic retries (webhook failures are logged)
- Status Codes: 2xx responses are considered successful
Debugging
Webhook delivery status is tracked in your call events. Check the VoiceKit dashboard or API to see:
- Webhook delivery attempts
- Response status codes
- Error messages for failed deliveries
Testing Webhooks
Local Development
Use tools like ngrok to test webhooks locally:
# Expose local server
ngrok http 3000
# Use the ngrok URL in your workflow webhook settings
https://abc123.ngrok.io/webhooks/voicekit
Webhook Testing Tools
- webhook.site: Inspect webhook payloads
- RequestBin: Capture and debug webhooks
- Postman: Mock webhook endpoints
Examples
Node.js/Express
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhooks/voicekit', async (req, res) => {
const { event, data } = req.body;
switch (event) {
case 'call_started':
console.log(`Call ${data.call_id} started`);
break;
case 'call_ended':
console.log(`Call ${data.call_id} ended after ${data.duration_minutes} minutes`);
break;
case 'call_finalized':
console.log(`Recording available: ${data.recording_url}`);
// Download and process the recording
await processRecording(data.recording_url);
break;
}
res.json({ success: true });
});
Python/Flask
from flask import Flask, request, jsonify
import requests
app = Flask(__name__)
@app.route('/webhooks/voicekit', methods=['POST'])
def handle_webhook():
payload = request.get_json()
event = payload.get('event')
data = payload.get('data')
if event == 'call_started':
print(f"Call {data['call_id']} started")
elif event == 'call_ended':
print(f"Call {data['call_id']} ended after {data['duration_minutes']} minutes")
elif event == 'call_finalized':
print(f"Recording available: {data['recording_url']}")
# Download and process the recording
process_recording(data['recording_url'])
return jsonify({'success': True})
def process_recording(recording_url):
# Download the recording file
response = requests.get(recording_url)
with open('call_recording.ogg', 'wb') as f:
f.write(response.content)
print("Recording downloaded successfully")
Troubleshooting
Common Issues
Webhook Not Receiving Events
- Verify the webhook URL is correct and publicly accessible
- Check that your endpoint accepts POST requests
- Ensure your server responds with 2xx status codes
Duplicate Events
- Implement idempotency using the call_id or timestamp
- Check for network issues that might cause retries
Missing Events
- Verify webhook URL is configured in workflow settings
- Check server logs for delivery failures
- Ensure your endpoint doesn't timeout (10 second limit)
Getting Help
If you're having trouble with webhooks:
- Check the call events in your VoiceKit dashboard
- Verify webhook delivery status and error messages
- Contact support at support@voicekit.com