No AI lock-in. No prompt migration. No vendor TTS.
Just HTTP webhooks with WAV files. You control the AI. We control the phone call.
We just give you the audio. You already solved the AI part.
Can't reuse your existing AI workflows
Prompts are platform-specific
Migrate everything or start over
Use your existing n8n/Make workflows
Prompts work anywhere (it's your code)
Just add 3 nodes to what you have
You've already figured out STT, TTS, and LLM prompting. You've got n8n workflows, Make.com scenarios, or custom API integrations that work perfectly.
Why rebuild all that just to add phone calls?
We handle telephony (SIP, VAD, DTMF, conferencing). You handle AI. Simple HTTP webhooks connect the two. That's it.
Call control, audio webhooks (WAV segments), VAD modes, and conference bridges
POST /v1/dial
Authorization: Bearer YOUR_API_KEY
{
"to": "+13105551234",
"from": "+17025559999"
}
{
"success": true,
"message": "Call initiated successfully",
"call_uuid": "86fd19ef-d17d-482f-bbda-65287272ac22",
"details": {
"to_number": "+13105551234",
"from_did": "+17025559999",
"customer_slug": "your-company-abc123"
},
"response_time_ms": 154
}
GET /v1/calls
Authorization: Bearer YOUR_API_KEY
{
"success": true,
"count": 1,
"calls": [
{
"call_id": "bc1473dd-73b9-42c6-8a88-11518ff615c6",
"status": "active",
"direction": "outbound",
"caller_id": "17029860828",
"caller_name": "unknown",
"did": "+17029860828",
"started_at": "2025-11-05 17:00:28"
}
]
}
GET /v1/call/{call_uuid}
Authorization: Bearer YOUR_API_KEY
{
"success": true,
"call_id": "bc1473dd-73b9-42c6-8a88-11518ff615c6",
"status": "completed",
"direction": "outbound",
"caller_id": "17029860828",
"caller_name": "unknown",
"did": "+17029860828",
"initiated_at": "2025-11-05 17:00:28",
"hangup_at": "2025-11-05 17:00:30",
"duration": 2
}
POST /v1/call/{call_uuid}/hangup
Authorization: Bearer YOUR_API_KEY
{
"success": true,
"message": "Hangup request sent",
"call_uuid": "86fd19ef-d17d-482f-bbda-65287272ac22"
}
POST /v1/pre-answer/{call_uuid}
Authorization: Bearer YOUR_API_KEY
{
"action": "answer"
}
{
"success": true,
"message": "Pre-answer action set to answer",
"call_uuid": "86fd19ef-d17d-482f-bbda-65287272ac22",
"action": "answer"
}
action: "answer" (accept call) or "reject" (hang up without answering). Used during pre-answer webhook timeout window to control call before it's answered. Only effective for inbound calls with pre-answer screening enabled.
POST /v1/calls/join
Authorization: Bearer YOUR_API_KEY
{
"call_ids": [
"call_550e8400-e29b-41d4-a716-446655440000",
"call_550e8401-e29c-41d4-a716-446655440001"
]
}
{
"success": true,
"message": "Bridge established",
"bridge_type": "same-server",
"response_time_ms": 45
}
Connect two active calls. Works across servers automatically. Both calls must be active.
POST /v1/calls/unjoin
Authorization: Bearer YOUR_API_KEY
{
"call_ids": [
"call_550e8400-e29b-41d4-a716-446655440000",
"call_550e8401-e29c-41d4-a716-446655440001"
]
}
{
"success": true,
"message": "Cross-server unjoin complete",
"unjoin_type": "cross-server",
"call1": {
"unjoined": true,
"status": "active"
},
"call2": {
"unjoined": true,
"status": "active"
},
"response_time_ms": 78
}
Disconnect two bridged calls and return them to AudioSocket. Both calls must be active.
curl -X POST https://api.monkeydial.com/v1/call/{call_uuid}/record/start \
-H "X-API-Key: YOUR_API_KEY"
const response = await fetch(
`https://api.monkeydial.com/v1/call/${callUuid}/record/start`,
{
method: 'POST',
headers: { 'X-API-Key': 'YOUR_API_KEY' }
}
);
const data = await response.json();
import requests
response = requests.post(
f"https://api.monkeydial.com/v1/call/{call_uuid}/record/start",
headers={"X-API-Key": "YOUR_API_KEY"}
)
data = response.json()
{
"success": true,
"recording_id": "call_rec_550e8400_2511071430_1",
"call_uuid": "550e8400-e29b-41d4-a716-446655440000",
"status": "recording",
"hub_server": "audiobot-asterisk-1"
}
Starts recording an active call. Multiple recordings can be started per call (sequence numbers: _1, _2, _3). Recording captures both sides (caller + system audio) mixed into a single MP3 file. Recording automatically stops when call ends.
curl -X POST https://api.monkeydial.com/v1/call/{call_uuid}/record/stop \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"recording_id": "call_rec_550e8400_2511071430_1"}'
const response = await fetch(
`https://api.monkeydial.com/v1/call/${callUuid}/record/stop`,
{
method: 'POST',
headers: {
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
recording_id: 'call_rec_550e8400_2511071430_1' // Optional
})
}
);
const data = await response.json();
import requests
response = requests.post(
f"https://api.monkeydial.com/v1/call/{call_uuid}/record/stop",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"recording_id": "call_rec_550e8400_2511071430_1"} # Optional
)
data = response.json()
{
"success": true,
"recording_id": "call_rec_550e8400_2511071430_1",
"status": "processing"
}
Stops active recording and triggers MP3 conversion. If recording_id is not provided, stops the most recent recording. Processing typically completes within 1-2 seconds. Webhook "call_recording_ready" is sent when MP3 is available.
curl -X GET https://api.monkeydial.com/v1/call/recording/{recording_id} \
-H "X-API-Key: YOUR_API_KEY" \
-o recording.mp3
const response = await fetch(
`https://api.monkeydial.com/v1/call/recording/${recordingId}`,
{ headers: { 'X-API-Key': 'YOUR_API_KEY' } }
);
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
fs.writeFileSync('recording.mp3', buffer);
import requests
response = requests.get(
f"https://api.monkeydial.com/v1/call/recording/{recording_id}",
headers={"X-API-Key": "YOUR_API_KEY"}
)
with open('recording.mp3', 'wb') as f:
f.write(response.content)
Content-Type: audio/mpeg
Content-Disposition: attachment; filename="{recording_id}.mp3"
Content-Length: 1480000
Downloads MP3 recording file. Only available when status is "ready" (check webhook or poll status). Returns 404 if recording is still processing or not found.
{
"event": "call_recording_ready",
"call_uuid": "550e8400-e29b-41d4-a716-446655440000",
"recording_id": "call_rec_550e8400_2511071430_1",
"customer_id": "acme-corp",
"duration_seconds": 185,
"file_size_bytes": 1480000,
"format": "mp3",
"download_url": "https://api.monkeydial.com/v1/call/recording/call_rec_550e8400_2511071430_1",
"started_at": "2025-11-07T14:30:00Z",
"stopped_at": "2025-11-07T14:33:05Z"
}
Sent when MP3 conversion completes and file is ready for download. Triggered after manual stop or automatic stop on call hangup.
{
"event": "call_start",
"call_id": "86fd19ef-d17d-482f-bbda-65287272ac22",
"customer_id": "acme-corp-x7k2mn",
"did": "+17029860828",
"caller_id": "+13105551234",
"caller_name": "John Doe",
"direction": "inbound",
"timestamp": "2025-11-05T19:14:06+00:00"
}
{
"event": "call_end",
"call_uuid": "86fd19ef-d17d-482f-bbda-65287272ac22",
"customer_id": "acme-corp-001",
"did": "+17029860828",
"caller_id": "+13105551234",
"caller_name": "John Doe",
"status": "completed",
"error_reason": null,
"duration": 120,
"timestamp": "2025-11-05T10:32:00+00:00"
}
status: "completed" or "failed" | duration: total call duration in seconds
{
"event": "call_pre_answer",
"timestamp": "2025-11-10T04:28:56Z",
"call_uuid": "4ea15232-5384-41dd-9291-805f18995211",
"customer_slug": "acme-corp-x7k2mn",
"did": "+17029860828",
"caller_id": "+14073461117",
"caller_name": "John Doe",
"direction": "inbound"
}
Fired before answering inbound calls when pre-answer screening is enabled. Gives you a configurable timeout window (1-60 seconds) to decide whether to accept or reject the call. Use POST /v1/pre-answer/{call_uuid} with {"action": "answer"} or {"action": "reject"} to control the call. If no action is taken within the timeout, the configured default action (answer or reject) is applied. Perfect for spam filtering, business hours validation, or caller screening.
POST /v1/vad/{call_uuid}/playback_audio
Authorization: Bearer YOUR_API_KEY
Content-Type: multipart/form-data
audio_file: [binary audio file]
{
"success": true,
"message": "Audio routed successfully",
"call_uuid": "call_550e8400-e29b..."
}
Upload audio file - queues multiple files. Supports WAV, MP3, μ-law.
POST /v1/vad/{call_uuid}/playback_audio/replace
Authorization: Bearer YOUR_API_KEY
Content-Type: multipart/form-data
audio_file: [binary audio file]
{
"success": true,
"message": "Audio routed successfully",
"call_uuid": "call_550e8400-e29b..."
}
Stops current playback and plays immediately.
POST /v1/vad/{call_uuid}/playback_audio/stop
Authorization: Bearer YOUR_API_KEY
{
"success": true,
"message": "Playback stopped",
"call_uuid": "call_550e8400..."
}
Stops current playback and clears queue.
POST /v1/vad/{call_uuid}/vad_mode
Authorization: Bearer YOUR_API_KEY
{
"vad_mode": "block"
}
DTMF detection works in all VAD modes. VAD automatically switches to mode 2 (blocked) during playback to prevent echo.
POST /v1/vad/{call_uuid}/record
Authorization: Bearer YOUR_API_KEY
{
"duration": 30
}
{
"success": true,
"message": "Recording started",
"uuid": "call_550e8400...",
"duration": 30
}
Record 1-60 seconds. Webhook sent when complete.
POST https://your-app.com/webhook
Content-Type: application/json
{
"event": "audio",
"call_id": "call_550e8400-e29b...",
"audio_url": "https://storage.md.com/seg_001.wav",
"duration_ms": 1500,
"caller_id": "+13105551234",
"did": "+17025559999"
}
Audio segments delivered in real-time as VAD detects speech. WAV format, 8kHz-48kHz.
{
"event": "dtmf",
"uuid": "86fd19ef-d17d-482f-bbda-65287272ac22",
"digit": "1",
"customer_id": "acme-corp-001",
"did": "+17029860828",
"caller_id": "+13105551234",
"timestamp": "2025-11-05T10:31:30+00:00"
}
Supports digits 0-9, *, #, A-D
event: recording
uuid: 86fd19ef-d17d-482f-bbda-65287272ac22
customer_id: acme-corp-001
did: +17029860828
duration: 5
actual_duration: 5.2
partial: false
audio_data: [WAV file - 8kHz, 16-bit, mono]
Sent when timed recording completes. Contains full audio as WAV.
POST /v1/conference/create
Authorization: Bearer YOUR_API_KEY
{
"name": "support-call-123",
"webhook_url": "https://yourdomain.com/webhooks/conference"
}
{
"success": true,
"conference_id": "conf_550e8400-e29b-41d4-a716-446655440000",
"name": "support-call-123",
"created_at": "2025-11-06T21:27:56+00:00"
}
POST /v1/conference/{conf_id}/join
Authorization: Bearer YOUR_API_KEY
{
"call_ids": ["call_550e8400-e29b...", "call_abc123..."]
}
{
"success": true,
"conference_id": "conf_550e8400-e29b-41d4-a716-446655440000",
"joined": [
{"call_id": "call_550e8400-e29b...", "status": "joined"}
],
"failed": [],
"participant_count": 2
}
GET /v1/conference/{conf_id}/list
Authorization: Bearer YOUR_API_KEY
{
"success": true,
"conference": {
"conference_id": "conf_550e8400-e29b...",
"name": "Sales Call",
"status": "active",
"created_at": "2025-11-06T21:27:56Z",
"max_participants": 10,
"participant_count": 2,
"hub": "audiobot-asterisk-2"
},
"participants": [
{
"call_uuid": "call_abc123",
"caller_id": "+13105551234",
"did": "+17025559999",
"joined_at": "2025-11-06T21:28:15Z",
"duration_in_conference": 145,
"is_muted": false
}
]
}
Get conference metadata and real-time participant list with join times and durations. Perfect for dashboards.
POST /v1/conference/{conf_id}/leave/{call_uuid}
Authorization: Bearer YOUR_API_KEY
{
"post_conference_action": "VAD"
}
{
"success": true,
"message": "Participant redirected to VAD",
"call_id": "call_550e8400-e29b-41d4-a716-446655440000",
"conference_id": "conf_550e8400-e29b-41d4-a716-446655440000",
"action": "VAD"
}
Remove a single participant from conference. Choose whether to return to VAD mode or hang up.
POST /v1/conference/{conf_id}/kick/{call_uuid}
Authorization: Bearer YOUR_API_KEY
{
"success": true,
"message": "Participant kicked from conference",
"call_id": "call_550e8400-e29b-41d4-a716-446655440000",
"conference_id": "conf_550e8400-e29b-41d4-a716-446655440000"
}
Forcibly remove and hang up a participant. Use /leave to return to VAD instead.
POST /v1/conference/{conf_id}/record/start
Headers: x-api-key: YOUR_API_KEY
Response:
{
"success": true,
"conference_id": "conf_550e8400-e29b-41d4-a716-446655440000",
"recording_id": "conf_rec_a1b2c3d4_2511071030",
"status": "recording",
"hub_server": "audiobot-asterisk-2"
}
Start recording conference audio. Only one recording per conference at a time. Supports multiple segments (stop/start creates separate files). MP3 format, ~190kbps VBR.
POST /v1/conference/{conf_id}/record/stop
Headers: x-api-key: YOUR_API_KEY
Response:
{
"success": true,
"recording_id": "conf_rec_a1b2c3d4_2511071030",
"conference_id": "conf_550e8400-e29b-41d4-a716-446655440000",
"status": "ready",
"file_size_bytes": 1810000,
"duration_seconds": 1205,
"format": "mp3"
}
Stop recording and convert WAV to MP3. Triggers conference_recording_ready webhook. File available for download immediately.
GET /v1/conference/recording/{recording_id}
Headers: x-api-key: YOUR_API_KEY
Response:
Content-Type: audio/mpeg
Content-Disposition: attachment; filename="conf_rec_a1b2c3d4_2511071030.mp3"
[Binary MP3 data]
Download conference recording MP3 file. Customer-isolated access. Supports Range requests for partial downloads.
POST /v1/conference/{conf_id}/play
Headers: x-api-key: YOUR_API_KEY
Content-Type: multipart/form-data
Body:
file: [audio file - MP3, WAV, OGG, M4A]
Response:
{
"success": true,
"playback_id": "conf_playback_690e6ea1261050_72120897",
"conference_id": "conf_abc123",
"duration_seconds": 29.57
}
Play audio file into active conference. All participants hear the audio. Supports MP3, WAV, OGG, M4A formats. Auto-converts to Asterisk format (16kHz mono). Temporary channel joins conference, plays audio, then leaves.
{
"event": "conference_created",
"conference_id": "conf_550e8400-e29b-41d4-a716-446655440000",
"name": "support-call-123",
"customer_id": "acme-corp-x7k2mn",
"max_participants": 10,
"created_at": "2025-11-06T21:27:56+00:00"
}
Sent when a new conference room is created.
{
"event": "conference_participant_joined",
"conference_id": "conf_550e8400-e29b-41d4-a716-446655440000",
"call_id": "call_abc123",
"caller_id": "+13105551234",
"participant_count": 2,
"timestamp": "2025-11-06T21:28:15+00:00"
}
Sent when a participant joins the conference.
{
"event": "conference_participant_left",
"conference_id": "conf_550e8400-e29b-41d4-a716-446655440000",
"call_id": "call_abc123",
"reason": "left",
"duration_in_conference": 295,
"participant_count": 1,
"timestamp": "2025-11-06T21:33:10+00:00"
}
Sent when a participant leaves the conference. Includes reason and duration.
{
"event": "conference_ended",
"conference_id": "conf_550e8400-e29b-41d4-a716-446655440000",
"reason": "deleted",
"duration": 610,
"total_participants": 3,
"timestamp": "2025-11-06T21:38:06+00:00"
}
Sent when conference is deleted or ends. Includes total duration and participant count.
{
"event": "conference_recording_ready",
"conference_id": "conf_550e8400-e29b-41d4-a716-446655440000",
"recording_id": "conf_rec_a1b2c3d4_2511071030",
"customer_id": "acme-corp-x7k2mn",
"duration_seconds": 1205,
"file_size_bytes": 1810000,
"format": "mp3",
"download_url": "https://api.monkeydial.com/v1/conference/recording/conf_rec_a1b2c3d4_2511071030",
"started_at": "2025-11-07T10:30:00Z",
"stopped_at": "2025-11-07T10:50:05Z"
}
Sent after recording is stopped and MP3 conversion completes. File ready for download. Use for archival, transcription, or analytics.
Inbound + Outbound + VAD + Conference + DTMF = Endless possibilities
Works with your existing SIP provider
No migration. No downtime. No risk.