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