Voice API
Build voice applications with Orbit’s Voice API. Make and receive calls, connect AI agents, configure SIP trunks, build IVR menus, host conference calls, and manage voicemail — all through a unified API.
Base path: /api/v1/voice
Calls
Initiate a Call
POST /api/v1/voice/calls
Start an outbound call. Route it to an AI voice agent, an IVR flow, or a raw SIP destination.
Destination phone number in E.164 format
Caller ID — must be a number registered in your Orbit account. If omitted, the server falls back to your tenant’s default outbound number.
Call direction: outbound or inbound.
AI voice agent to handle the call. The agent must exist in your tenant and not be archived (omit for raw SIP or an Application-bound IVR).
Whether to record the call
Request carrier-side Answering Machine Detection for the outbound call.
Campaign ID for spend-cap tracking. Recorded on the call log for attribution.
IANA timezone (e.g. America/New_York) used for the TCPA quiet-hours check on outbound calls.
Arbitrary key-value pairs attached to the call
Per-call webhooks, IVR menus, and TTS prompts are not set on this request. Configure them on the Application or IVR Flow bound to the number — there are no per-call webhook_url, ivr_id, or max_duration overrides. Fields outside the list above are silently ignored by the server.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/calls" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"to": "+14155552671",
"from": "+12025551234",
"agent_id": "agt_abc123",
"record": true,
"amd": true
}'
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit ({ apiKey: process . env . ORBIT_API_KEY ! })
const call = await orbit . voice . calls . create ({
to: '+14155552671' ,
from: '+12025551234' ,
agent_id: 'agt_abc123' ,
record: true ,
amd: true ,
})
console . log ( call . data . id )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ], "Content-Type" : "application/json" }
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/calls" , headers = headers, json = {
"to" : "+14155552671" ,
"from" : "+12025551234" ,
"agent_id" : "agt_abc123" ,
"record" : True ,
"amd" : True
})
print (r.json())
package main
import (
" bytes "
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" , "https://api.orbit.devotel.io/api/v1/voice/calls" , bytes . NewBuffer ([] byte ( `{
"to": "+14155552671",
"from": "+12025551234",
"agent_id": "agt_abc123",
"record": true,
"amd": true
}` )))
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
req . Header . Set ( "Content-Type" , "application/json" )
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/calls' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' ),
'Content-Type: application/json' ,
]);
curl_setopt ( $ch , CURLOPT_POSTFIELDS , <<< JSON
{
"to" : "+14155552671" ,
"from" : "+12025551234" ,
"agent_id" : "agt_abc123" ,
"record" : true ,
"amd" : true
}
JSON );
echo curl_exec ( $ch );
{
"data" : {
"id" : "call_abc123" ,
"to" : "+14155552671" ,
"from" : "+12025551234" ,
"agent_id" : "agt_abc123" ,
"status" : "initiating" ,
"record" : true ,
"created_at" : "2026-03-08T12:00:00Z"
},
"meta" : {
"request_id" : "req_call_001" ,
"timestamp" : "2026-03-08T12:00:00Z"
}
}
List Calls
GET /api/v1/voice/calls
Retrieve call logs with cursor-based pagination and optional filters.
Results per page (max 100)
Filter by status: initiating, ringing, in_progress, completed, failed
Filter by destination number
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/calls?status=completed&limit=20" \
-H "X-API-Key: dv_live_sk_your_key_here"
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit ({ apiKey: process . env . ORBIT_API_KEY ! })
const calls = await orbit . voice . calls . list ({ status: 'completed' , limit: 20 })
console . log ( calls . data )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.get( "https://api.orbit.devotel.io/api/v1/voice/calls" , headers = headers,
params = { "status" : "completed" , "limit" : 20 })
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "GET" , "https://api.orbit.devotel.io/api/v1/voice/calls?status=completed&limit=20" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/calls?status=completed&limit=20' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' ),
]);
echo curl_exec ( $ch );
Get Call Details
GET /api/v1/voice/calls/{id}
Retrieve full details of a call including status, duration, recording URL, and agent interaction summary.
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123" \
-H "X-API-Key: dv_live_sk_your_key_here"
const call = await orbit . voice . calls . get ( 'call_abc123' )
console . log ( call . data )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.get( "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123" , headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "GET" , "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Hang Up a Call
POST /api/v1/voice/calls/{id}/hangup
Terminate an active call. AI agents receive a graceful shutdown signal before disconnection.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/hangup" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . calls . hangup ( 'call_abc123' )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/hangup" , headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" , "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/hangup" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/hangup' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Accept / Decline an Inbound Call
POST /api/v1/voice/calls/{id}/accept
Accept an incoming call before it auto-rings the configured number-handler. Use when the softphone or programmatic handler wants to answer in lieu of the default routing.
POST /api/v1/voice/calls/{id}/decline
Reject an incoming call. The carrier receives 486 Busy Here.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/accept" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . calls . accept ( 'call_abc123' )
// or: await orbit.voice.calls.decline('call_abc123')
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/accept" , headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" , "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/accept" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/accept' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Mute / Unmute a Call Leg
POST /api/v1/voice/calls/{id}/mute
POST /api/v1/voice/calls/{id}/unmute
Mute or unmute the operator side of a connected call. Mute is per-leg — the participant on the other side keeps speaking and their audio still records.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/mute" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . calls . mute ( 'call_abc123' )
await orbit . voice . calls . unmute ( 'call_abc123' )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/mute" , headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" , "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/mute" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/mute' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Hold / Unhold
POST /api/v1/voice/calls/{id}/hold
POST /api/v1/voice/calls/{id}/unhold
Place the remote party on hold (carrier-side music or silence depending on the trunk’s hold_music configuration). Unhold restores audio in both directions.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/hold" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . calls . hold ( 'call_abc123' )
await orbit . voice . calls . unhold ( 'call_abc123' )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/hold" , headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" , "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/hold" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/hold' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Send DTMF Tones
POST /api/v1/voice/calls/{id}/dtmf
String of DTMF digits to send (0–9, *, #, A–D). Plays at standard 100ms / 100ms gap.
Use to navigate carrier IVRs from a programmatic outbound call (PIN entry, account confirmations, etc.).
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/dtmf" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "digits": "1234#" }'
await orbit . voice . calls . sendDTMF ( 'call_abc123' , { digits: '1234#' })
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ], "Content-Type" : "application/json" }
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/dtmf" ,
headers = headers, json = { "digits" : "1234#" })
print (r.json())
package main
import (
" bytes "
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" , "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/dtmf" ,
bytes . NewBuffer ([] byte ( `{"digits":"1234#"}` )))
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
req . Header . Set ( "Content-Type" , "application/json" )
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/dtmf' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' ),
'Content-Type: application/json' ,
]);
curl_setopt ( $ch , CURLOPT_POSTFIELDS , '{"digits":"1234#"}' );
echo curl_exec ( $ch );
Transfer (Blind)
POST /api/v1/voice/calls/{id}/transfer
Cold-transfer the call to a new destination. Hangs up the original leg the moment the new leg connects.
Destination phone number, agent ID (agt_*), or extension (ext_*).
Override the From number presented to the destination. Defaults to the original caller’s number.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/transfer" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "to": "+14155557890", "caller_id": "+12025551234" }'
await orbit . voice . calls . transfer ( 'call_abc123' , {
to: '+14155557890' ,
caller_id: '+12025551234' ,
})
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ], "Content-Type" : "application/json" }
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/transfer" ,
headers = headers, json = { "to" : "+14155557890" , "caller_id" : "+12025551234" })
print (r.json())
package main
import (
" bytes "
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" , "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/transfer" ,
bytes . NewBuffer ([] byte ( `{"to":"+14155557890","caller_id":"+12025551234"}` )))
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
req . Header . Set ( "Content-Type" , "application/json" )
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/transfer' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' ),
'Content-Type: application/json' ,
]);
curl_setopt ( $ch , CURLOPT_POSTFIELDS , '{"to":"+14155557890","caller_id":"+12025551234"}' );
echo curl_exec ( $ch );
Warm Transfer
POST /api/v1/voice/calls/{id}/warm-transfer
Start a warm transfer — the calling agent stays connected while a third leg is dialled. The original caller is placed on hold; once the third party answers, the agent has a private “whisper” window with them before completing the handoff.
Destination for the warm-transfer consult leg.
Optional TTS message played to the consult party before audio bridges (e.g. “Customer Jane Doe, escalation tier 2”).
POST /api/v1/voice/calls/{id}/warm-transfer/complete
Bridge the original caller into the consult leg and disconnect the original agent. The two non-agent legs are now connected.
POST /api/v1/voice/calls/{id}/warm-transfer/cancel
Abort the warm transfer and return the original caller to the agent. The consult leg is hung up.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/warm-transfer" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "to": "+14155557890", "whisper_message": "Customer Jane Doe, escalation tier 2" }'
await orbit . voice . calls . warmTransfer ( 'call_abc123' , {
to: '+14155557890' ,
whisper_message: 'Customer Jane Doe, escalation tier 2' ,
})
// later:
await orbit . voice . calls . warmTransferComplete ( 'call_abc123' )
// or to abort:
await orbit . voice . calls . warmTransferCancel ( 'call_abc123' )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ], "Content-Type" : "application/json" }
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/warm-transfer" ,
headers = headers,
json = { "to" : "+14155557890" , "whisper_message" : "Customer Jane Doe, escalation tier 2" })
print (r.json())
package main
import (
" bytes "
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" , "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/warm-transfer" ,
bytes . NewBuffer ([] byte ( `{"to":"+14155557890","whisper_message":"Customer Jane Doe, escalation tier 2"}` )))
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
req . Header . Set ( "Content-Type" , "application/json" )
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/warm-transfer' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' ),
'Content-Type: application/json' ,
]);
curl_setopt ( $ch , CURLOPT_POSTFIELDS ,
'{"to":"+14155557890","whisper_message":"Customer Jane Doe, escalation tier 2"}' );
echo curl_exec ( $ch );
Recording
Recordings are configured per-call via record: true on POST /api/v1/voice/calls, or started/stopped mid-call via the endpoints below. Storage is tenant-isolated; URLs expire after 24 hours and require re-fetching via the call detail endpoint.
Start / Stop Mid-Call Recording
POST /api/v1/voice/calls/{id}/recording/start
Begin recording an in-progress call. Idempotent — calling on an already-recording call returns the existing recording id.
Record agent + caller on separate audio channels (left/right). Single-channel mixes both.
Trim leading/trailing silence from the saved file.
POST /api/v1/voice/calls/{id}/recording/stop
Stop the in-progress recording and finalise the file. Subsequent reads of GET /api/v1/voice/calls/{id}/recording will return the fully-encoded URL.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/recording/start" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "dual_channel": true, "trim": false }'
await orbit . voice . recordings . start ( 'call_abc123' , { dual_channel: true , trim: false })
await orbit . voice . recordings . stop ( 'call_abc123' )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ], "Content-Type" : "application/json" }
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/recording/start" ,
headers = headers, json = { "dual_channel" : True , "trim" : False })
print (r.json())
package main
import (
" bytes "
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" , "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/recording/start" ,
bytes . NewBuffer ([] byte ( `{"dual_channel":true,"trim":false}` )))
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
req . Header . Set ( "Content-Type" , "application/json" )
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/recording/start' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' ),
'Content-Type: application/json' ,
]);
curl_setopt ( $ch , CURLOPT_POSTFIELDS , '{"dual_channel":true,"trim":false}' );
echo curl_exec ( $ch );
Retrieve a Call Recording
GET /api/v1/voice/calls/{id}/recording
Returns the recording metadata + a 24h-expiring signed URL. If the call is still being recorded, status is in_progress and signed_url is null.
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/recording" \
-H "X-API-Key: dv_live_sk_your_key_here"
const rec = await orbit . voice . recordings . get ( 'call_abc123' )
console . log ( rec . data . signed_url )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.get( "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/recording" , headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "GET" , "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/recording" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/recording' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
{
"data" : {
"call_id" : "call_abc123" ,
"status" : "completed" ,
"duration_seconds" : 312 ,
"format" : "mp3" ,
"channels" : 2 ,
"size_bytes" : 4980000 ,
"signed_url" : "https://storage.devotel.io/recordings/call_abc123.mp3?Expires=1735689600&Signature=..." ,
"signed_url_expires_at" : "2026-03-09T12:00:00Z" ,
"created_at" : "2026-03-08T11:55:48Z"
},
"meta" : {
"request_id" : "req_rec_001" ,
"timestamp" : "2026-03-08T12:00:00Z"
}
}
Conference Recording
POST /api/v1/voice/conferences/{id}/recording/start
POST /api/v1/voice/conferences/{id}/recording/stop
Same semantics as call recording but for conference rooms — captures the mixed audio of every participant.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/recording/start" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . conferences . startRecording ( 'conf_abc123' )
await orbit . voice . conferences . stopRecording ( 'conf_abc123' )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/recording/start" , headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" ,
"https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/recording/start" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/recording/start' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
SIP Trunks
Bring your own carrier by connecting SIP trunks to Orbit. Orbit acts as an SBC (Session Border Controller) with TLS and SRTP encryption.
Create SIP Trunk
POST /api/v1/voice/sip-trunks
A descriptive name for the trunk
SIP URI for outbound call routing (e.g., sip:trunk.carrier.com:5060)
SIP URIs that Orbit should accept inbound calls from
SIP authentication username
SIP authentication password
codecs
string[]
default: "[\"PCMU\", \"PCMA\", \"OPUS\"]"
Preferred audio codecs in priority order
Transport encryption: tls_srtp, tls, or none
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/sip-trunks" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Primary Carrier",
"termination_uri": "sip:trunk.carrier.com:5060",
"origination_uris": ["sip:orbit-gw.devotel.io"],
"auth_username": "orbit_user",
"auth_password": "secure_password",
"codecs": ["OPUS", "PCMU"],
"encryption": "tls_srtp"
}'
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit ({ apiKey: process . env . ORBIT_API_KEY ! })
const trunk = await orbit . voice . sipTrunks . create ({
name: 'Primary Carrier' ,
termination_uri: 'sip:trunk.carrier.com:5060' ,
origination_uris: [ 'sip:orbit-gw.devotel.io' ],
auth_username: 'orbit_user' ,
auth_password: 'secure_password' ,
codecs: [ 'OPUS' , 'PCMU' ],
encryption: 'tls_srtp' ,
})
console . log ( trunk . data . id )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ], "Content-Type" : "application/json" }
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/sip-trunks" , headers = headers, json = {
"name" : "Primary Carrier" ,
"termination_uri" : "sip:trunk.carrier.com:5060" ,
"origination_uris" : [ "sip:orbit-gw.devotel.io" ],
"auth_username" : "orbit_user" ,
"auth_password" : "secure_password" ,
"codecs" : [ "OPUS" , "PCMU" ],
"encryption" : "tls_srtp"
})
print (r.json())
package main
import (
" bytes "
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" , "https://api.orbit.devotel.io/api/v1/voice/sip-trunks" , bytes . NewBuffer ([] byte ( `{
"name": "Primary Carrier",
"termination_uri": "sip:trunk.carrier.com:5060",
"origination_uris": ["sip:orbit-gw.devotel.io"],
"auth_username": "orbit_user",
"auth_password": "secure_password",
"codecs": ["OPUS", "PCMU"],
"encryption": "tls_srtp"
}` )))
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
req . Header . Set ( "Content-Type" , "application/json" )
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/sip-trunks' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' ),
'Content-Type: application/json' ,
]);
curl_setopt ( $ch , CURLOPT_POSTFIELDS , <<< JSON
{
"name" : "Primary Carrier" ,
"termination_uri" : "sip:trunk.carrier.com:5060" ,
"origination_uris" : [ "sip:orbit-gw.devotel.io" ],
"auth_username" : "orbit_user" ,
"auth_password" : "secure_password" ,
"codecs" : [ "OPUS" , "PCMU" ],
"encryption" : "tls_srtp"
}
JSON );
echo curl_exec ( $ch );
{
"data" : {
"id" : "trunk_abc123" ,
"name" : "Primary Carrier" ,
"termination_uri" : "sip:trunk.carrier.com:5060" ,
"status" : "active" ,
"codecs" : [ "OPUS" , "PCMU" ],
"encryption" : "tls_srtp" ,
"created_at" : "2026-03-08T12:00:00Z"
},
"meta" : {
"request_id" : "req_trunk_001" ,
"timestamp" : "2026-03-08T12:00:00Z"
}
}
List SIP Trunks
GET /api/v1/voice/sip-trunks
Get SIP Trunk
GET /api/v1/voice/sip-trunks/{id}
Update SIP Trunk
PUT /api/v1/voice/sip-trunks/{id}
Delete SIP Trunk
DELETE /api/v1/voice/sip-trunks/{id}
# List
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/sip-trunks" \
-H "X-API-Key: dv_live_sk_your_key_here"
# Get
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/sip-trunks/trunk_abc123" \
-H "X-API-Key: dv_live_sk_your_key_here"
# Update
curl -X PUT "https://api.orbit.devotel.io/api/v1/voice/sip-trunks/trunk_abc123" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "name": "Primary Carrier (renamed)" }'
# Delete
curl -X DELETE "https://api.orbit.devotel.io/api/v1/voice/sip-trunks/trunk_abc123" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . sipTrunks . list ()
await orbit . voice . sipTrunks . get ( 'trunk_abc123' )
await orbit . voice . sipTrunks . update ( 'trunk_abc123' , { name: 'Primary Carrier (renamed)' })
await orbit . voice . sipTrunks . delete ( 'trunk_abc123' )
import os, requests
h = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
requests.get( "https://api.orbit.devotel.io/api/v1/voice/sip-trunks" , headers = h)
requests.get( "https://api.orbit.devotel.io/api/v1/voice/sip-trunks/trunk_abc123" , headers = h)
requests.put( "https://api.orbit.devotel.io/api/v1/voice/sip-trunks/trunk_abc123" ,
headers = { ** h, "Content-Type" : "application/json" },
json = { "name" : "Primary Carrier (renamed)" })
requests.delete( "https://api.orbit.devotel.io/api/v1/voice/sip-trunks/trunk_abc123" , headers = h)
package main
import (
" net/http "
" os "
)
func main () {
base := "https://api.orbit.devotel.io/api/v1/voice/sip-trunks"
key := os . Getenv ( "ORBIT_API_KEY" )
for _ , r := range [] struct { m , u string }{{ "GET" , base }, { "GET" , base + "/trunk_abc123" }, { "DELETE" , base + "/trunk_abc123" }} {
req , _ := http . NewRequest ( r . m , r . u , nil )
req . Header . Set ( "X-API-Key" , key )
http . DefaultClient . Do ( req )
}
}
<? php
$key = getenv ( 'ORBIT_API_KEY' );
$base = 'https://api.orbit.devotel.io/api/v1/voice/sip-trunks' ;
foreach ([ 'GET' => $base , ' DELETE ' => " $base /trunk_abc123" ] as $method => $url ) {
$ch = curl_init ( $url );
curl_setopt_array ( $ch , [
CURLOPT_RETURNTRANSFER => true ,
CURLOPT_CUSTOMREQUEST => $method ,
CURLOPT_HTTPHEADER => [ "X-API-Key: $key " ],
]);
echo curl_exec ( $ch );
}
IVR (Interactive Voice Response)
Build programmable voice menus that route callers through options, collect DTMF input, and transfer to agents or queues.
Create IVR Flow
POST /api/v1/voice/ivr-flows
The IVR flow body is a graph definition , not a flat list of greeting/menu
fields. The greeting, per-option prompts, timeouts, and fallbacks are all
expressed as nodes and edges inside the required definition object. The API
ignores any top-level field other than name, definition, and active.
IVR flow name (1–200 characters).
The IVR flow graph, expressed as an XYFlow -style
{ nodes, edges } object (the same shape the dashboard IVR builder canvas
produces). It is structurally validated before persistence — an invalid graph
is rejected with 422 IVR_FLOW_GRAPH_INVALID and a list of node/edge errors. Validation rules: exactly one entry node (type ivrStart, or any node with
data.isEntry = true); at least one exit node (end, hangup, transfer,
voicemail, ringGroup, dialByName, or offerCallback); every edge must
reference existing nodes; every node must be reachable from the entry; and
every reachable node must have a path to an exit (a loop with no escape is
rejected to avoid burning carrier minutes). Limits: 1000 nodes, 2000 edges. Flow nodes. Unique node id (referenced by edges).
Node type. Entry: ivrStart. Interactive: menu. Terminal/exit:
transfer, voicemail, hangup, end, ringGroup, dialByName,
offerCallback.
Per-node config (camelCase). For a menu node: ttsText (the spoken
menu prompt), optional language, timeout (seconds), and
menuOptions — an array of { key, label } where key is a DTMF key
(0–9, *, #). For a transfer node: transferTo — a PSTN
number (E.164) or on-net extension. Per invariant #45, outbound voice
must exit via the Devotel softswitch, so a free-form sip:/sips:
transferTo is rejected with transfer_external_sip.
Directed edges connecting nodes. definition.edges[].source
Source node id.
definition.edges[].target
Target node id.
definition.edges[].sourceHandle
For edges leaving a menu node, the DTMF key this branch is taken on
(e.g. "1"). Matched against menuOptions[].key.
Whether the flow is active.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/ivr-flows" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Main Menu",
"active": true,
"definition": {
"nodes": [
{ "id": "start", "type": "ivrStart", "data": {} },
{ "id": "menu", "type": "menu", "data": {
"ttsText": "Welcome to Acme Corp. Press 1 for sales, 2 for support, or 3 to leave a voicemail.",
"language": "en-US",
"timeout": 8,
"menuOptions": [
{ "key": "1", "label": "Sales" },
{ "key": "2", "label": "Support" },
{ "key": "3", "label": "Voicemail" }
]
}
},
{ "id": "sales", "type": "transfer", "data": { "transferTo": "+14155550101" } },
{ "id": "support", "type": "transfer", "data": { "transferTo": "+14155550102" } },
{ "id": "voicemail", "type": "voicemail", "data": { "greeting": "Please leave a message after the tone." } }
],
"edges": [
{ "id": "e0", "source": "start", "target": "menu" },
{ "id": "e1", "source": "menu", "target": "sales", "sourceHandle": "1" },
{ "id": "e2", "source": "menu", "target": "support", "sourceHandle": "2" },
{ "id": "e3", "source": "menu", "target": "voicemail", "sourceHandle": "3" }
]
}
}'
const ivr = await orbit . voice . ivr . create ({
name: 'Main Menu' ,
active: true ,
definition: {
nodes: [
{ id: 'start' , type: 'ivrStart' , data: {} },
{
id: 'menu' ,
type: 'menu' ,
data: {
ttsText: 'Welcome to Acme Corp. Press 1 for sales, 2 for support, or 3 to leave a voicemail.' ,
language: 'en-US' ,
timeout: 8 ,
menuOptions: [
{ key: '1' , label: 'Sales' },
{ key: '2' , label: 'Support' },
{ key: '3' , label: 'Voicemail' },
],
},
},
{ id: 'sales' , type: 'transfer' , data: { transferTo: '+14155550101' } },
{ id: 'support' , type: 'transfer' , data: { transferTo: '+14155550102' } },
{ id: 'voicemail' , type: 'voicemail' , data: { greeting: 'Please leave a message after the tone.' } },
],
edges: [
{ id: 'e0' , source: 'start' , target: 'menu' },
{ id: 'e1' , source: 'menu' , target: 'sales' , sourceHandle: '1' },
{ id: 'e2' , source: 'menu' , target: 'support' , sourceHandle: '2' },
{ id: 'e3' , source: 'menu' , target: 'voicemail' , sourceHandle: '3' },
],
},
})
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ], "Content-Type" : "application/json" }
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/ivr-flows" , headers = headers, json = {
"name" : "Main Menu" ,
"active" : True ,
"definition" : {
"nodes" : [
{ "id" : "start" , "type" : "ivrStart" , "data" : {}},
{ "id" : "menu" , "type" : "menu" , "data" : {
"ttsText" : "Welcome to Acme Corp. Press 1 for sales, 2 for support, or 3 to leave a voicemail." ,
"language" : "en-US" ,
"timeout" : 8 ,
"menuOptions" : [
{ "key" : "1" , "label" : "Sales" },
{ "key" : "2" , "label" : "Support" },
{ "key" : "3" , "label" : "Voicemail" }
]
}},
{ "id" : "sales" , "type" : "transfer" , "data" : { "transferTo" : "+14155550101" }},
{ "id" : "support" , "type" : "transfer" , "data" : { "transferTo" : "+14155550102" }},
{ "id" : "voicemail" , "type" : "voicemail" , "data" : { "greeting" : "Please leave a message after the tone." }}
],
"edges" : [
{ "id" : "e0" , "source" : "start" , "target" : "menu" },
{ "id" : "e1" , "source" : "menu" , "target" : "sales" , "sourceHandle" : "1" },
{ "id" : "e2" , "source" : "menu" , "target" : "support" , "sourceHandle" : "2" },
{ "id" : "e3" , "source" : "menu" , "target" : "voicemail" , "sourceHandle" : "3" }
]
}
})
print (r.json())
package main
import (
" bytes "
" net/http "
" os "
)
func main () {
body := [] byte ( `{
"name": "Main Menu",
"active": true,
"definition": {
"nodes": [
{"id": "start", "type": "ivrStart", "data": {}},
{"id": "menu", "type": "menu", "data": {
"ttsText": "Welcome to Acme Corp. Press 1 for sales.",
"timeout": 8,
"menuOptions": [{"key": "1", "label": "Sales"}]
}},
{"id": "sales", "type": "transfer", "data": {"transferTo": "+14155550101"}}
],
"edges": [
{"id": "e0", "source": "start", "target": "menu"},
{"id": "e1", "source": "menu", "target": "sales", "sourceHandle": "1"}
]
}
}` )
req , _ := http . NewRequest ( "POST" , "https://api.orbit.devotel.io/api/v1/voice/ivr-flows" , bytes . NewBuffer ( body ))
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
req . Header . Set ( "Content-Type" , "application/json" )
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/ivr-flows' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' ),
'Content-Type: application/json' ,
]);
curl_setopt ( $ch , CURLOPT_POSTFIELDS , json_encode ([
'name' => 'Main Menu' ,
'active' => true ,
'definition' => [
'nodes' => [
[ 'id' => 'start' , 'type' => 'ivrStart' , 'data' => ( object ) []],
[ 'id' => 'menu' , 'type' => 'menu' , 'data' => [
'ttsText' => 'Welcome to Acme Corp. Press 1 for sales.' ,
'timeout' => 8 ,
'menuOptions' => [[ 'key' => '1' , 'label' => 'Sales' ]],
]],
[ 'id' => 'sales' , 'type' => 'transfer' , 'data' => [ 'transferTo' => '+14155550101' ]],
],
'edges' => [
[ 'id' => 'e0' , 'source' => 'start' , 'target' => 'menu' ],
[ 'id' => 'e1' , 'source' => 'menu' , 'target' => 'sales' , 'sourceHandle' => '1' ],
],
],
]));
echo curl_exec ( $ch );
{
"data" : {
"id" : "ivr_abc123" ,
"name" : "Main Menu" ,
"status" : "active" ,
"options_count" : 3 ,
"created_at" : "2026-03-08T12:00:00Z"
},
"meta" : {
"request_id" : "req_ivr_001" ,
"timestamp" : "2026-03-08T12:00:00Z"
}
}
List IVR Flows
GET /api/v1/voice/ivr-flows
Update IVR Flow
PUT /api/v1/voice/ivr-flows/{id}
Delete IVR Flow
DELETE /api/v1/voice/ivr-flows/{id}
# List
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/ivr-flows" \
-H "X-API-Key: dv_live_sk_your_key_here"
# Update
curl -X PUT "https://api.orbit.devotel.io/api/v1/voice/ivr-flows/ivr_abc123" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "name": "Renamed Menu" }'
# Delete
curl -X DELETE "https://api.orbit.devotel.io/api/v1/voice/ivr-flows/ivr_abc123" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . ivr . list ()
await orbit . voice . ivr . update ( 'ivr_abc123' , { name: 'Renamed Menu' })
await orbit . voice . ivr . delete ( 'ivr_abc123' )
import os, requests
h = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
requests.get( "https://api.orbit.devotel.io/api/v1/voice/ivr-flows" , headers = h)
requests.put( "https://api.orbit.devotel.io/api/v1/voice/ivr-flows/ivr_abc123" ,
headers = { ** h, "Content-Type" : "application/json" }, json = { "name" : "Renamed Menu" })
requests.delete( "https://api.orbit.devotel.io/api/v1/voice/ivr-flows/ivr_abc123" , headers = h)
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "GET" , "https://api.orbit.devotel.io/api/v1/voice/ivr-flows" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/ivr-flows' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Conferences
Host multi-party conference calls with moderation controls, recording, and real-time participant management.
List Conferences
GET /api/v1/voice/conferences
Lists conferences for your tenant, paginated by started_at descending.
Comma-separated status filter — in-progress, completed, failed, ended.
Opaque pagination cursor returned in the previous response’s meta.cursor.
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/conferences?status=in-progress&limit=20" \
-H "X-API-Key: dv_live_sk_your_key_here"
const conferences = await orbit . voice . conferences . list ({
status: 'in-progress' ,
limit: 20 ,
})
for ( const conf of conferences . data ) {
console . log ( conf . id , conf . name , conf . status )
}
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.get( "https://api.orbit.devotel.io/api/v1/voice/conferences" ,
headers = headers, params = { "status" : "in-progress" , "limit" : 20 })
print (r.json())
package main
import (
" net/http "
" net/url "
" os "
)
func main () {
u , _ := url . Parse ( "https://api.orbit.devotel.io/api/v1/voice/conferences" )
q := u . Query ()
q . Set ( "status" , "in-progress" )
q . Set ( "limit" , "20" )
u . RawQuery = q . Encode ()
req , _ := http . NewRequest ( "GET" , u . String (), nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/conferences?status=in-progress&limit=20' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
{
"data" : [
{
"id" : "conf_abc1234567890abcdef1234567890abcd" ,
"name" : "Q1 Planning Call" ,
"status" : "in-progress" ,
"caller_id" : "+18005551234" ,
"started_at" : "2026-03-08T12:00:00Z" ,
"ended_at" : null ,
"duration_seconds" : null ,
"max_participants" : 25 ,
"recording_url" : null ,
"created_by" : "usr_xyz" ,
"created_at" : "2026-03-08T12:00:00Z"
}
],
"meta" : {
"request_id" : "req_conf_list_001" ,
"cursor" : null ,
"has_more" : false ,
"timestamp" : "2026-03-08T12:00:01Z"
}
}
Get Conference
GET /api/v1/voice/conferences/{id}
Returns a single conference with its participants embedded under participants. For rooms with more than 32 legs, paginate via GET /api/v1/voice/conferences/{id}/participants.
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123" \
-H "X-API-Key: dv_live_sk_your_key_here"
const conf = await orbit . voice . conferences . get ( 'conf_abc123' )
console . log ( conf . data . name , conf . data . participants . length )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.get( "https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123" , headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "GET" ,
"https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
{
"data" : {
"id" : "conf_abc1234567890abcdef1234567890abcd" ,
"name" : "Q1 Planning Call" ,
"status" : "in-progress" ,
"caller_id" : "+18005551234" ,
"started_at" : "2026-03-08T12:00:00Z" ,
"ended_at" : null ,
"duration_seconds" : null ,
"max_participants" : 25 ,
"recording_url" : null ,
"participants" : [
{
"id" : "cpart_abc" ,
"phone_number" : "+14155552671" ,
"status" : "answered" ,
"leg_call_sid" : "CA-…" ,
"joined_at" : "2026-03-08T12:00:14Z" ,
"left_at" : null ,
"is_host" : true
}
]
},
"meta" : {
"request_id" : "req_conf_get_001" ,
"timestamp" : "2026-03-08T12:00:15Z"
}
}
Get Aggregate Metrics
GET /api/v1/voice/conferences/aggregate
Returns server-side rollup KPIs over a rolling window (default 30 days). Powers the dashboard’s conference stat strip — avg duration, success rate, peak concurrent participants. Pass window_days to widen or narrow the rollup (max 365).
Window in days the rollup spans (1-365).
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/conferences/aggregate?window_days=7" \
-H "X-API-Key: dv_live_sk_your_key_here"
const metrics = await orbit . voice . conferences . aggregate ({ windowDays: 7 })
console . log (
metrics . data . avg_duration_seconds ,
metrics . data . success_rate ,
metrics . data . peak_concurrent_participants ,
)
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.get( "https://api.orbit.devotel.io/api/v1/voice/conferences/aggregate" ,
headers = headers, params = { "window_days" : 7 })
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "GET" ,
"https://api.orbit.devotel.io/api/v1/voice/conferences/aggregate?window_days=7" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/conferences/aggregate?window_days=7' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
{
"data" : {
"total_count" : 142 ,
"active_count" : 3 ,
"avg_duration_seconds" : 1820 ,
"success_rate" : 0.9437 ,
"peak_concurrent_participants" : 18 ,
"total_participants_window" : 612 ,
"window_days" : 7
},
"meta" : {
"request_id" : "req_conf_agg_001" ,
"timestamp" : "2026-03-08T12:00:00Z"
}
}
Create Conference
POST /api/v1/voice/conferences
Conference name (max 200 chars).
Seed participants to dial when the conference is created. Each entry must be E.164. 1-32 entries.
E.164 caller-id used for each seed-participant dial. Must be a voice-capable number owned by your organization.
Record every participant leg on creation. Subject to per-jurisdiction recording consent gates.
Cap on simultaneously-bridged legs (max 32).
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/conferences" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Q1 Planning Call",
"participants": ["+14155552671", "+14155552672"],
"from": "+18005551234",
"record": true,
"maxParticipants": 25
}'
const conf = await orbit . voice . conferences . create ({
name: 'Q1 Planning Call' ,
participants: [ '+14155552671' , '+14155552672' ],
from: '+18005551234' ,
record: true ,
maxParticipants: 25 ,
})
console . log ( conf . data . id )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ], "Content-Type" : "application/json" }
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/conferences" , headers = headers, json = {
"name" : "Q1 Planning Call" ,
"participants" : [ "+14155552671" , "+14155552672" ],
"from" : "+18005551234" ,
"record" : True ,
"maxParticipants" : 25
})
print (r.json())
package main
import (
" bytes "
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" , "https://api.orbit.devotel.io/api/v1/voice/conferences" , bytes . NewBuffer ([] byte ( `{
"name": "Q1 Planning Call",
"participants": ["+14155552671", "+14155552672"],
"from": "+18005551234",
"record": true,
"maxParticipants": 25
}` )))
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
req . Header . Set ( "Content-Type" , "application/json" )
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/conferences' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' ),
'Content-Type: application/json' ,
]);
curl_setopt ( $ch , CURLOPT_POSTFIELDS , json_encode ([
'name' => 'Q1 Planning Call' ,
'participants' => [ '+14155552671' , '+14155552672' ],
'from' => '+18005551234' ,
'record' => true ,
'maxParticipants' => 25 ,
]));
echo curl_exec ( $ch );
{
"data" : {
"id" : "conf_abc123" ,
"name" : "Q1 Planning Call" ,
"status" : "waiting" ,
"pin" : "482901" ,
"dial_in_number" : "+18005559999" ,
"max_participants" : 25 ,
"record" : true ,
"created_at" : "2026-03-08T12:00:00Z"
},
"meta" : {
"request_id" : "req_conf_001" ,
"timestamp" : "2026-03-08T12:00:00Z"
}
}
Add Participant
POST /api/v1/voice/conferences/{id}/participants
Phone number to dial into the conference
Join the participant in muted state
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/participants" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "number": "+14155552671", "muted": false }'
await orbit . voice . conferences . addParticipant ( 'conf_abc123' , {
number: '+14155552671' ,
muted: false ,
})
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ], "Content-Type" : "application/json" }
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/participants" ,
headers = headers, json = { "number" : "+14155552671" , "muted" : False })
print (r.json())
package main
import (
" bytes "
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" ,
"https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/participants" ,
bytes . NewBuffer ([] byte ( `{"number":"+14155552671","muted":false}` )))
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
req . Header . Set ( "Content-Type" , "application/json" )
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/participants' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' ),
'Content-Type: application/json' ,
]);
curl_setopt ( $ch , CURLOPT_POSTFIELDS , '{"number":"+14155552671","muted":false}' );
echo curl_exec ( $ch );
Remove Participant
DELETE /api/v1/voice/conferences/{id}/participants/{participantId}
curl -X DELETE "https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/participants/part_xyz" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . conferences . removeParticipant ( 'conf_abc123' , 'part_xyz' )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.delete( "https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/participants/part_xyz" ,
headers = headers)
print (r.status_code)
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "DELETE" ,
"https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/participants/part_xyz" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/participants/part_xyz' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , ' DELETE ' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
End Conference
POST /api/v1/voice/conferences/{id}/end
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/end" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . conferences . end ( 'conf_abc123' )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/end" , headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" ,
"https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/end" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/end' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Voicemail
Manage voicemail boxes for receiving and transcribing voice messages when calls go unanswered.
Create Voicemail Box
POST /api/v1/voice/voicemail-boxes
Custom TTS greeting (default: “Please leave a message after the tone.”)
Automatically transcribe voicemail messages using STT
Maximum voicemail recording duration in seconds
Email to send voicemail notifications to
URL for voicemail event callbacks
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/voicemail-boxes" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "General Inbox",
"greeting": "You have reached Acme Corp. We are currently unavailable. Please leave a message.",
"transcribe": true,
"notification_email": "team@acme.com"
}'
const box = await orbit . voice . voicemail . create ({
name: 'General Inbox' ,
greeting: 'You have reached Acme Corp. We are currently unavailable. Please leave a message.' ,
transcribe: true ,
notification_email: 'team@acme.com' ,
})
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ], "Content-Type" : "application/json" }
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/voicemail" , headers = headers, json = {
"name" : "General Inbox" ,
"greeting" : "You have reached Acme Corp. We are currently unavailable. Please leave a message." ,
"transcribe" : True ,
"notification_email" : "team@acme.com"
})
print (r.json())
package main
import (
" bytes "
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" , "https://api.orbit.devotel.io/api/v1/voice/voicemail" , bytes . NewBuffer ([] byte ( `{
"name": "General Inbox",
"transcribe": true,
"notification_email": "team@acme.com"
}` )))
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
req . Header . Set ( "Content-Type" , "application/json" )
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/voicemail' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' ),
'Content-Type: application/json' ,
]);
curl_setopt ( $ch , CURLOPT_POSTFIELDS , json_encode ([
'name' => 'General Inbox' , 'transcribe' => true , 'notification_email' => 'team@acme.com' ,
]));
echo curl_exec ( $ch );
{
"data" : {
"id" : "vmail_abc123" ,
"name" : "General Inbox" ,
"transcribe" : true ,
"max_duration" : 120 ,
"messages_count" : 0 ,
"created_at" : "2026-03-08T12:00:00Z"
},
"meta" : {
"request_id" : "req_vmail_001" ,
"timestamp" : "2026-03-08T12:00:00Z"
}
}
List Voicemail Messages
GET /api/v1/voice/voicemails
Retrieve voicemail messages across the org with transcriptions, with cursor pagination and filters by direction, status, and unread state.
GET /api/v1/voice/voicemails/{id}
Get a single voicemail message including transcription and recording URL.
PATCH /api/v1/voice/voicemails/{id}/read
Mark a voicemail as read.
DELETE /api/v1/voice/voicemails/{id}
Permanently delete a voicemail message.
# List
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/voicemails?status=unread&limit=20" \
-H "X-API-Key: dv_live_sk_your_key_here"
# Get
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/voicemails/vmsg_001" \
-H "X-API-Key: dv_live_sk_your_key_here"
# Mark read
curl -X PATCH "https://api.orbit.devotel.io/api/v1/voice/voicemails/vmsg_001/read" \
-H "X-API-Key: dv_live_sk_your_key_here"
# Delete
curl -X DELETE "https://api.orbit.devotel.io/api/v1/voice/voicemails/vmsg_001" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . voicemails . list ({ status: 'unread' , limit: 20 })
await orbit . voice . voicemails . get ( 'vmsg_001' )
await orbit . voice . voicemails . markRead ( 'vmsg_001' )
await orbit . voice . voicemails . delete ( 'vmsg_001' )
import os, requests
h = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
requests.get( "https://api.orbit.devotel.io/api/v1/voice/voicemails" ,
headers = h, params = { "status" : "unread" , "limit" : 20 })
requests.get( "https://api.orbit.devotel.io/api/v1/voice/voicemails/vmsg_001" , headers = h)
requests.patch( "https://api.orbit.devotel.io/api/v1/voice/voicemails/vmsg_001/read" , headers = h)
requests.delete( "https://api.orbit.devotel.io/api/v1/voice/voicemails/vmsg_001" , headers = h)
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "GET" , "https://api.orbit.devotel.io/api/v1/voice/voicemails?status=unread&limit=20" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/voicemails?status=unread&limit=20' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
{
"data" : [
{
"id" : "vmsg_001" ,
"voicemail_id" : "vmail_abc123" ,
"caller" : "+14155552671" ,
"duration_seconds" : 35 ,
"transcription" : "Hi, this is Jane. I wanted to follow up on my order. Please call me back." ,
"recording_url" : "https://storage.devotel.io/voicemail/vmsg_001.wav" ,
"listened" : false ,
"created_at" : "2026-03-08T14:30:00Z"
}
],
"meta" : {
"request_id" : "req_vmsg_001" ,
"timestamp" : "2026-03-08T15:00:00Z" ,
"pagination" : {
"cursor" : "cur_vmsg_abc" ,
"has_more" : false ,
"total" : 1
}
}
}
Conference Operations (advanced)
Beyond Add Participant / Remove Participant, the conference API exposes a complete moderation surface.
Mute / Unmute a Participant
POST /api/v1/voice/conferences/{id}/participants/{participantId}/mute
POST /api/v1/voice/conferences/{id}/participants/{participantId}/unmute
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/participants/part_xyz/mute" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . conferences . muteParticipant ( 'conf_abc123' , 'part_xyz' )
await orbit . voice . conferences . unmuteParticipant ( 'conf_abc123' , 'part_xyz' )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.post(
"https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/participants/part_xyz/mute" ,
headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" ,
"https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/participants/part_xyz/mute" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/participants/part_xyz/mute' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Mute / Unmute Everyone
POST /api/v1/voice/conferences/{id}/mute-all
POST /api/v1/voice/conferences/{id}/unmute-all
Useful for “all-hands” conferences where the moderator wants to control the floor. Already-muted participants keep their state.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/mute-all" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . conferences . muteAll ( 'conf_abc123' )
await orbit . voice . conferences . unmuteAll ( 'conf_abc123' )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/mute-all" , headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" ,
"https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/mute-all" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/mute-all' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Hold / Unhold a Participant
POST /api/v1/voice/conferences/{id}/participants/{participantId}/hold
POST /api/v1/voice/conferences/{id}/participants/{participantId}/unhold
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/participants/part_xyz/hold" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . conferences . holdParticipant ( 'conf_abc123' , 'part_xyz' )
await orbit . voice . conferences . unholdParticipant ( 'conf_abc123' , 'part_xyz' )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.post(
"https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/participants/part_xyz/hold" ,
headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" ,
"https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/participants/part_xyz/hold" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/participants/part_xyz/hold' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Lock / Unlock Conference
POST /api/v1/voice/conferences/{id}/lock
POST /api/v1/voice/conferences/{id}/unlock
When locked, no new participants can join — existing participants stay connected. Use to seal a conference for a confidential discussion.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/lock" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . conferences . lock ( 'conf_abc123' )
await orbit . voice . conferences . unlock ( 'conf_abc123' )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/lock" , headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" ,
"https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/lock" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123/lock' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
End / Delete a Conference
POST /api/v1/voice/conferences/{id}/end
End the active conference and disconnect every participant gracefully. The conference row is retained for analytics.
DELETE /api/v1/voice/conferences/{id}
Hard-delete the conference row (only allowed when status is ended).
curl -X DELETE "https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . conferences . end ( 'conf_abc123' )
await orbit . voice . conferences . delete ( 'conf_abc123' )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
requests.delete( "https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123" , headers = headers)
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "DELETE" ,
"https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/conferences/conf_abc123' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , ' DELETE ' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Call Queues
Queue inbound calls by skill / priority and route them to the next available agent. Pairs with the AI agent layer for hybrid human + AI front-desk experiences.
Create a Queue
POST /api/v1/voice/queues
Queue name (visible to supervisors in the wallboard).
strategy
string
default: "longest_idle"
Distribution strategy: longest_idle, round_robin, least_calls, random.
Maximum hold time before the call falls through to overflow_action.
overflow_action
string
default: "voicemail"
Action when max-wait expires: voicemail, transfer_number, hangup.
Public URL of an MP3 played while waiting.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/queues" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Support Tier 1",
"strategy": "longest_idle",
"max_wait_seconds": 240,
"overflow_action": "voicemail",
"hold_music_url": "https://cdn.example.com/hold-music.mp3"
}'
const q = await orbit . voice . queues . create ({
name: 'Support Tier 1' ,
strategy: 'longest_idle' ,
max_wait_seconds: 240 ,
overflow_action: 'voicemail' ,
hold_music_url: 'https://cdn.example.com/hold-music.mp3' ,
})
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ], "Content-Type" : "application/json" }
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/queues" , headers = headers, json = {
"name" : "Support Tier 1" ,
"strategy" : "longest_idle" ,
"max_wait_seconds" : 240 ,
"overflow_action" : "voicemail" ,
"hold_music_url" : "https://cdn.example.com/hold-music.mp3"
})
print (r.json())
package main
import (
" bytes "
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" , "https://api.orbit.devotel.io/api/v1/voice/queues" , bytes . NewBuffer ([] byte ( `{
"name": "Support Tier 1",
"strategy": "longest_idle",
"max_wait_seconds": 240,
"overflow_action": "voicemail"
}` )))
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
req . Header . Set ( "Content-Type" , "application/json" )
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/queues' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' ),
'Content-Type: application/json' ,
]);
curl_setopt ( $ch , CURLOPT_POSTFIELDS , json_encode ([
'name' => 'Support Tier 1' , 'strategy' => 'longest_idle' ,
'max_wait_seconds' => 240 , 'overflow_action' => 'voicemail' ,
]));
echo curl_exec ( $ch );
List / Update / Delete
GET /api/v1/voice/queues
PUT /api/v1/voice/queues/{id}
DELETE /api/v1/voice/queues/{id}
# List
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/queues" \
-H "X-API-Key: dv_live_sk_your_key_here"
# Update
curl -X PUT "https://api.orbit.devotel.io/api/v1/voice/queues/queue_abc" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "max_wait_seconds": 180 }'
# Delete
curl -X DELETE "https://api.orbit.devotel.io/api/v1/voice/queues/queue_abc" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . queues . list ()
await orbit . voice . queues . update ( 'queue_abc' , { max_wait_seconds: 180 })
await orbit . voice . queues . delete ( 'queue_abc' )
import os, requests
h = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
requests.get( "https://api.orbit.devotel.io/api/v1/voice/queues" , headers = h)
requests.put( "https://api.orbit.devotel.io/api/v1/voice/queues/queue_abc" ,
headers = { ** h, "Content-Type" : "application/json" }, json = { "max_wait_seconds" : 180 })
requests.delete( "https://api.orbit.devotel.io/api/v1/voice/queues/queue_abc" , headers = h)
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "GET" , "https://api.orbit.devotel.io/api/v1/voice/queues" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/queues' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Enqueue a Call
POST /api/v1/voice/queues/{id}/enqueue
Call ID to add to the queue.
Higher priority routes ahead of lower. VIP customers, escalations.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/queues/queue_abc/enqueue" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "call_id": "call_abc123", "priority": 5 }'
await orbit . voice . queues . enqueue ( 'queue_abc' , { call_id: 'call_abc123' , priority: 5 })
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ], "Content-Type" : "application/json" }
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/queues/queue_abc/enqueue" ,
headers = headers, json = { "call_id" : "call_abc123" , "priority" : 5 })
print (r.json())
package main
import (
" bytes "
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" ,
"https://api.orbit.devotel.io/api/v1/voice/queues/queue_abc/enqueue" ,
bytes . NewBuffer ([] byte ( `{"call_id":"call_abc123","priority":5}` )))
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
req . Header . Set ( "Content-Type" , "application/json" )
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/queues/queue_abc/enqueue' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' ),
'Content-Type: application/json' ,
]);
curl_setopt ( $ch , CURLOPT_POSTFIELDS , '{"call_id":"call_abc123","priority":5}' );
echo curl_exec ( $ch );
Queue Stats
GET /api/v1/voice/queues/{id}/stats
Returns live counts: waiting, connected, abandoned_24h, avg_wait_seconds_24h, plus per-agent presence.
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/queues/queue_abc/stats" \
-H "X-API-Key: dv_live_sk_your_key_here"
const stats = await orbit . voice . queues . stats ( 'queue_abc' )
console . log ( stats . data . waiting )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.get( "https://api.orbit.devotel.io/api/v1/voice/queues/queue_abc/stats" , headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "GET" ,
"https://api.orbit.devotel.io/api/v1/voice/queues/queue_abc/stats" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/queues/queue_abc/stats' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Set Agent Status
POST /api/v1/voice/agents/{id}/status
Agent presence: available, busy, wrap_up, offline.
Drives queue routing — available agents are eligible for the next call.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/agents/agt_jane/status" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "status": "available" }'
await orbit . voice . agents . setStatus ( 'agt_jane' , { status: 'available' })
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ], "Content-Type" : "application/json" }
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/agents/agt_jane/status" ,
headers = headers, json = { "status" : "available" })
print (r.json())
package main
import (
" bytes "
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" ,
"https://api.orbit.devotel.io/api/v1/voice/agents/agt_jane/status" ,
bytes . NewBuffer ([] byte ( `{"status":"available"}` )))
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
req . Header . Set ( "Content-Type" , "application/json" )
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/agents/agt_jane/status' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' ),
'Content-Type: application/json' ,
]);
curl_setopt ( $ch , CURLOPT_POSTFIELDS , '{"status":"available"}' );
echo curl_exec ( $ch );
Ring Groups
A ring group rings multiple destinations (SIP usernames, PSTN numbers, or other ring groups) on a single inbound number. Use for “ring all sales reps until someone answers” patterns, plus hunt-group walks and nested team-of-teams structures.
Create / List / Get / Delete
POST /api/v1/voice/ring-groups
Unique ring group name within the organization (max 100 chars).
strategy
string
default: "simultaneous"
Ring strategy. Implemented today: simultaneous, sequential,
round_robin, longest_idle. fewest_calls was removed from the
accepted set in 2026-05-24 (SCAN-UCAAS-005) — the per-username
call counter the strategy required was never implemented, and
leaving it accepted caused silent fall-back to simultaneous ring.
Any pre-existing ring group whose strategy was fewest_calls has
been backfilled to simultaneous.
Array of { kind, value } entries. kind is one of
"sip_username" (references a row in tenant_sip_credentials),
"pstn" (an E.164-ish digits-only number, ^\+?[0-9]{7,20}$), or
"ring_group" (id of another ring group in the same org —
cycle-checked at save time). Min 1, max 100 members per group.
Per-step ring timeout in seconds (5-300). For simultaneous it’s
the overall ring duration before the actionHook fires; for
sequential it’s the per-username ring duration.
GET /api/v1/voice/ring-groups
GET /api/v1/voice/ring-groups/{id}
DELETE /api/v1/voice/ring-groups/{id}
Strategies
simultaneous — fork the INVITE to every leaf in parallel; first answer wins. PSTN leaves are dialed in parallel with SIP usernames.
sequential — walk SIP usernames in array order, advancing on no-answer via /jambonz/lookup-next. PSTN leaves are appended after the SIP walk so “try Alice’s desk, then Bob’s desk, then ring my mobile” works without a parallel fork.
round_robin — Redis-pinned cursor at {devotel}:ringgroup:rr:<groupId> rotates picks across members one at a time. One target per call so per-call billing aligns with the answerOnBridge contract.
longest_idle — Redis hash at {devotel}:ringgroup:li:<groupId> records per-member last-selected timestamp. Picker chooses the longest-idle member; never-selected members beat any timestamped member; alphabetical tie-break gives deterministic convergence across replicas.
fewest_calls — REMOVED 2026-05-24 (SCAN-UCAAS-005). The verb-builder never implemented the per-username call counter the strategy required, and operators saving fewest_calls got a silent fallback to simultaneous ring. Sending strategy: "fewest_calls" now returns 422.
Wiring a ring group to an inbound number: save the route via PUT /v1/numbers/{e164}/routing with type: "ring_group", config: {}, and ring_group_id: "<group_id>" — the group identity lives on the route row, not in config.
# Create
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/ring-groups" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Sales All-Rings",
"strategy": "simultaneous",
"ringTimeoutSec": 30,
"members": [
{ "kind": "sip_username", "value": "alice" },
{ "kind": "sip_username", "value": "bob" },
{ "kind": "pstn", "value": "+14155557890" }
]
}'
# List / Get / Delete
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/ring-groups" -H "X-API-Key: $K "
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/ring-groups/rg_abc" -H "X-API-Key: $K "
curl -X DELETE "https://api.orbit.devotel.io/api/v1/voice/ring-groups/rg_abc" -H "X-API-Key: $K "
await orbit . voice . ringGroups . create ({
name: 'Sales All-Rings' ,
strategy: 'simultaneous' ,
ringTimeoutSec: 30 ,
members: [
{ kind: 'sip_username' , value: 'alice' },
{ kind: 'sip_username' , value: 'bob' },
{ kind: 'pstn' , value: '+14155557890' },
],
})
await orbit . voice . ringGroups . list ()
await orbit . voice . ringGroups . get ( 'rg_abc' )
await orbit . voice . ringGroups . delete ( 'rg_abc' )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ], "Content-Type" : "application/json" }
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/ring-groups" , headers = headers, json = {
"name" : "Sales All-Rings" ,
"strategy" : "simultaneous" ,
"ringTimeoutSec" : 30 ,
"members" : [
{ "kind" : "sip_username" , "value" : "alice" },
{ "kind" : "pstn" , "value" : "+14155557890" }
]
})
print (r.json())
package main
import (
" bytes "
" net/http "
" os "
)
func main () {
body := [] byte ( `{
"name": "Sales All-Rings",
"strategy": "simultaneous",
"ringTimeoutSec": 30,
"members": [{"kind":"sip_username","value":"alice"}]
}` )
req , _ := http . NewRequest ( "POST" , "https://api.orbit.devotel.io/api/v1/voice/ring-groups" , bytes . NewBuffer ( body ))
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
req . Header . Set ( "Content-Type" , "application/json" )
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/ring-groups' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' ),
'Content-Type: application/json' ,
]);
curl_setopt ( $ch , CURLOPT_POSTFIELDS , json_encode ([
'name' => 'Sales All-Rings' ,
'strategy' => 'simultaneous' ,
'ringTimeoutSec' => 30 ,
'members' => [[ 'kind' => 'sip_username' , 'value' => 'alice' ]],
]));
echo curl_exec ( $ch );
Extensions
SIP extensions for soft-phone registration. Each extension binds one user to a SIP credential pair (managed via the SIP Credentials endpoints) and can receive direct calls or appear in ring groups.
Extensions are managed end-to-end through the dashboard at Voice → Extensions . The underlying call-flow surface (registering, rotating credentials, observing register state) is exposed via the SIP Credentials API at /v1/voice/sip-credentials/* (see SIP Credentials ).
Calendars
Per-extension business-hours calendars route inbound calls to voicemail / a fallback number outside of working hours. Calendars carry an IANA timezone and a list of weekday windows.
Create a Calendar
POST /api/v1/voice/calendars
IANA timezone, e.g. Europe/Istanbul.
Array of { weekday: 0-6, start: "HH:MM", end: "HH:MM" }. weekday follows ISO-8601 (1=Mon, 7=Sun).
Array of ISO-8601 dates (YYYY-MM-DD) treated as off-hours.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/calendars" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Istanbul Office Hours",
"timezone": "Europe/Istanbul",
"weekday_windows": [
{ "weekday": 1, "start": "09:00", "end": "18:00" },
{ "weekday": 2, "start": "09:00", "end": "18:00" }
],
"holidays": ["2026-04-23", "2026-05-01"]
}'
await orbit . voice . calendars . create ({
name: 'Istanbul Office Hours' ,
timezone: 'Europe/Istanbul' ,
weekday_windows: [
{ weekday: 1 , start: '09:00' , end: '18:00' },
{ weekday: 2 , start: '09:00' , end: '18:00' },
],
holidays: [ '2026-04-23' , '2026-05-01' ],
})
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ], "Content-Type" : "application/json" }
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/calendars" , headers = headers, json = {
"name" : "Istanbul Office Hours" ,
"timezone" : "Europe/Istanbul" ,
"weekday_windows" : [{ "weekday" : 1 , "start" : "09:00" , "end" : "18:00" }],
"holidays" : [ "2026-04-23" ]
})
print (r.json())
package main
import (
" bytes "
" net/http "
" os "
)
func main () {
body := [] byte ( `{
"name": "Istanbul Office Hours",
"timezone": "Europe/Istanbul",
"weekday_windows": [{"weekday":1,"start":"09:00","end":"18:00"}],
"holidays": ["2026-04-23"]
}` )
req , _ := http . NewRequest ( "POST" , "https://api.orbit.devotel.io/api/v1/voice/calendars" , bytes . NewBuffer ( body ))
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
req . Header . Set ( "Content-Type" , "application/json" )
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/calendars' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' ),
'Content-Type: application/json' ,
]);
curl_setopt ( $ch , CURLOPT_POSTFIELDS , json_encode ([
'name' => 'Istanbul Office Hours' ,
'timezone' => 'Europe/Istanbul' ,
'weekday_windows' => [[ 'weekday' => 1 , 'start' => '09:00' , 'end' => '18:00' ]],
'holidays' => [ '2026-04-23' ],
]));
echo curl_exec ( $ch );
List / Get / Delete
GET /api/v1/voice/calendars
GET /api/v1/voice/calendars/{id}
DELETE /api/v1/voice/calendars/{id}
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/calendars" -H "X-API-Key: $K "
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/calendars/cal_abc" -H "X-API-Key: $K "
curl -X DELETE "https://api.orbit.devotel.io/api/v1/voice/calendars/cal_abc" -H "X-API-Key: $K "
await orbit . voice . calendars . list ()
await orbit . voice . calendars . get ( 'cal_abc' )
await orbit . voice . calendars . delete ( 'cal_abc' )
import os, requests
h = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
requests.get( "https://api.orbit.devotel.io/api/v1/voice/calendars" , headers = h)
requests.get( "https://api.orbit.devotel.io/api/v1/voice/calendars/cal_abc" , headers = h)
requests.delete( "https://api.orbit.devotel.io/api/v1/voice/calendars/cal_abc" , headers = h)
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "GET" , "https://api.orbit.devotel.io/api/v1/voice/calendars" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/calendars' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Monitoring
Live supervisor surface — listen-in, whisper to the agent, or barge into a call for real-time coaching. All operations require the voice:monitor scope.
Start Listening (silent monitoring)
POST /api/v1/voice/calls/{id}/listen
Open a one-way audio bridge to the supervisor — neither call leg hears the supervisor.
POST /api/v1/voice/calls/{id}/unlisten
Disconnect the supervisor leg.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/listen" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . supervisor . listen ( 'call_abc123' )
await orbit . voice . supervisor . unlisten ( 'call_abc123' )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/listen" , headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" ,
"https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/listen" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/listen' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Whisper to the Agent
POST /api/v1/voice/calls/{id}/whisper
Audio plays only to the agent leg. The customer cannot hear. Use to coach the agent mid-call.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/whisper" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . supervisor . whisper ( 'call_abc123' )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/whisper" , headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" ,
"https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/whisper" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/whisper' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Barge In
POST /api/v1/voice/calls/{id}/barge
Add the supervisor as a third audible participant. Both call legs hear the supervisor.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/barge" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . supervisor . barge ( 'call_abc123' )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/barge" , headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" ,
"https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/barge" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/barge' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Live Monitoring Snapshot
GET /api/v1/voice/monitoring
Returns a denormalised snapshot of every active call, queue, and agent presence — drives the wallboard / Voice → Monitoring page.
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/monitoring" \
-H "X-API-Key: dv_live_sk_your_key_here"
const snap = await orbit . voice . monitoring . snapshot ()
console . log ( snap . data . active_calls )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.get( "https://api.orbit.devotel.io/api/v1/voice/monitoring" , headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "GET" , "https://api.orbit.devotel.io/api/v1/voice/monitoring" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/monitoring' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
{
"data" : {
"active_calls" : 14 ,
"queued_calls" : 3 ,
"available_agents" : 7 ,
"busy_agents" : 5 ,
"calls" : [
{
"id" : "call_abc123" ,
"from" : "+14155552671" ,
"to" : "+18005551234" ,
"agent_id" : "agt_support_jane" ,
"duration_seconds" : 87 ,
"queue_id" : "queue_support" ,
"started_at" : "2026-03-08T11:58:33Z"
}
]
}
}
Real-time Transcripts
GET /api/v1/voice/calls/{callId}/transcript/stream
Server-Sent Events (SSE) stream of live STT transcript chunks for an in-progress call. Each event carries a JSON body of:
{ "speaker": "agent" | "caller", "text": "...", "ts": "ISO-8601" }
Use for live captions, real-time agent assist, or downstream sentiment / intent classification. The stream closes when the call ends.
GET /api/v1/voice/calls/{id}/transcript
Final, post-call transcript (one shot, no streaming). Available once the recording has been processed (~30s after hangup).
POST /api/v1/voice/transcribe
Submit an arbitrary audio URL for asynchronous transcription. Returns a transcript_id to poll for the result.
# SSE stream
curl -N -X GET "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/transcript/stream" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Accept: text/event-stream"
# Final post-call transcript
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/transcript" \
-H "X-API-Key: dv_live_sk_your_key_here"
# Submit audio URL for async transcription
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/transcribe" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "audio_url": "https://example.com/audio.mp3", "language": "en-US" }'
// Final transcript
const tx = await orbit . voice . transcripts . get ( 'call_abc123' )
console . log ( tx . data . utterances )
// Async submit
const job = await orbit . voice . transcripts . submit ({
audio_url: 'https://example.com/audio.mp3' ,
language: 'en-US' ,
})
console . log ( job . data . transcript_id )
import os, requests
h = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
requests.get( "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/transcript" , headers = h)
requests.post( "https://api.orbit.devotel.io/api/v1/voice/transcribe" ,
headers = { ** h, "Content-Type" : "application/json" },
json = { "audio_url" : "https://example.com/audio.mp3" , "language" : "en-US" })
package main
import (
" bytes "
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" , "https://api.orbit.devotel.io/api/v1/voice/transcribe" ,
bytes . NewBuffer ([] byte ( `{"audio_url":"https://example.com/audio.mp3","language":"en-US"}` )))
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
req . Header . Set ( "Content-Type" , "application/json" )
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/transcribe' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' ),
'Content-Type: application/json' ,
]);
curl_setopt ( $ch , CURLOPT_POSTFIELDS , '{"audio_url":"https://example.com/audio.mp3","language":"en-US"}' );
echo curl_exec ( $ch );
AI Call Intelligence
Post-call analysis — sentiment trajectory, key moments, summary, action items, customer effort score. Powered by the agent runtime’s call intelligence model.
Per-Call Intelligence
GET /api/v1/voice/calls/{id}/intelligence
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/intelligence" \
-H "X-API-Key: dv_live_sk_your_key_here"
const intel = await orbit . voice . intelligence . getForCall ( 'call_abc123' )
console . log ( intel . data . summary )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.get( "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/intelligence" , headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "GET" ,
"https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/intelligence" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/intelligence' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
{
"data" : {
"call_id" : "call_abc123" ,
"summary" : "Customer called to dispute a duplicate charge dated 2026-03-05 for $89.00. Agent confirmed the charge was a billing system retry artifact, refunded immediately, sent confirmation email." ,
"sentiment" : {
"overall" : "neutral_to_positive" ,
"trajectory" : "improved" ,
"agent_score" : 0.82 ,
"caller_score" : 0.65
},
"key_moments" : [
{ "ts" : 12 , "type" : "intent_detected" , "label" : "billing_dispute" },
{ "ts" : 47 , "type" : "frustration_peak" , "score" : 0.78 },
{ "ts" : 134 , "type" : "resolution_offered" },
{ "ts" : 189 , "type" : "satisfaction_signal" , "score" : 0.85 }
],
"action_items" : [
"Send refund confirmation email by EOD" ,
"Flag billing system for duplicate-charge investigation"
],
"customer_effort_score" : 2.4 ,
"agent_compliance_flags" : [],
"language" : "en-US" ,
"processed_at" : "2026-03-08T12:08:14Z"
}
}
Run Sentiment On-Demand
POST /api/v1/voice/calls/{id}/sentiment
Forces a re-run of the sentiment analyzer. Used when a call’s transcript has been edited or when the org’s sentiment model has changed.
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/sentiment" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . intelligence . runSentiment ( 'call_abc123' )
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/sentiment" , headers = headers)
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" ,
"https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/sentiment" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/calls/call_abc123/sentiment' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
List Intelligence Records
GET /api/v1/voice/intelligence/calls
List intelligence records for the org with filters by sentiment, agent, date range, and customer-effort threshold.
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/intelligence/calls?sentiment=negative&limit=50" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . intelligence . listCalls ({ sentiment: 'negative' , limit: 50 })
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.get( "https://api.orbit.devotel.io/api/v1/voice/intelligence/calls" ,
headers = headers, params = { "sentiment" : "negative" , "limit" : 50 })
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "GET" ,
"https://api.orbit.devotel.io/api/v1/voice/intelligence/calls?sentiment=negative&limit=50" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/intelligence/calls?sentiment=negative&limit=50' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Trends
GET /api/v1/voice/intelligence/trends
Aggregated time-series of sentiment + effort score, bucketed by day / week. Drives the Voice → Intelligence → Trends dashboard.
ISO-8601 start of the trend window. Defaults to 30 days ago.
ISO-8601 end of the trend window. Defaults to now.
Aggregation bucket: hour, day, week.
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/intelligence/trends?from=2026-02-01&to=2026-03-08&bucket=day" \
-H "X-API-Key: dv_live_sk_your_key_here"
await orbit . voice . intelligence . trends ({
from: '2026-02-01' ,
to: '2026-03-08' ,
bucket: 'day' ,
})
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
r = requests.get( "https://api.orbit.devotel.io/api/v1/voice/intelligence/trends" , headers = headers,
params = { "from" : "2026-02-01" , "to" : "2026-03-08" , "bucket" : "day" })
print (r.json())
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "GET" ,
"https://api.orbit.devotel.io/api/v1/voice/intelligence/trends?from=2026-02-01&to=2026-03-08&bucket=day" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/intelligence/trends?from=2026-02-01&to=2026-03-08&bucket=day' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Voice Quality (VAQI)
Voice Aggregated Quality Index — a 0–100 score derived from MOS, jitter, packet-loss, and one-way audio detection. Surface for SIP-trunk health and per-call diagnostics.
Aggregates
GET /api/v1/voice/quality/aggregates
Per-period rollup of every active SIP trunk: average MOS, p95 jitter, packet loss percentage, VAQI score. Used by the Voice → Quality dashboard.
Per-Carrier Quality
GET /api/v1/voice/quality/carriers
Same shape as /aggregates but bucketed by upstream carrier. Use to compare provider performance across the same time window.
Worst Calls
GET /api/v1/voice/quality/worst-calls
Top-N calls in the period by lowest VAQI / highest packet loss / highest jitter. Drives the “Investigate” CTA in the dashboard.
VAQI Detail
GET /api/v1/voice/vaqi
Full per-call quality matrix (MOS, jitter, packet loss, RTT, audio gaps) with optional call_id filter for a single-call drill-down.
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/quality/aggregates" -H "X-API-Key: $K "
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/quality/carriers" -H "X-API-Key: $K "
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/quality/worst-calls?limit=10" -H "X-API-Key: $K "
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/vaqi?call_id=call_abc123" -H "X-API-Key: $K "
await orbit . voice . quality . aggregates ()
await orbit . voice . quality . carriers ()
await orbit . voice . quality . worstCalls ({ limit: 10 })
await orbit . voice . quality . vaqi ({ call_id: 'call_abc123' })
import os, requests
h = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ]}
requests.get( "https://api.orbit.devotel.io/api/v1/voice/quality/aggregates" , headers = h)
requests.get( "https://api.orbit.devotel.io/api/v1/voice/quality/carriers" , headers = h)
requests.get( "https://api.orbit.devotel.io/api/v1/voice/quality/worst-calls" , headers = h, params = { "limit" : 10 })
requests.get( "https://api.orbit.devotel.io/api/v1/voice/vaqi" , headers = h, params = { "call_id" : "call_abc123" })
package main
import (
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "GET" , "https://api.orbit.devotel.io/api/v1/voice/quality/aggregates" , nil )
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/quality/aggregates' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [ 'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' )]);
echo curl_exec ( $ch );
Softphone Token
POST /api/v1/voice/softphone/token
Mints a short-lived JWT (default 60s expiry) for browser-based softphone clients to register against the Orbit Media / Jambonz cluster without exposing a long-lived API key. Orbit Media is forked from LiveKit OSS under Apache-2 — see attribution .
POST /api/v1/voice/softphone/dial
Server-initiated outbound dial from a softphone session — the softphone client passes the to and Orbit places the call from the bound extension’s caller-ID.
# Mint a JWT
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/softphone/token" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "extension_id": "ext_alice", "ttl_seconds": 60 }'
# Server-initiated outbound dial
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/softphone/dial" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "to": "+14155557890", "extension_id": "ext_alice" }'
const jwt = await orbit . voice . softphone . mintToken ({
extension_id: 'ext_alice' ,
ttl_seconds: 60 ,
})
await orbit . voice . softphone . dial ({ to: '+14155557890' , extension_id: 'ext_alice' })
import os, requests
headers = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ], "Content-Type" : "application/json" }
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/softphone/token" , headers = headers,
json = { "extension_id" : "ext_alice" , "ttl_seconds" : 60 })
print (r.json())
package main
import (
" bytes "
" net/http "
" os "
)
func main () {
req , _ := http . NewRequest ( "POST" ,
"https://api.orbit.devotel.io/api/v1/voice/softphone/token" ,
bytes . NewBuffer ([] byte ( `{"extension_id":"ext_alice","ttl_seconds":60}` )))
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
req . Header . Set ( "Content-Type" , "application/json" )
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/softphone/token' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' ),
'Content-Type: application/json' ,
]);
curl_setopt ( $ch , CURLOPT_POSTFIELDS , '{"extension_id":"ext_alice","ttl_seconds":60}' );
echo curl_exec ( $ch );
Voice Clones
Custom-trained TTS voices for AI agents. Voices are tenant-scoped and require an upfront audio-sample upload — at least 30 seconds of clean speech from a single speaker, ≥ 16 kHz mono WAV / MP3 / FLAC.
Create / List / Delete
POST /api/v1/voice/clones
GET /api/v1/voice/clones
DELETE /api/v1/voice/clones/{id}
Voice IDs returned by GET /api/v1/voice/clones can be referenced from agent configuration via tts_voice_id.
# Create
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/clones" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Brand Voice — EN",
"language": "en-US",
"sample_url": "https://storage.example.com/sample.wav"
}'
# List
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/clones" -H "X-API-Key: $K "
# Delete
curl -X DELETE "https://api.orbit.devotel.io/api/v1/voice/clones/clone_abc" -H "X-API-Key: $K "
await orbit . voice . clones . create ({
name: 'Brand Voice — EN' ,
language: 'en-US' ,
sample_url: 'https://storage.example.com/sample.wav' ,
})
await orbit . voice . clones . list ()
await orbit . voice . clones . delete ( 'clone_abc' )
import os, requests
h = { "X-API-Key" : os.environ[ "ORBIT_API_KEY" ], "Content-Type" : "application/json" }
r = requests.post( "https://api.orbit.devotel.io/api/v1/voice/clones" , headers = h, json = {
"name" : "Brand Voice — EN" ,
"language" : "en-US" ,
"sample_url" : "https://storage.example.com/sample.wav"
})
print (r.json())
package main
import (
" bytes "
" net/http "
" os "
)
func main () {
body := [] byte ( `{
"name": "Brand Voice — EN",
"language": "en-US",
"sample_url": "https://storage.example.com/sample.wav"
}` )
req , _ := http . NewRequest ( "POST" , "https://api.orbit.devotel.io/api/v1/voice/clones" , bytes . NewBuffer ( body ))
req . Header . Set ( "X-API-Key" , os . Getenv ( "ORBIT_API_KEY" ))
req . Header . Set ( "Content-Type" , "application/json" )
http . DefaultClient . Do ( req )
}
<? php
$ch = curl_init ( 'https://api.orbit.devotel.io/api/v1/voice/clones' );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'POST' );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'X-API-Key: ' . getenv ( 'ORBIT_API_KEY' ),
'Content-Type: application/json' ,
]);
curl_setopt ( $ch , CURLOPT_POSTFIELDS , json_encode ([
'name' => 'Brand Voice — EN' ,
'language' => 'en-US' ,
'sample_url' => 'https://storage.example.com/sample.wav' ,
]));
echo curl_exec ( $ch );
Call Statuses
Status Description initiatingCall is being set up ringingDestination phone is ringing in_progressCall is connected and active completedCall ended normally failedCall could not be connected busyDestination returned busy signal no_answerDestination did not answer within timeout cancelledCall was cancelled before connection