Call Control API
Programmatically control active calls — play TTS, transfer, hang up, and more.
The Call Control API provides programmatic control over active calls. Use it to play TTS, hang up, transfer calls, send DTMF, or put calls on hold — all via RESTful HTTP/JSON endpoints. This is the imperative counterpart to the declarative dialog FSM.
Base URL
All endpoints are relative to your voicetyped host:
https://gateway.example.com/v1/calls/{id}/...
Replace {id} with the active call session ID. All request and response bodies use JSON with Content-Type: application/json.
Authentication
Include your API token in the Authorization header:
Authorization: Bearer <token>
Endpoints
Play TTS
Render text to speech and play it on an active call.
POST /v1/calls/{id}/tts
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
text | string | yes | Text to speak |
voice | string | no | Voice model (default: server-configured) |
speed | float | no | Playback speed (default 1.0) |
barge_in | boolean | no | Allow caller to interrupt (default true) |
Response body:
| Field | Type | Description |
|---|---|---|
playback_id | string | ID to track this playback |
estimated_duration_ms | integer | Estimated playback duration in milliseconds |
Example:
curl -X POST https://gateway.example.com/v1/calls/call-abc-123/tts \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"text": "Your ticket has been created. The ticket number is 45678.",
"voice": "en_US-amy-medium",
"speed": 1.0,
"barge_in": true
}'
{
"playback_id": "pb-9f2a-4e71",
"estimated_duration_ms": 3200
}
Hangup
Terminate an active call.
POST /v1/calls/{id}/hangup
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
reason | string | no | Reason for hangup: normal, busy, rejected, or error (default normal) |
Response body:
| Field | Type | Description |
|---|---|---|
call_duration_seconds | integer | Total call duration in seconds |
Example:
curl -X POST https://gateway.example.com/v1/calls/call-abc-123/hangup \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"reason": "normal"
}'
{
"call_duration_seconds": 142
}
Transfer
Transfer an active call to another SIP endpoint.
POST /v1/calls/{id}/transfer
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
target_uri | string | yes | SIP URI to transfer to |
type | string | no | Transfer type: blind or attended (default blind) |
headers | object | no | Custom SIP headers as key-value pairs |
Response body:
| Field | Type | Description |
|---|---|---|
success | boolean | Whether the transfer was initiated |
transfer_id | string | ID to track this transfer |
Blind Transfer
The call is immediately redirected to the target URI:
curl -X POST https://gateway.example.com/v1/calls/call-abc-123/transfer \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"target_uri": "sip:[email protected]",
"type": "blind",
"headers": {
"X-Transfer-Reason": "escalation",
"X-Original-Caller": "+15551234567"
}
}'
{
"success": true,
"transfer_id": "xfer-7c3d-88a1"
}
Attended Transfer
The system first establishes a call to the target, then bridges the caller:
curl -X POST https://gateway.example.com/v1/calls/call-abc-123/transfer \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"target_uri": "sip:[email protected]",
"type": "attended",
"headers": {}
}'
Send DTMF
Send DTMF digits on an active call. Useful for navigating external IVR systems.
POST /v1/calls/{id}/dtmf
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
digits | string | yes | Digits to send (0-9, *, #) |
duration_ms | integer | no | Tone duration per digit in milliseconds (default 100) |
gap_ms | integer | no | Gap between digits in milliseconds (default 50) |
Example:
curl -X POST https://gateway.example.com/v1/calls/call-abc-123/dtmf \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"digits": "12345",
"duration_ms": 100,
"gap_ms": 50
}'
{
"success": true
}
Hold
Put an active call on hold.
POST /v1/calls/{id}/hold
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
music_file | string | no | Path to hold music file (WAV, MP3) |
Example:
curl -X POST https://gateway.example.com/v1/calls/call-abc-123/hold \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"music_file": "/path/to/music.wav"
}'
{
"success": true
}
Resume
Resume a held call.
POST /v1/calls/{id}/resume
Request body:
Empty object or omitted.
Example:
curl -X POST https://gateway.example.com/v1/calls/call-abc-123/resume \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{}'
{
"success": true
}
Play Audio
Play a pre-recorded audio file on an active call.
POST /v1/calls/{id}/play-audio
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
file_path | string | yes | Path to audio file (WAV, MP3) |
loop | boolean | no | Loop playback (default false) |
barge_in | boolean | no | Allow caller to interrupt (default true) |
Response body:
| Field | Type | Description |
|---|---|---|
playback_id | string | ID to track this playback |
Example:
curl -X POST https://gateway.example.com/v1/calls/call-abc-123/play-audio \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"file_path": "/var/audio/greeting.wav",
"loop": false,
"barge_in": true
}'
{
"playback_id": "pb-4a8e-c312"
}
Stop Playback
Stop any currently playing audio or TTS.
POST /v1/calls/{id}/stop-playback
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
playback_id | string | no | Stop a specific playback by ID. If omitted, stops all active playback on the call. |
Example:
curl -X POST https://gateway.example.com/v1/calls/call-abc-123/stop-playback \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"playback_id": "pb-9f2a-4e71"
}'
{
"success": true
}
Error Handling
All endpoints return standard HTTP status codes:
| Code | Meaning |
|---|---|
200 OK | Success |
404 Not Found | Session ID does not exist or call has ended |
409 Conflict | Call is in wrong state (e.g., already on hold) |
400 Bad Request | Missing or malformed parameters |
503 Service Unavailable | Service is temporarily unavailable |
500 Internal Server Error | Unexpected error |
Error responses include a JSON body with details:
{
"error": {
"code": 404,
"status": "NOT_FOUND",
"message": "Call session call-abc-123 not found or has already ended."
}
}
Usage Pattern
A common pattern is combining the Call Event Stream API with the Call Control API for reactive call management. Subscribe to a server-sent event stream for real-time call events, then issue control commands in response.
#!/usr/bin/env bash
TOKEN="your-api-token"
BASE="https://gateway.example.com"
# 1. Subscribe to speech events via SSE (runs in the background)
curl -N -H "Authorization: Bearer $TOKEN" \
"$BASE/v1/events?types=speech_final" | while read -r line; do
# Parse the event data (assumes each line is a JSON event)
session_id=$(echo "$line" | jq -r '.session_id // empty')
transcript=$(echo "$line" | jq -r '.speech.transcript // empty')
[ -z "$session_id" ] && continue
# 2. If the caller says "transfer", announce and transfer
if echo "$transcript" | grep -qi "transfer"; then
# Play a TTS announcement
curl -s -X POST "$BASE/v1/calls/$session_id/tts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"text": "Transferring you now. Please hold.",
"barge_in": false
}'
# Wait briefly for the announcement, then transfer
sleep 3
curl -s -X POST "$BASE/v1/calls/$session_id/transfer" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"target_uri": "sip:[email protected]",
"type": "blind",
"headers": {}
}'
fi
done
For production use, consider implementing this pattern in your application language with a proper HTTP client and SSE parser rather than shell scripting.
Next Steps
- Call Event Stream API — subscribe to call events
- Dialog Hooks API — implement backend integration
- Speech API — direct ASR access