This guide details how to integrate with the Agera Voice Gateway API to manage calls, control media streams, and configure your account programmatically.
All API requests should be authenticated and directed to:
http://<GATEWAY_IP>:8000/api
Authentication is performed using an API Key. Include the key in the HTTP Header:
X-API-KEY: sk_live_...
You can generate API Keys in the Admin Dashboard > API Keys.
These endpoints allow you to manipulate calls.
Initiates a new call from the Gateway to a destination number. The media will be immediately bridged to the configured socket_url or the Account/Extension default.
POST /api/calls
Body:
{
"destination_number": "5511999999999",
"caller_id_number": "5511888888888",
"caller_id_name": "My Business",
"socket_url": "wss://my-bot-server.com/media", // Option A: Direct WebSocket
"webhook_url": "https://my-api.com/route-call" // Option B: Dynamic Routing (Webhook)
}- Note 1: You must provide either
socket_urlORwebhook_url(unless a default is configured). - Note 2: To force a specific native AI session on outbound, you can pass
"socket_url": "elevenlabs"or"socket_url": "vapi". To also force a specific Agent ID without waiting for a webhook, pass it inline using the&delimiter:"socket_url": "elevenlabs&tVvN...or"socket_url": "vapi&abc-123...".
Terminates an active call session.
POST /api/calls/hangup
Body:
{
"call_uuid": "d3b07384-d113-444c-..."
}Blind transfers the call to a new destination (PSTN Number, Extension, or SIP URI).
POST /api/calls/transfer
Body:
{
"call_uuid": "d3b07384-d113-444c-...",
"destination_number": "5511999999999"
}Switches the active Media Driver for the call without hanging up. This is useful for handoffs (e.g., from a basic IVR to a human-like ElevenLabs AI, or between different Bot servers).
POST /api/calls/redirect
Body:
{
"call_uuid": "d3b07384-d113-444c-...",
"media_server": "elevenlabs"
}media_server: Can be"elevenlabs"(native integration) or a WebSocket URL ("wss://myserver.com/bot").
The simplest way to use AI. The Gateway handles the connection to ElevenLabs directly.
- Setup: Configure your DID or Extension to use
ElevenLabsmode in the Dashboard. - Requirements: Add your
Agent IDandAPI Keyin the settings.
Bring Your Own Bot. You host a WebSocket server that receives audio and sends audio back.
When a call connects, the Gateway will initiate a Secure WebSocket (wss://) connection to your server.
- Protocol: JSON Messages
- Audio Codec: G.711 PCMu (Mu-Law) 8kHz
- Packetization: 20ms chunks formatted as Base64 strings.
The Gateway sends media events containing raw audio from the telephone network.
{
"event": "media",
"media": {
"payload": "fRa/739..." // Base64 encoded Mu-Law audio
}
}Your server should decode this Base64 payload, process it (e.g., STT/transcription), and generate a response.
To speak back to the caller, send a media event with your audio encoded in Base64 Mu-Law.
{
"event": "media",
"streamSid": "stream_123...",
"media": {
"payload": "a3f..." // Base64 encoded Mu-Law audio
}
}Ensure your audio is strictly 8000Hz Mono Mu-Law. Sending other formats (like MP3 or PCM) will result in noise or silence.
For the complete list of events (start, stop, mark, clear), refer to PROTOCOL.md.
The Gateway uses Webhooks to route calls dynamically and report status. Two types of webhooks are supported:
- Routing Webhook: Consulted at the start of a call to determine the destination WebSocket.
- Status Callbacks: Asynchronous updates about call progress.
When a call starts (Inbound or Outbound), the Gateway sends a POST request to your configured webhook_url.
- Timeout: 3 seconds (Strict).
- Retry: None. Fallback to default behavior on failure.
Content-Type:application/jsonX-Call-UUID:{call_uuid}X-Signature:sha256={hmac_signature}(HMAC SHA256 of the payload using your Account Secret)User-Agent:VoiceAPI/1.0
{
"event_type": "call.routing_request",
"call_uuid": "550e8400-e29b...",
"timestamp": "2026-02-09T14:30:00Z",
"direction": "inbound", // or "outbound"
"caller_number": "5511999999999",
"destination_number": "5511888888888",
"account_id": "12",
"request_id": "req_789012"
}Your server must respond with 200 OK and a JSON body indicating the action.
Scenario A: Connect to WebSocket Connects the call to your realtime media server.
{
"action": "connect",
"socket_url": "wss://your-bot-server.com/media/stream",
"timeout": 30 // Optional session timeout
}Scenario B: Reject Call Hangs up the call immediately.
{
"action": "hangup",
"reason": "business_hours_closed"
}The Gateway sends asynchronous events to the same webhook_url to report lifecycle changes.
-
call.answered: Sent when the media session is established.{ "event_type": "call.answered", "call_uuid": "...", "timestamp": "...", "call_status": "in-progress" } -
call.completed: Sent when the media session ends.{ "event_type": "call.completed", "call_uuid": "...", "timestamp": "...", "call_status": "completed", "duration": 45, "hangup_cause": "NORMAL_CLEARING" }
All webhook requests include an X-Signature header. You should verify this signature using your Webhook Secret (available in the Dashboard) to ensure the request is authentically coming from the Agera Voice Gateway.
Important: The serialization must be deterministic for the hashes to match. The Gateway generates the HMAC SHA-256 signature using the exact following JSON stringification parameters:
sort_keys=True(Alphabetical order of keys)separators=(',', ':')(No extra spaces between keys and values)
Python Verification Example:
import hmac
import hashlib
import json
def verify_signature(secret_string, payload_dict, signature_header):
if not signature_header or not signature_header.startswith("sha256="):
return False
# 1. Deterministic serialization exactly as sent by Agera
payload_bytes = json.dumps(
payload_dict,
sort_keys=True,
separators=(',', ':'),
ensure_ascii=False
).encode('utf-8')
# 2. Extract the hash from the header
received_signature = signature_header.split("=")[1]
# 3. Generate expected HMAC-SHA256
expected_signature = hmac.new(
secret_string.encode('utf-8'),
payload_bytes,
hashlib.sha256
).hexdigest()
# 4. Secure string comparison
return hmac.compare_digest(received_signature, expected_signature)