How to Build an AI Voice Agent with Callsavvy Call Control API
Introduction
Callsavvy's Call Control Webhook API allows you to build intelligent, interactive voice agents that can handle inbound calls, process DTMF inputs, recognize speech, and control call flows in real time. This guide walks you through setting up a Callsavvy account, obtaining your API credentials, and building your first AI-powered voice agent.
Step 1: Create a Callsavvy Account and Get Your API Credentials
Before you can use the Call Control API, you need to sign up for a Callsavvy account and retrieve your API keys.
1.1 Sign Up
Visit the Callsavvy website and create a new account. You will need administrative privileges to access API settings.
1.2 Locate Your API Credentials
Once logged in as an Admin:
- Navigate to Settings → API & Integration from your Callsavvy workplace dashboard.
- Your credentials will be displayed here, including:
- Public Key – Used to identify your account
- Private Key – Used to authenticate API requests
1.3 Security Best Practices
For added security, Callsavvy recommends restricting access to your API credentials by whitelisting only trusted IP addresses and domains. You can configure this under Whitelisted Domains in the same API settings section.
1.4 Authentication
All API requests require authentication via HTTP headers over TLS (HTTPS). Include the following headers in every request:
| Header | Value |
|---|---|
Public-Key |
Your API Public Key |
Private-Key |
Your API Private Key |
Step 2: Understanding the Call Control Webhook Architecture
Callsavvy uses a webhook-based architecture where your server responds to incoming call events with JSON instructions that control the call flow.
How It Works
Caller → Callsavvy → Your Webhook URL → Your Server → JSON Response → Callsavvy executes action
- An inbound call triggers a
POSTrequest to your callback URL - Your server responds with a JSON payload specifying what action to take
- Callsavvy executes that action (play audio, gather DTMF, bridge call, etc.)
- Subsequent events (DTMF, speech completion) trigger additional webhook calls
Required Webhook Endpoints
You need to implement a publicly accessible HTTPS endpoint that can receive and respond to these event types:
| Event | Trigger |
|---|---|
inboundCall |
New incoming call received |
callProgress |
Call status changes (ringing, answered, ended, etc.) |
dtmfReceived |
Caller presses digits on their keypad |
speechCompleted |
AI speech recognition finishes processing |
Step 3: Building Your AI Voice Agent
3.1 Basic Webhook Response Structure
All webhook responses must follow this JSON format:
{
"action": "call_control_action",
"action_parameters": {
"parameter_name": "parameter_value"
},
"next_action": "next_call_control_action",
"next_action_parameters": {
"parameter_name": "parameter_value"
}
}
3.2 Creating an AI-Powered Voice Agent
The voiceAI action connects the call to Callsavvy's native AI voice agent. Here's a complete example that handles an inbound call with an AI agent:
Inbound Call Webhook Response:
{
"action": "voiceAI",
"action_parameters": {
"speech_timeout": 3,
"language": "en-US",
"voice": "female",
"branding_name": "My Business",
"branding_description": "A customer support AI assistant",
"workflows": {
"workflow_id_1": true,
"workflow_id_2": true
},
"Knowledgebase_category": {
"category_id_1": "support",
"category_id_2": "sales"
}
}
}
Parameter Reference:
| Parameter | Description | Constraints |
|---|---|---|
speech_timeout |
Seconds to wait for speech input | Number between 1 and 5 |
language |
Language for speech recognition | See supported languages |
voice |
TTS voice gender/style | See supported voices |
workflows |
Object of workflow IDs to associate | Object format |
Knowledgebase_category |
Object of knowledge base categories | Object format |
branding_name |
Business name for AI context | String |
branding_description |
AI role description | String |
3.3 Handling DTMF Input (Menus)
Use gatherDTMF to collect keypad input from callers, such as menu selections or account numbers.
Example: Collect a 1-digit menu selection
{
"action": "gatherDTMF",
"action_parameters": {
"dtmf_digits": "1"
}
}
Example: Collect a 4-digit extension
{
"action": "gatherDTMF",
"action_parameters": {
"dtmf_length": 4
}
}
When digits are received, Callsavvy sends a dtmfReceived webhook containing the digits.
3.4 Playing Audio and Text-to-Speech
Play an MP3 file:
{
"action": "playAudio",
"action_parameters": {
"audio_url": "https://your-server.com/welcome.mp3"
},
"next_action": "gatherDTMF",
"next_action_parameters": {
"dtmf_length": 1
}
}
Read text aloud using TTS:
{
"action": "readText",
"action_parameters": {
"text": "Thank you for calling. Please hold while I connect you.",
"language": "en-US",
"voice": "female"
}
}
3.5 Bridging Calls
Dial an external number:
{
"action": "dialNumber",
"action_parameters": {
"phone_number": "+14155551234"
}
}
Dial a SIP endpoint (only as next_action):
{
"action": "playAudio",
"action_parameters": {
"audio_url": "https://your-server.com/connecting.mp3"
},
"next_action": "dialSIP",
"next_action_parameters": {
"sip_host": "sip.example.com",
"sip_port": 5060,
"sip_username": "user",
"sip_password": "password"
}
}
Step 4: Call Progress and Disposition Tracking
The callProgress webhook provides real-time updates about call state. Here's an example payload:
{
"event": "callProgress",
"call_status": "answered",
"call_disposition": "answered",
"cost": 0.025,
"call_duration": 45,
"recording_url": "https://callsavvy.com/recordings/abc123.mp3",
"from": "+14155551234",
"to": "+14155556789",
"session_id": "session_123",
"call_id": "encrypted_call_id_456",
"call_direction": "inbound"
}
Call Status Values
| Status | Description |
|---|---|
ringing | Call is ringing |
answered | Call was answered |
ended | Call has ended |
enqueued | Call placed in queue |
dequeued | Call removed from queue |
agent_left | Agent disconnected |
audio_playback_completed | Audio finished playing |
Call Disposition Values
| Disposition | Description |
|---|---|
answered | Call was successfully answered |
not answered | Caller did not answer |
failed | Call failed due to error |
no response | No response from called party |
busy | Line was busy |
canceled | Call was canceled |
Step 5: Important Rules and Limitations
⚠️ CRITICAL WARNING: If your webhook returns an invalid or unprocessable JSON response, Callsavvy will play an error message to the caller and the call will fail to continue. Always validate your response format.
Actions that CANNOT have a next_action
These actions are terminal or blocking:
voiceAIdialNumberhangupsilencedialSIPgatherDTMF
Actions that CANNOT be used as next_action
These can only be primary actions:
readTextplayAudio
Valid Action Combinations
| Primary Action | Can have next_action? | Allowed next_action values |
|---|---|---|
readText |
✅ Yes | readText, playAudio, hangup, silence |
playAudio |
✅ Yes | readText, playAudio, hangup, silence |
voiceAI |
❌ No | None |
dialNumber |
❌ No | None |
hangup |
❌ No | None |
silence |
❌ No | None |
dialSIP |
❌ No | None |
gatherDTMF |
❌ No | None |
enqueue |
✅ Yes | readText, playAudio, hangup, silence |
Complete Example: IVR Menu with AI Fallback
Here's a complete call flow that:
- Plays a welcome message
- Collects a 1-digit menu selection
- Routes to AI agent for selections 1-2, or loops back for invalid input
Initial Webhook Response (inboundCall)
{
"action": "playAudio",
"action_parameters": {
"audio_url": "https://your-server.com/welcome.mp3"
},
"next_action": "gatherDTMF",
"next_action_parameters": {
"dtmf_digits": "1,2,3"
}
}
Handling dtmfReceived Webhook
When DTMF is received, your webhook should respond based on the digits:
{
"action": "voiceAI",
"action_parameters": {
"speech_timeout": 3,
"language": "en-US",
"voice": "female",
"branding_name": "Support Center",
"branding_description": "Customer support AI assistant"
}
}
Handling callProgress for Call Tracking
{
"action": "silence",
"action_parameters": {}
}
Step 6: Testing Your Implementation
- Use a tool like Postman to mock webhook requests and test your JSON responses
- Set up a staging endpoint with a publicly accessible HTTPS URL (use ngrok for local testing)
- Configure your callback URL in Callsavvy under Settings → API & Integration
- Place a test call to your Callsavvy number and monitor your server logs
- Verify proper JSON formatting – invalid responses will cause call failures with error messages
Full List of Call Control Actions
| Action | Description | Parameters |
|---|---|---|
voiceAI |
Native AI voice agent | speech_timeout, language, voice, workflows, Knowledgebase_category, branding_name, branding_description |
readText |
Text-to-speech playback | text, language, voice |
playAudio |
Play MP3 file | audio_url |
dialNumber |
Bridge to phone number | phone_number |
dialSIP |
Bridge to SIP endpoint (next_action only) | sip_host, sip_port, sip_username, sip_password |
enqueue |
Place call in queue | hold_music_url |
gatherDTMF |
Collect DTMF input | dtmf_digits OR dtmf_length |
hangup |
End the call | None |
silence |
Do nothing | None |
Conclusion
The Callsavvy Call Control Webhook API provides a flexible foundation for building sophisticated AI voice agents. By implementing the webhook endpoints described in this guide and following the response formatting rules, you can create interactive phone systems that handle menus, speech recognition, call routing, and AI-powered conversations.
For a complete reference of supported languages and voices, visit the Callsavvy API documentation.