Skip to main content

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:

  1. Call Events Webhook: Receives notifications for all call lifecycle events
  2. 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 call
  • workflow_slug: Slug of the workflow being executed
  • phone_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 call
  • workflow_slug: Slug of the workflow that was executed
  • phone_number: Phone number involved in the call (if applicable)
  • duration_seconds: Total call duration in seconds
  • duration_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 call
  • call_slug: Short unique identifier for the call
  • recording_url: Direct URL to download the call recording
  • workflow_id: Database ID of the workflow
  • organization_id: Database ID of the organization
  • egress_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​

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:

  1. Check the call events in your VoiceKit dashboard
  2. Verify webhook delivery status and error messages
  3. Contact support at support@voicekit.com