Voice API Reference
This page is the complete reference for the EnableX Core Voice API. Every endpoint is documented here: what it does, when to use it, how to construct the request, what to expect in the response, and which webhook events the platform sends back to your server.
Overview
The EnableX Core Voice API is a REST API that gives your application full programmatic control over telephone calls. You can initiate outbound calls from your server, handle inbound calls routed to your webhook, play audio and collect keypad input, connect multiple parties, record conversations, and hang up — all through HTTP requests.
Calls on the platform fall into two categories:
- Outbound calls — Your server initiates a call to a destination phone number. The platform dials out, and once the far end answers, the call progresses based on the
action_on_connectinstructions you supplied. - Inbound calls — A caller dials a number assigned to your project. The platform notifies your webhook, and your server takes control by accepting the call and issuing subsequent API commands.
Both call types share the same call lifecycle model: a call starts, transitions through states (ringing, connected, bridged, disconnected), and at each stage your application can issue commands or receive webhook events. The voice_id returned when a call is initiated is the key you use in every subsequent operation on that call.
Endpoint Quick Reference
Here are all Core Voice API endpoints at a glance. Each is covered in full detail in the sections below.
| Method | Endpoint | Description |
|---|---|---|
| POST | /voice/v1/call |
Initiate an outbound call |
| PUT | /voice/v1/call/{voice_id}/accept |
Accept an incoming inbound call |
| PUT | /voice/v1/call/{voice_id}/play |
Play a voice prompt, TTS, or SSML on an active call |
| PUT | /voice/v1/call/{voice_id}/connect |
Bridge (connect) two call parties together |
| POST | /voice/v1/call/{voice_id}/hold |
Place an active call on hold or resume it |
| PUT | /voice/v1/call/{voice_id}/recording |
Start or stop recording a live call |
| DELETE | /voice/v1/call/{voice_id} |
Hang up and disconnect an active call |
| POST | /voice/v1/webclient/token |
Create a JWT token for web clients or Voice Bot SDK |
Authentication
Every request to the Core Voice API must include an Authorization header using HTTP Basic Authentication. You supply your App ID and App Key — both available in your EnableX Portal project — encoded as a Base64 string.
For full details on obtaining credentials and constructing the header, see the Voice API Authentication page. The header format for every request in this reference is:
Authorization: Basic base64(APP_ID:APP_KEY)
Your App Key is a server-side secret. Never include it in client-side code, mobile apps, or version control. All API calls described on this page must originate from your backend server over HTTPS.
Base URL
All Core Voice API endpoints are served from a single base URL. Prepend this to every endpoint path shown in this reference:
https://api.enablex.io
For example, the outbound call endpoint is accessed at https://api.enablex.io/voice/v1/call.
Make an Outbound Call
The outbound call endpoint instructs EnableX to dial a destination phone number on behalf of your application. Your server does not need to stay connected — you fire the request, get back a voice_id, and the platform handles the rest, notifying your webhook URL as the call progresses through its states.
One important constraint before you dial: the from number you specify must exactly match a number you have provisioned in your EnableX project. This is the Caller Line Identification (CLI) that the destination party sees when their phone rings. If the number does not match what is configured, the call will fail immediately.
The action_on_connect field in the request body is where you define what the platform should do the moment the call is answered. It supports six distinct modes: play a pre-recorded prompt by name, play synthesized speech (TTS), play SSML-formatted speech, stream audio from a URL, bridge the call to another number, or connect the caller directly into a video room.
POST https://api.enablex.io/voice/v1/call
Authorization: Basic base64(APP_ID:APP_KEY)
Content-Type: application/json
Request: Play a Pre-Recorded Prompt
Use this variant when you have already uploaded an audio prompt to the EnableX Portal and want it played as soon as the call connects. Reference the prompt by its configured name and optionally attach a reference string for tracking in webhook events.
curl -X POST https://api.enablex.io/voice/v1/call \
-u YOUR_APP_ID:YOUR_APP_KEY \
-H "Content-Type: application/json" \
-d '{
"name": "service name",
"owner_ref": "xyz",
"auto_record": false,
"from": "CLI number",
"to": "Destination number",
"action_on_connect": {
"play": {
"prompt_name": "prompt_name",
"prompt_ref": "1"
}
},
"event_url": "https://yourserver.com/webhook"
}'
const axios = require('axios');
const response = await axios.post(
'https://api.enablex.io/voice/v1/call',
{
name: 'service name',
owner_ref: 'xyz',
auto_record: false,
from: 'CLI number',
to: 'Destination number',
action_on_connect: {
play: {
prompt_name: 'prompt_name',
prompt_ref: '1'
}
},
event_url: 'https://yourserver.com/webhook'
},
{
auth: {
username: 'YOUR_APP_ID',
password: 'YOUR_APP_KEY'
}
}
);
console.log(response.data);
import requests
from requests.auth import HTTPBasicAuth
response = requests.post(
'https://api.enablex.io/voice/v1/call',
auth=HTTPBasicAuth('YOUR_APP_ID', 'YOUR_APP_KEY'),
json={
'name': 'service name',
'owner_ref': 'xyz',
'auto_record': False,
'from': 'CLI number',
'to': 'Destination number',
'action_on_connect': {
'play': {
'prompt_name': 'prompt_name',
'prompt_ref': '1'
}
},
'event_url': 'https://yourserver.com/webhook'
}
)
print(response.json())
<?php
$payload = json_encode([
'name' => 'service name',
'owner_ref' => 'xyz',
'auto_record' => false,
'from' => 'CLI number',
'to' => 'Destination number',
'action_on_connect' => [
'play' => [
'prompt_name' => 'prompt_name',
'prompt_ref' => '1'
]
],
'event_url' => 'https://yourserver.com/webhook'
]);
$ch = curl_init('https://api.enablex.io/voice/v1/call');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_USERPWD, 'YOUR_APP_ID:YOUR_APP_KEY');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Request: Play TTS Speech
Use this variant to have the platform convert a text string to speech and play it when the call is answered. Specify the text, language, and preferred voice gender.
curl -X POST https://api.enablex.io/voice/v1/call \
-u YOUR_APP_ID:YOUR_APP_KEY \
-H "Content-Type: application/json" \
-d '{
"name": "service name",
"owner_ref": "xyz",
"from": "CLI number",
"to": "Destination number",
"action_on_connect": {
"play": {
"text": "text to be played as speech",
"language": "en-US",
"voice": "female"
}
},
"event_url": "https://yourserver.com/webhook"
}'
const axios = require('axios');
const response = await axios.post(
'https://api.enablex.io/voice/v1/call',
{
name: 'service name',
owner_ref: 'xyz',
from: 'CLI number',
to: 'Destination number',
action_on_connect: {
play: {
text: 'text to be played as speech',
language: 'en-US',
voice: 'female'
}
},
event_url: 'https://yourserver.com/webhook'
},
{
auth: {
username: 'YOUR_APP_ID',
password: 'YOUR_APP_KEY'
}
}
);
console.log(response.data);
import requests
from requests.auth import HTTPBasicAuth
response = requests.post(
'https://api.enablex.io/voice/v1/call',
auth=HTTPBasicAuth('YOUR_APP_ID', 'YOUR_APP_KEY'),
json={
'name': 'service name',
'owner_ref': 'xyz',
'from': 'CLI number',
'to': 'Destination number',
'action_on_connect': {
'play': {
'text': 'text to be played as speech',
'language': 'en-US',
'voice': 'female'
}
},
'event_url': 'https://yourserver.com/webhook'
}
)
print(response.json())
<?php
$payload = json_encode([
'name' => 'service name',
'owner_ref' => 'xyz',
'from' => 'CLI number',
'to' => 'Destination number',
'action_on_connect' => [
'play' => [
'text' => 'text to be played as speech',
'language' => 'en-US',
'voice' => 'female'
]
],
'event_url' => 'https://yourserver.com/webhook'
]);
$ch = curl_init('https://api.enablex.io/voice/v1/call');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_USERPWD, 'YOUR_APP_ID:YOUR_APP_KEY');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Request: Play SSML
Use this variant when you need precise control over pronunciation, pauses, pitch, and rate using SSML markup. The entire SSML document is passed as a string in the ssml field.
curl -X POST https://api.enablex.io/voice/v1/call -u YOUR_APP_ID:YOUR_APP_KEY -H "Content-Type: application/json" -d '{ "name": "service name", "owner_ref": "xyz", "from": "CLI number", "to": "Destination number", "action_on_connect": { "play": { "ssml": "<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US"><voice name="en-US-JennyNeural"><prosody rate="0%" pitch="0%">Hello! Welcome to EnableX.</prosody></voice></speak>" } }, "event_url": "https://yourserver.com/webhook" }'
const axios = require('axios');
const ssml = '<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US"><voice name="en-US-JennyNeural"><prosody rate="0%" pitch="0%">Hello! Welcome to EnableX.</prosody></voice></speak>';
const response = await axios.post(
'https://api.enablex.io/voice/v1/call',
{
name: 'service name',
owner_ref: 'xyz',
from: 'CLI number',
to: 'Destination number',
action_on_connect: {
play: { ssml }
},
event_url: 'https://yourserver.com/webhook'
},
{
auth: {
username: 'YOUR_APP_ID',
password: 'YOUR_APP_KEY'
}
}
);
console.log(response.data);
import requests
from requests.auth import HTTPBasicAuth
ssml = '<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US"><voice name="en-US-JennyNeural"><prosody rate="0%" pitch="0%">Hello! Welcome to EnableX.</prosody></voice></speak>'
response = requests.post(
'https://api.enablex.io/voice/v1/call',
auth=HTTPBasicAuth('YOUR_APP_ID', 'YOUR_APP_KEY'),
json={
'name': 'service name',
'owner_ref': 'xyz',
'from': 'CLI number',
'to': 'Destination number',
'action_on_connect': {
'play': {'ssml': ssml}
},
'event_url': 'https://yourserver.com/webhook'
}
)
print(response.json())
<?php
$ssml = '<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US"><voice name="en-US-JennyNeural"><prosody rate="0%" pitch="0%">Hello! Welcome to EnableX.</prosody></voice></speak>';
$payload = json_encode([
'name' => 'service name',
'owner_ref' => 'xyz',
'from' => 'CLI number',
'to' => 'Destination number',
'action_on_connect' => [
'play' => ['ssml' => $ssml]
],
'event_url' => 'https://yourserver.com/webhook'
]);
$ch = curl_init('https://api.enablex.io/voice/v1/call');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_USERPWD, 'YOUR_APP_ID:YOUR_APP_KEY');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Request: Play Audio from URL
Use this variant to stream an audio file directly from a URL when the call is answered. The file must be served over HTTPS. All audio formats supported by Asterisk are accepted (WAV, MP3, etc.).
curl -X POST https://api.enablex.io/voice/v1/call \
-u YOUR_APP_ID:YOUR_APP_KEY \
-H "Content-Type: application/json" \
-d '{
"name": "service name",
"owner_ref": "xyz",
"from": "CLI number",
"to": "Destination number",
"action_on_connect": {
"play": {
"url": "https://yourserver/prompt.wav"
}
},
"event_url": "https://yourserver.com/webhook"
}'
const axios = require('axios');
const response = await axios.post(
'https://api.enablex.io/voice/v1/call',
{
name: 'service name',
owner_ref: 'xyz',
from: 'CLI number',
to: 'Destination number',
action_on_connect: {
play: {
url: 'https://yourserver/prompt.wav'
}
},
event_url: 'https://yourserver.com/webhook'
},
{
auth: {
username: 'YOUR_APP_ID',
password: 'YOUR_APP_KEY'
}
}
);
console.log(response.data);
import requests
from requests.auth import HTTPBasicAuth
response = requests.post(
'https://api.enablex.io/voice/v1/call',
auth=HTTPBasicAuth('YOUR_APP_ID', 'YOUR_APP_KEY'),
json={
'name': 'service name',
'owner_ref': 'xyz',
'from': 'CLI number',
'to': 'Destination number',
'action_on_connect': {
'play': {
'url': 'https://yourserver/prompt.wav'
}
},
'event_url': 'https://yourserver.com/webhook'
}
)
print(response.json())
<?php
$payload = json_encode([
'name' => 'service name',
'owner_ref' => 'xyz',
'from' => 'CLI number',
'to' => 'Destination number',
'action_on_connect' => [
'play' => [
'url' => 'https://yourserver/prompt.wav'
]
],
'event_url' => 'https://yourserver.com/webhook'
]);
$ch = curl_init('https://api.enablex.io/voice/v1/call');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_USERPWD, 'YOUR_APP_ID:YOUR_APP_KEY');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Request: Bridge to Another Number
Use this variant to connect the called party directly to a third party once they answer. The platform dials the to number, and when answered, immediately initiates a bridge call to the number specified in action_on_connect.connect.to.
curl -X POST https://api.enablex.io/voice/v1/call \
-u YOUR_APP_ID:YOUR_APP_KEY \
-H "Content-Type: application/json" \
-d '{
"name": "service name",
"owner_ref": "xyz",
"from": "CLI number",
"to": "Destination number",
"action_on_connect": {
"connect": {
"from": "Originating CLI",
"to": "Number for bridging"
}
},
"event_url": "https://yourserver.com/webhook"
}'
const axios = require('axios');
const response = await axios.post(
'https://api.enablex.io/voice/v1/call',
{
name: 'service name',
owner_ref: 'xyz',
from: 'CLI number',
to: 'Destination number',
action_on_connect: {
connect: {
from: 'Originating CLI',
to: 'Number for bridging'
}
},
event_url: 'https://yourserver.com/webhook'
},
{
auth: {
username: 'YOUR_APP_ID',
password: 'YOUR_APP_KEY'
}
}
);
console.log(response.data);
import requests
from requests.auth import HTTPBasicAuth
response = requests.post(
'https://api.enablex.io/voice/v1/call',
auth=HTTPBasicAuth('YOUR_APP_ID', 'YOUR_APP_KEY'),
json={
'name': 'service name',
'owner_ref': 'xyz',
'from': 'CLI number',
'to': 'Destination number',
'action_on_connect': {
'connect': {
'from': 'Originating CLI',
'to': 'Number for bridging'
}
},
'event_url': 'https://yourserver.com/webhook'
}
)
print(response.json())
<?php
$payload = json_encode([
'name' => 'service name',
'owner_ref' => 'xyz',
'from' => 'CLI number',
'to' => 'Destination number',
'action_on_connect' => [
'connect' => [
'from' => 'Originating CLI',
'to' => 'Number for bridging'
]
],
'event_url' => 'https://yourserver.com/webhook'
]);
$ch = curl_init('https://api.enablex.io/voice/v1/call');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_USERPWD, 'YOUR_APP_ID:YOUR_APP_KEY');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Request: Join a Video Room
Use this variant to connect the answering party into a video conference room. The call is bridged into the specified EnableX Video Room, allowing the phone caller to participate as an audio participant in the video session.
curl -X POST https://api.enablex.io/voice/v1/call \
-u YOUR_APP_ID:YOUR_APP_KEY \
-H "Content-Type: application/json" \
-d '{
"name": "service name",
"owner_ref": "xyz",
"from": "CLI number",
"to": "Destination number",
"action_on_connect": {
"room": {
"room_id": "Video Room ID"
}
},
"event_url": "https://yourserver.com/webhook"
}'
const axios = require('axios');
const response = await axios.post(
'https://api.enablex.io/voice/v1/call',
{
name: 'service name',
owner_ref: 'xyz',
from: 'CLI number',
to: 'Destination number',
action_on_connect: {
room: {
room_id: 'Video Room ID'
}
},
event_url: 'https://yourserver.com/webhook'
},
{
auth: {
username: 'YOUR_APP_ID',
password: 'YOUR_APP_KEY'
}
}
);
console.log(response.data);
import requests
from requests.auth import HTTPBasicAuth
response = requests.post(
'https://api.enablex.io/voice/v1/call',
auth=HTTPBasicAuth('YOUR_APP_ID', 'YOUR_APP_KEY'),
json={
'name': 'service name',
'owner_ref': 'xyz',
'from': 'CLI number',
'to': 'Destination number',
'action_on_connect': {
'room': {
'room_id': 'Video Room ID'
}
},
'event_url': 'https://yourserver.com/webhook'
}
)
print(response.json())
<?php
$payload = json_encode([
'name' => 'service name',
'owner_ref' => 'xyz',
'from' => 'CLI number',
'to' => 'Destination number',
'action_on_connect' => [
'room' => [
'room_id' => 'Video Room ID'
]
],
'event_url' => 'https://yourserver.com/webhook'
]);
$ch = curl_init('https://api.enablex.io/voice/v1/call');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_USERPWD, 'YOUR_APP_ID:YOUR_APP_KEY');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Request Field Reference
| Key | Description |
|---|---|
name |
Optional label for this call service. Useful for your own internal logging. |
owner_ref |
Free-form string returned as-is in all webhook events for this call. Use it to correlate API calls with your own records — for example, a customer ID or order number. |
auto_record |
Boolean. Set to true to automatically start recording as soon as the call connects. Default is false. |
from |
The CLI (Caller Line Identification) to present to the called party. Must exactly match a number provisioned in your EnableX project. A mismatch will cause the call to fail. |
to |
The destination phone number to dial. |
action_on_connect.play |
Play a pre-recorded prompt (by prompt_name), synthesized speech (text), or SSML (ssml) once the call is answered. |
action_on_connect.connect |
Bridge the answered call to another phone number or SIP URI immediately on connect. |
action_on_connect.room |
Connect the answered call into an EnableX Video Room identified by room_id. |
event_url |
The HTTPS URL on your server where EnableX will POST webhook event notifications for this call. |
Success Response
On a successful dial-out request, the platform returns HTTP 200 with the call identifier and initial state. Store the voice_id — you will use it in all subsequent commands for this call.
{
"voice_id": "f1aa71c0-8f2a-4fe8-b5ef-9a330454ef58",
"state": "initiated",
"from": "Originating CLI",
"to": "Called Number",
"timestamp": "2020-02-16T10:52:00Z"
}
If the call could not be initiated (for example, a CLI mismatch or a provisioning issue), the state field will be "failed" instead of "initiated".
Error Response
When the request itself is rejected — invalid payload, authentication failure, or a configuration problem — the platform returns an error body with a 4-digit result code and a descriptive message:
{
"result": 1234,
"msg": "description of the error",
"state": "failed",
"timestamp": "2020-02-16T10:52:00Z"
}
Webhook Events for Outbound Calls
As the outbound call progresses, EnableX sends event notifications to your event_url. Each notification includes the voice_id along with the current call state and contextual fields depending on what happened.
{
"voice_id": "f1aa71c0-8f2a-4fe8-b5ef-9a330454ef58",
"state": "ringing",
"from": "CLI number",
"to": "Destination number",
"recording_url": "https://domain/sample.wav",
"recording_duration": 50,
"disconnect_reason": "Normal Clearing",
"room_id": "5f83f9743a8ad03af54eab4b",
"timestamp": "2020-02-16T10:52:00Z"
}
The state field tells you exactly where the call is in its lifecycle:
| State | Meaning |
|---|---|
ringing |
The destination number is ringing but has not yet been answered. |
connected |
The destination party answered the call. The call is now active. |
disconnected |
The call was not answered or was declined by the destination party. |
bridging |
A bridge call has been initiated — the second party is being dialed. |
bridged |
The bridge call was accepted and both parties are now connected. |
bridged_disconnected |
The bridged party has disconnected from the call. |
failed |
The bridging attempt failed — the second party could not be reached. |
Fields like recording_url, recording_duration, disconnect_reason, and room_id are only populated in the events where they are relevant. For example, recording_url only appears in a disconnected event when a recording was made. Your webhook handler should check for their presence before using them.
Handle an Incoming Call
When a caller dials a number provisioned to your EnableX project, the platform routes the call to your application via a webhook notification. Your server then uses the Accept Call API to take control of the call. The flow is always the same: configure in the portal, receive a webhook, then accept and manage.
Configure the Portal for Incoming Calls
Before your application can receive calls, you need to complete three steps in the EnableX Portal for your Voice project:
- Assign a Phone Number — Add a rented number to your Voice project. This is the number callers will dial.
- Configure a Voice Prompt (optional) — Upload or configure a greeting prompt that plays before your webhook is called. This is what the caller hears while the platform is setting up the session.
- Configure a Webhook URL — Set the HTTPS URL on your server where EnableX should send the incoming call notification.
Securing Your Webhook — HTTP Basic Authentication
EnableX supports HTTP Basic Authentication for incoming call webhook delivery. When enabled, EnableX includes your credentials in every webhook POST so your server can verify the request is genuinely from EnableX before processing it.
To enable this, check the HTTP Authentication checkbox in the webhook configuration screen in the EnableX Portal and enter a username and password. EnableX will include these credentials in the Authorization header of every incoming call notification sent to your endpoint.
Your webhook URL must be publicly reachable over HTTPS. During local development, use a tunnelling tool such as ngrok http 3000 to expose your local server, then paste the generated HTTPS URL into the portal.
Shared vs. Dedicated Numbers
How the call is routed depends on whether the number is shared or dedicated to your project:
- Dedicated number — All calls to this number are immediately routed to your application. The configured greeting plays and call control passes to your webhook.
- Shared number — Multiple applications share the same physical number. When a caller dials in, the platform presents a PIN. The caller enters the PIN assigned to your application, which routes the call to your webhook.
Incoming Call Webhook Notification
When a call arrives for your project, EnableX sends a POST to your configured webhook URL with the following payload. The voice_id in this notification is the identifier you must use to accept or interact with the call.
{
"voice_id": "f1aa71c0-8f2a-4fe8-b5ef-9a330454ef58",
"state": "incomingcall",
"from": "CLI number",
"to": "Destination number",
"timestamp": "2020-02-16T10:52:00Z"
}
| Field | Description |
|---|---|
voice_id |
Unique identifier for this call. Use this value in the Accept Call API and all subsequent commands. |
state |
Always "incomingcall" for this notification — indicates an inbound call is waiting to be accepted. |
from |
The caller's phone number (CLI). |
to |
The EnableX number the caller dialed — i.e., your provisioned number. |
timestamp |
UTC timestamp of when the incoming call was received. |
Accept an Incoming Call
To answer and take control of an incoming call, send a PUT request to the accept endpoint using the voice_id from the webhook notification. No request body is needed — the presence of a valid voice_id is sufficient to accept the call.
curl -X PUT https://api.enablex.io/voice/v1/call/{voice_id}/accept \
-u YOUR_APP_ID:YOUR_APP_KEY
const axios = require('axios');
// Replace {voice_id} with the value from the incoming call webhook
const voiceId = 'VOICE_ID_FROM_WEBHOOK';
const response = await axios.put(
`https://api.enablex.io/voice/v1/call/${voiceId}/accept`,
{},
{
auth: {
username: 'YOUR_APP_ID',
password: 'YOUR_APP_KEY'
}
}
);
console.log(response.data);
import requests
from requests.auth import HTTPBasicAuth
# Replace with the voice_id from the incoming call webhook
voice_id = 'VOICE_ID_FROM_WEBHOOK'
response = requests.put(
f'https://api.enablex.io/voice/v1/call/{voice_id}/accept',
auth=HTTPBasicAuth('YOUR_APP_ID', 'YOUR_APP_KEY')
)
print(response.json())
<?php
// Replace with the voice_id from the incoming call webhook
$voiceId = 'VOICE_ID_FROM_WEBHOOK';
$ch = curl_init("https://api.enablex.io/voice/v1/call/{$voiceId}/accept");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_USERPWD, 'YOUR_APP_ID:YOUR_APP_KEY');
$response = curl_exec($ch);
curl_close($ch);
echo $response;
A successful accept returns HTTP 200:
{
"voice_id": "3d561239-a1e8-43dd-bd6d-73f60b027a81",
"status": "success",
"timestamp": "2023-01-10T13:47:59.667Z"
}
Webhook After Accept
After your application accepts the call, EnableX sends a connected event to your webhook confirming the call is now active and under your control:
{
"voice_id": "a8e3607c-46a1-43f6-b0be-f3ead722a6b1",
"from": "CLI number",
"to": "Destination number",
"state": "connected",
"timestamp": "2020-02-16T10:52:00Z"
}
The incoming call webhook is time-sensitive. The caller is waiting on hold. Your webhook handler should respond to the notification and call the Accept endpoint without delay. If you need to do processing (such as database lookups to route the call), do it asynchronously after acceptance.
Play Voice Prompt
Once a call is active — whether outbound or inbound — you can play audio to the caller at any time using the play endpoint. This is the primary mechanism for IVR (Interactive Voice Response) menus, welcome announcements, error messages, and any other audio interaction during a call.
The play endpoint supports four audio sources: a pre-recorded prompt uploaded to the EnableX portal (referenced by name), synthesized speech via the TTS engine (text or SSML), or audio streamed from a URL. You can also enable DTMF collection or speech recognition alongside the audio so the caller can respond.
PUT https://api.enablex.io/voice/v1/call/{voice_id}/play
Authorization: Basic base64(APP_ID:APP_KEY)
Content-Type: application/json
Mode 1: Pre-Recorded Prompt
Play an audio file already uploaded to the EnableX Portal. Reference it by its configured prompt name.
curl -X PUT https://api.enablex.io/voice/v1/call/{voice_id}/play \
-u YOUR_APP_ID:YOUR_APP_KEY \
-H "Content-Type: application/json" \
-d '{
"prompt_name": "prompt_name",
"prompt_ref": "welcome_ref",
"text": "text to be played as speech",
"language": "en-US",
"voice": "female",
"dtmf": true,
"asr": true
}'
const axios = require('axios');
const response = await axios.put(
`https://api.enablex.io/voice/v1/call/${voiceId}/play`,
{
prompt_name: 'prompt_name',
prompt_ref: 'welcome_ref',
text: 'text to be played as speech',
language: 'en-US',
voice: 'female',
dtmf: true,
asr: true
},
{
auth: {
username: 'YOUR_APP_ID',
password: 'YOUR_APP_KEY'
}
}
);
console.log(response.data);
import requests
from requests.auth import HTTPBasicAuth
response = requests.put(
f'https://api.enablex.io/voice/v1/call/{voice_id}/play',
auth=HTTPBasicAuth('YOUR_APP_ID', 'YOUR_APP_KEY'),
json={
'prompt_name': 'prompt_name',
'prompt_ref': 'welcome_ref',
'text': 'text to be played as speech',
'language': 'en-US',
'voice': 'female',
'dtmf': True,
'asr': True
}
)
print(response.json())
<?php
$payload = json_encode([
'prompt_name' => 'prompt_name',
'prompt_ref' => 'welcome_ref',
'text' => 'text to be played as speech',
'language' => 'en-US',
'voice' => 'female',
'dtmf' => true,
'asr' => true
]);
$ch = curl_init("https://api.enablex.io/voice/v1/call/{$voiceId}/play");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_USERPWD, 'YOUR_APP_ID:YOUR_APP_KEY');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Mode 2: TTS with Text
Convert a plain text string to speech using the platform TTS engine. Specify the language and voice gender.
curl -X PUT https://api.enablex.io/voice/v1/call/{voice_id}/play \
-u YOUR_APP_ID:YOUR_APP_KEY \
-H "Content-Type: application/json" \
-d '{
"prompt_ref": "welcome_ref",
"text": "text to be played as speech",
"language": "en-US",
"voice": "female",
"dtmf": true,
"asr": true
}'
const axios = require('axios');
const response = await axios.put(
`https://api.enablex.io/voice/v1/call/${voiceId}/play`,
{
prompt_ref: 'welcome_ref',
text: 'text to be played as speech',
language: 'en-US',
voice: 'female',
dtmf: true,
asr: true
},
{
auth: {
username: 'YOUR_APP_ID',
password: 'YOUR_APP_KEY'
}
}
);
console.log(response.data);
import requests
from requests.auth import HTTPBasicAuth
response = requests.put(
f'https://api.enablex.io/voice/v1/call/{voice_id}/play',
auth=HTTPBasicAuth('YOUR_APP_ID', 'YOUR_APP_KEY'),
json={
'prompt_ref': 'welcome_ref',
'text': 'text to be played as speech',
'language': 'en-US',
'voice': 'female',
'dtmf': True,
'asr': True
}
)
print(response.json())
<?php
$payload = json_encode([
'prompt_ref' => 'welcome_ref',
'text' => 'text to be played as speech',
'language' => 'en-US',
'voice' => 'female',
'dtmf' => true,
'asr' => true
]);
$ch = curl_init("https://api.enablex.io/voice/v1/call/{$voiceId}/play");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_USERPWD, 'YOUR_APP_ID:YOUR_APP_KEY');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Mode 3: TTS with SSML
Use SSML markup for fine-grained control over pronunciation, pauses, pitch, and rate. Pass the full SSML document as a string in the ssml field.
curl -X PUT https://api.enablex.io/voice/v1/call/{voice_id}/play -u YOUR_APP_ID:YOUR_APP_KEY -H "Content-Type: application/json" -d '{ "prompt_ref": "welcome_ref", "ssml": "<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US"><voice name="en-US-JennyNeural"><prosody rate="0%" pitch="0%">Hello! Welcome to EnableX.</prosody></voice></speak>", "dtmf": true, "asr": true }'
const axios = require('axios');
const ssml = '<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US"><voice name="en-US-JennyNeural"><prosody rate="0%" pitch="0%">Hello! Welcome to EnableX.</prosody></voice></speak>';
const response = await axios.put(
`https://api.enablex.io/voice/v1/call/${voiceId}/play`,
{
prompt_ref: 'welcome_ref',
ssml,
dtmf: true,
asr: true
},
{
auth: {
username: 'YOUR_APP_ID',
password: 'YOUR_APP_KEY'
}
}
);
console.log(response.data);
import requests
from requests.auth import HTTPBasicAuth
ssml = '<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US"><voice name="en-US-JennyNeural"><prosody rate="0%" pitch="0%">Hello! Welcome to EnableX.</prosody></voice></speak>'
response = requests.put(
f'https://api.enablex.io/voice/v1/call/{voice_id}/play',
auth=HTTPBasicAuth('YOUR_APP_ID', 'YOUR_APP_KEY'),
json={
'prompt_ref': 'welcome_ref',
'ssml': ssml,
'dtmf': True,
'asr': True
}
)
print(response.json())
<?php
$ssml = '<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US"><voice name="en-US-JennyNeural"><prosody rate="0%" pitch="0%">Hello! Welcome to EnableX.</prosody></voice></speak>';
$payload = json_encode([
'prompt_ref' => 'welcome_ref',
'ssml' => $ssml,
'dtmf' => true,
'asr' => true
]);
$ch = curl_init("https://api.enablex.io/voice/v1/call/{$voiceId}/play");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_USERPWD, 'YOUR_APP_ID:YOUR_APP_KEY');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Mode 4: Audio from URL
Stream an audio file directly from an HTTPS URL. All Asterisk-supported audio formats are accepted (WAV, MP3, etc.).
curl -X PUT https://api.enablex.io/voice/v1/call/{voice_id}/play \
-u YOUR_APP_ID:YOUR_APP_KEY \
-H "Content-Type: application/json" \
-d '{
"prompt_ref": "welcome_ref",
"url": "https://yourserver/prompt.wav",
"dtmf": true,
"asr": true
}'
const axios = require('axios');
const response = await axios.put(
`https://api.enablex.io/voice/v1/call/${voiceId}/play`,
{
prompt_ref: 'welcome_ref',
url: 'https://yourserver/prompt.wav',
dtmf: true,
asr: true
},
{
auth: {
username: 'YOUR_APP_ID',
password: 'YOUR_APP_KEY'
}
}
);
console.log(response.data);
import requests
from requests.auth import HTTPBasicAuth
response = requests.put(
f'https://api.enablex.io/voice/v1/call/{voice_id}/play',
auth=HTTPBasicAuth('YOUR_APP_ID', 'YOUR_APP_KEY'),
json={
'prompt_ref': 'welcome_ref',
'url': 'https://yourserver/prompt.wav',
'dtmf': True,
'asr': True
}
)
print(response.json())
<?php
$payload = json_encode([
'prompt_ref' => 'welcome_ref',
'url' => 'https://yourserver/prompt.wav',
'dtmf' => true,
'asr' => true
]);
$ch = curl_init("https://api.enablex.io/voice/v1/call/{$voiceId}/play");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_USERPWD, 'YOUR_APP_ID:YOUR_APP_KEY');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Request Field Reference
| Key | Description | Required |
|---|---|---|
prompt_name |
Identifier of the pre-recorded prompt configured in the EnableX portal. | Required if text is not used |
prompt_ref |
A user-specified reference string that is echoed back in webhook events. Use this to track which prompt triggered which response. | Optional |
text |
The text string to convert to speech and play. Uses the platform TTS engine. | Required if prompt_name is not used |
voice |
Voice gender for TTS: male or female. Required when text is used. |
Conditional |
language |
Language code for TTS synthesis, e.g. en-US. Required when text is used. |
Conditional |
dtmf |
Set to true to wait for the caller to press a keypad digit after the prompt finishes playing. |
Optional |
asr |
Set to true to enable speech recognition after the prompt plays. The recognized speech is returned in a webhook event. |
Optional |
Success Response
A successful play request returns HTTP 200 with a playstate of "initiated", confirming the platform has started playing the audio:
{
"voice_id": "f1aa71c0-8f2a-4fe8-b5ef-9a330454ef58",
"playstate": "initiated",
"from": "CLI number",
"to": "Destination number",
"timestamp": "2020-02-16T10:52:00Z"
}
Webhook Event
When the prompt finishes playing, or when the caller responds with DTMF, EnableX sends a webhook event with the final playstate and any collected input:
{
"voice_id": "a8e3607c-46a1-43f6-b0be-f3ead722a6b1",
"from": "CLI number",
"to": "Destination number",
"playstate": "playfinished",
"prompt_ref": "prompt_ref",
"digit": 12345,
"timestamp": "2020-02-16T10:52:00Z"
}
The playstate field in the webhook tells you the outcome of the play operation:
| playstate | Meaning |
|---|---|
playfinished |
The audio prompt completed playing and no DTMF was collected (or DTMF collection was not enabled). |
digitcollected |
The caller pressed a keypad digit. The digit field in the event contains the value entered. |
menutimeout |
DTMF was enabled but the caller did not press any key within the timeout window. |
failed |
The platform could not play the prompt — see error responses below for causes. |
Error Responses
Several conditions can prevent a prompt from playing. Each has a distinct HTTP status code and result code:
405 Method Not Allowed — call is bridged
{
"voice_id": "...",
"from": "...",
"to": "...",
"playstate": "failed",
"result": 6112,
"msg": "Call is in bridged mode, can't play media",
"timestamp": "..."
}
406 Not Acceptable — unsupported TTS voice
{
"result": 6117,
"msg": "Voice not supported for text-to-speech conversion",
"playstate": "failed",
"timestamp": "..."
}
408 Request Pending — previous play request still in progress
{
"result": 6114,
"msg": "Play request pending",
"playstate": "failed",
"timestamp": "..."
}
400 Bad Request — media request could not be processed
{
"result": 400,
"msg": "Media Request Failed",
"playstate": "failed",
"timestamp": "..."
}
503 Service Unavailable — TTS engine error
{
"result": 6107,
"msg": "Error doing text to speech conversion",
"playstate": "failed",
"timestamp": "..."
}
You cannot issue a new play request while a previous one is still in progress. Wait for the playfinished, digitcollected, or menutimeout webhook event before sending the next play command. Sending a second play request too early will return result code 6114.
Bridge Call
The bridge endpoint connects a third party to an existing active call by dialing out to a new number and joining both parties together into a two-way conversation. This is the mechanism behind call routing to an agent, supervisor escalation, and consultative transfers.
When you send a bridge request, the platform dials the number in to using the CLI in from. Once that second party answers, both the original caller and the new party are connected. Your application receives webhook events tracking each stage of the bridging process.
PUT https://api.enablex.io/voice/v1/call/{voice_id}/connect
Authorization: Basic base64(APP_ID:APP_KEY)
Content-Type: application/json
Request Body
The bridge request requires two fields — both mandatory. The to field accepts a phone number or a SIP URI.
curl -X PUT https://api.enablex.io/voice/v1/call/{voice_id}/connect \
-u YOUR_APP_ID:YOUR_APP_KEY \
-H "Content-Type: application/json" \
-d '{
"from": "CLI number",
"to": "Destination number"
}'
const axios = require('axios');
const response = await axios.put(
`https://api.enablex.io/voice/v1/call/${voiceId}/connect`,
{
from: 'CLI number',
to: 'Destination number'
},
{
auth: {
username: 'YOUR_APP_ID',
password: 'YOUR_APP_KEY'
}
}
);
console.log(response.data);
import requests
from requests.auth import HTTPBasicAuth
response = requests.put(
f'https://api.enablex.io/voice/v1/call/{voice_id}/connect',
auth=HTTPBasicAuth('YOUR_APP_ID', 'YOUR_APP_KEY'),
json={
'from': 'CLI number',
'to': 'Destination number'
}
)
print(response.json())
<?php
$payload = json_encode([
'from' => 'CLI number',
'to' => 'Destination number'
]);
$ch = curl_init("https://api.enablex.io/voice/v1/call/{$voiceId}/connect");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_USERPWD, 'YOUR_APP_ID:YOUR_APP_KEY');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Response
The platform acknowledges the bridge request immediately and returns an "initiated" state:
{
"voice_id": "f1aa71c0-8f2a-4fe8-b5ef-9a330454ef58",
"state": "initiated",
"from": "CLI number",
"to": "Destination number",
"timestamp": "2017-02-16T10:52:00Z"
}
Webhook Events
As the bridge call progresses, EnableX sends events to your webhook. The state field tracks each phase:
{
"voice_Id": "f1aa71c0-8f2a-4fe8-b5ef-9a330454ef58",
"state": "bridged",
"from": "CLI number",
"to": "Destination number",
"timestamp": "2017-02-16T10:52:00Z"
}
| State | Meaning |
|---|---|
initiated |
The bridge request has been accepted and the platform is dialing the second party. |
bridging |
The destination number is ringing — the second party has not yet answered. |
bridged |
The second party answered. Both parties are now connected in the bridged call. |
failed |
The bridging attempt failed — the second party was unreachable, busy, or declined. |
bridge_disconnected |
The bridged (second) party has disconnected while the original caller is still connected. |
disconnected |
The original caller has disconnected from the call. |
Error Responses
404 Application Not Found
{
"result": "6110",
"msg": "Application Not Found",
"state": "failed",
"timestamp": "..."
}
404 Phone Number Not Found
{
"result": "6118",
"msg": "Phone number not found for the service",
"state": "failed",
"timestamp": "..."
}
Hold / Unhold
The hold endpoint places a bridged call on hold or resumes it from hold. When a call is placed on hold, the held party typically hears music-on-hold while the other party waits or performs another action. Sending the same endpoint with hold: false resumes the call.
Hold only applies to calls that are already in a bridged state — you cannot place a call on hold if it has not yet been bridged to a second party.
POST https://api.enablex.io/voice/v1/call/{voice_id}/hold
Authorization: Basic base64(APP_ID:APP_KEY)
Content-Type: application/json
Request: Place on Hold
To put the call on hold, send hold: true:
curl -X POST https://api.enablex.io/voice/v1/call/{voice_id}/hold \
-u YOUR_APP_ID:YOUR_APP_KEY \
-H "Content-Type: application/json" \
-d '{"hold": true}'
const axios = require('axios');
const response = await axios.post(
`https://api.enablex.io/voice/v1/call/${voiceId}/hold`,
{ hold: true },
{
auth: {
username: 'YOUR_APP_ID',
password: 'YOUR_APP_KEY'
}
}
);
console.log(response.data);
import requests
from requests.auth import HTTPBasicAuth
response = requests.post(
f'https://api.enablex.io/voice/v1/call/{voice_id}/hold',
auth=HTTPBasicAuth('YOUR_APP_ID', 'YOUR_APP_KEY'),
json={'hold': True}
)
print(response.json())
<?php
$payload = json_encode(['hold' => true]);
$ch = curl_init("https://api.enablex.io/voice/v1/call/{$voiceId}/hold");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_USERPWD, 'YOUR_APP_ID:YOUR_APP_KEY');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Request: Resume from Hold
To take the call off hold and resume the bridged conversation, send hold: false:
curl -X POST https://api.enablex.io/voice/v1/call/{voice_id}/hold \
-u YOUR_APP_ID:YOUR_APP_KEY \
-H "Content-Type: application/json" \
-d '{"hold": false}'
const axios = require('axios');
const response = await axios.post(
`https://api.enablex.io/voice/v1/call/${voiceId}/hold`,
{ hold: false },
{
auth: {
username: 'YOUR_APP_ID',
password: 'YOUR_APP_KEY'
}
}
);
console.log(response.data);
import requests
from requests.auth import HTTPBasicAuth
response = requests.post(
f'https://api.enablex.io/voice/v1/call/{voice_id}/hold',
auth=HTTPBasicAuth('YOUR_APP_ID', 'YOUR_APP_KEY'),
json={'hold': False}
)
print(response.json())
<?php
$payload = json_encode(['hold' => false]);
$ch = curl_init("https://api.enablex.io/voice/v1/call/{$voiceId}/hold");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_USERPWD, 'YOUR_APP_ID:YOUR_APP_KEY');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
The hold field is a boolean and is required in every request to this endpoint. There are no other fields.
Start / Stop Recording
The recording endpoint lets you start or stop recording a live call on demand. This complements the auto_record flag on the outbound call request — use auto_record when you want recording to begin immediately on connect, and use this endpoint when you need to start or stop recording at a specific point during the call.
Recording can only be started while the call is in a bridged state. If the call has not been bridged yet, the request will be rejected. Each recording is assigned a recording_id which you use to retrieve the file later.
PUT https://api.enablex.io/voice/v1/call/{voice_id}/recording
Authorization: Basic base64(APP_ID:APP_KEY)
Content-Type: application/json
Request: Start Recording
To begin recording, set start to true and provide a name for the recording file. The name must be unique — if a recording with that name already exists, the request will fail.
curl -X PUT https://api.enablex.io/voice/v1/call/{voice_id}/recording \
-u YOUR_APP_ID:YOUR_APP_KEY \
-H "Content-Type: application/json" \
-d '{
"start": true,
"recording_name": "my_voice_recording"
}'
const axios = require('axios');
const response = await axios.put(
`https://api.enablex.io/voice/v1/call/${voiceId}/recording`,
{
start: true,
recording_name: 'my_voice_recording'
},
{
auth: {
username: 'YOUR_APP_ID',
password: 'YOUR_APP_KEY'
}
}
);
console.log(response.data);
import requests
from requests.auth import HTTPBasicAuth
response = requests.put(
f'https://api.enablex.io/voice/v1/call/{voice_id}/recording',
auth=HTTPBasicAuth('YOUR_APP_ID', 'YOUR_APP_KEY'),
json={
'start': True,
'recording_name': 'my_voice_recording'
}
)
print(response.json())
<?php
$payload = json_encode([
'start' => true,
'recording_name' => 'my_voice_recording'
]);
$ch = curl_init("https://api.enablex.io/voice/v1/call/{$voiceId}/recording");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_USERPWD, 'YOUR_APP_ID:YOUR_APP_KEY');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Request: Stop Recording
To end the recording, set stop to true:
curl -X PUT https://api.enablex.io/voice/v1/call/{voice_id}/recording \
-u YOUR_APP_ID:YOUR_APP_KEY \
-H "Content-Type: application/json" \
-d '{"stop": true}'
const axios = require('axios');
const response = await axios.put(
`https://api.enablex.io/voice/v1/call/${voiceId}/recording`,
{ stop: true },
{
auth: {
username: 'YOUR_APP_ID',
password: 'YOUR_APP_KEY'
}
}
);
console.log(response.data);
import requests
from requests.auth import HTTPBasicAuth
response = requests.put(
f'https://api.enablex.io/voice/v1/call/{voice_id}/recording',
auth=HTTPBasicAuth('YOUR_APP_ID', 'YOUR_APP_KEY'),
json={'stop': True}
)
print(response.json())
<?php
$payload = json_encode(['stop' => true]);
$ch = curl_init("https://api.enablex.io/voice/v1/call/{$voiceId}/recording");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_USERPWD, 'YOUR_APP_ID:YOUR_APP_KEY');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
A single request must contain either start: true or stop: true — not both. Sending both fields in the same request is not valid.
Field Reference
| Key | Description |
|---|---|
start |
Boolean. Set to true to begin recording the call. |
recording_name |
Name for the recording file. Required when start: true. Must be unique — if a file with this name already exists, the request fails. |
stop |
Boolean. Set to true to end an in-progress recording. |
Success Response
A successful recording start or stop returns HTTP 200 with the recording_id assigned to this recording file:
{
"status": "success",
"from": "CLI number",
"to": "Destination number",
"voice_id": "f1aa71c0-8f2a-4fe8-b5ef-9a330454ef58",
"recording_id": "uniquerecording identifier",
"timestamp": "2017-02-16T10:52:00Z"
}
Save the recording_id — you will need it to retrieve the recording file after the call ends.
Error Responses
Failed to start recording on bridge
{
"status": "failed",
"result": "6119",
"msg": "Failed to start recording on bridge"
}
409 — Call is not yet bridged
{
"status": "failed",
"result": "409",
"msg": "Call is not bridged yet"
}
408 — Call is already being recorded
{
"status": "failed",
"result": "408",
"msg": "Call is already recording"
}
Webhook Events
EnableX sends recording lifecycle events to your webhook. The state field reflects the current status of the recording:
{
"voice_id": "f1aa71c0-8f2a-4fe8-b5ef-9a330454ef58",
"state": "completed",
"timestamp": "2017-02-16T10:52:00Z",
"description": "Recording Started"
}
| State | Meaning |
|---|---|
started |
Recording has begun and is in progress. |
completed |
Recording has been stopped and the file is being finalized. |
failed |
The recording operation could not be completed. |
Hang Up
The hang-up endpoint disconnects an active call and releases all associated resources. Use this when your application logic determines the call should end — for example, after an IVR flow completes, after a timeout, or when the caller reaches a dead end in your routing logic.
No request body is needed. The call is identified entirely by the voice_id in the URL path.
curl -X DELETE https://api.enablex.io/voice/v1/call/{voice_id} \
-u YOUR_APP_ID:YOUR_APP_KEY
const axios = require('axios');
const response = await axios.delete(
`https://api.enablex.io/voice/v1/call/${voiceId}`,
{
auth: {
username: 'YOUR_APP_ID',
password: 'YOUR_APP_KEY'
}
}
);
console.log(response.data);
import requests
from requests.auth import HTTPBasicAuth
response = requests.delete(
f'https://api.enablex.io/voice/v1/call/{voice_id}',
auth=HTTPBasicAuth('YOUR_APP_ID', 'YOUR_APP_KEY')
)
print(response.json())
<?php
$ch = curl_init("https://api.enablex.io/voice/v1/call/{$voiceId}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_USERPWD, 'YOUR_APP_ID:YOUR_APP_KEY');
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Response
{
"voice_id": "f1aa71c0-8f2a-4fe8-b5ef-9a330454ef58",
"state": "success",
"from": "CLI number",
"to": "Destination number",
"timestamp": "2020-02-16T10:52:00Z"
}
Disconnect Webhook Event
After the call is terminated, EnableX sends a disconnected event to your webhook. This event includes the reason for disconnection and, if the call was recorded, the URL and duration of the recording file:
{
"voice_id": "f1aa71c0-8f2a-4fe8-b5ef-9a330454ef58",
"state": "disconnected",
"from": "CLI number",
"to": "Destination number",
"timestamp": "2020-02-16T10:52:00Z",
"disconnect_reason": "Normal Clearing",
"recording_url": "https://domain/sample.wav",
"recording_duration": 50
}
| Field | Description |
|---|---|
disconnect_reason |
Why the call ended. Possible values: Normal Clearing (call ended cleanly), User Busy (destination was busy). |
recording_url |
URL to the recording file, if the call was recorded. Only present when a recording exists. |
recording_duration |
Duration of the recording in seconds. Only present when a recording exists. |
DTMF Events
DTMF (Dual-Tone Multi-Frequency) events are asynchronous notifications sent to your webhook when a caller presses a key on their phone keypad during a bridged call. These are different from the digitcollected playstate event — that event is specific to the IVR play flow where DTMF collection is explicitly enabled. DTMF events described here fire independently, at any time after the call has been bridged, whenever a key is pressed.
To receive DTMF events, your application must provide a webhook URL in the call setup. No additional configuration or API call is needed — EnableX sends the event automatically whenever a digit is pressed on a bridged call.
Webhook Event
{
"voice_Id": "f1aa71c0-8f2a-4fe8-b5ef-9a330454ef58",
"state": "dtmfcollected",
"digit": 12345,
"timestamp": "2017-02-16T10:52:00Z"
}
| Field | Description |
|---|---|
voice_Id |
The unique identifier for the call on which the DTMF event occurred. Note the capital "I" in voice_Id. |
state |
Always "dtmfcollected" for this event type. |
digit |
The DTMF digit or sequence of digits pressed by the caller. |
timestamp |
UTC timestamp of when the DTMF input was received. |
DTMF events are only sent after a call has been bridged. If you need to collect digit input before bridging — for example, in an IVR menu — use the Play Voice Prompt endpoint with dtmf: true instead, which returns a digitcollected playstate event.
Speech Recognition Events
When speech recognition is enabled on a play prompt (by setting asr: true in the play request), EnableX captures the caller's spoken response, processes it through the speech recognition engine, and sends the result to your webhook as an asynchronous event. You use this for natural-language IVR flows where callers speak their responses rather than pressing keys.
Recognized text can be a single word or a full sentence. Accuracy depends on factors such as audio quality, background noise, the clarity of the speaker's voice, and the language setting used.
Webhook Event
{
"voice_id": "6b211e69-c2cc-4a65-99e9-f7a3c6922796",
"state": "recognized",
"from": "CLI number",
"to": "Destination number",
"text": "No, I'm not available.",
"reason": "Recognized",
"playstate": "speech_recognized",
"timestamp": "2017-02-16T10:52:00Z",
"prompt_ref": "welcome-prompt"
}
| Field | Description |
|---|---|
voice_id |
Identifier of the call on which the speech was recognized. |
state |
Outcome of the recognition attempt. Values: recognized (speech detected and transcribed), timeout (caller did not speak within the timeout window), unrecognized (speech was detected but could not be transcribed). |
text |
The recognized text. Only present when state is "recognized". |
reason |
A human-readable reason string from the recognition engine. |
playstate |
Always "speech_recognized" for this event type. |
prompt_ref |
The prompt_ref value you passed in the original play request, echoed back for correlation. |
timestamp |
UTC timestamp of when the recognition result was produced. |
TTS Advanced Options
When constructing the action_on_connect.play object in an outbound call request or the body of a play prompt request, you can include a text_to_speech_option object to exercise fine-grained control over how the TTS engine renders the speech. These options let you add emotion, control pacing and pitch, insert pauses, and instruct the engine on how to interpret special content such as numbers, dates, and phone numbers.
express-as: Emotion Styles
The express-as option lets you specify an emotional speaking style for the synthesized voice. The supported style values are:
advertisement_upbeat— Energetic, promotional tone suited for marketing messages.Cheerful— Positive and upbeat delivery.customerservice— Warm and helpful, suited for support interactions.Friendly— Casual and approachable tone.gentle— Soft and calm delivery, suited for sensitive or reassuring contexts.
break: Pause Duration
The break option inserts a silence at a specific point in the synthesized speech stream. This is useful for adding natural pauses between sentences or clauses. Pause durations must always be below 5000 microseconds.
prosody: Pitch, Rate, and Volume
The prosody option controls the acoustic properties of the synthesized voice. It can contain text content as well as nested break and say-as elements. The available attributes are:
- pitch — Sets the baseline pitch of the voice. Adjust this to make the voice sound higher or lower than its default.
- rate — Controls the speaking rate (speed). Increase to speak faster, decrease to speak slower.
- volume — Controls the loudness. Accepts a percentage modifier such as
+50%, or one of the named constants:silent,x-soft,soft,medium,loud,x-loud,default.
say-as: Interpreting Special Content
The say-as option tells the TTS engine how to interpret and speak a specific piece of text. This is important for content like phone numbers, dates, and currency, which have specific spoken forms that differ from their written form. Use the interpret-as attribute to specify the interpretation mode:
| interpret-as value | How the text is spoken |
|---|---|
characters / spell-out |
Each character is spoken as an individual letter. Example: "API" is spoken as "A P I". |
cardinal / number |
Spoken as a cardinal (counting) number. Example: "42" is spoken as "forty-two". |
ordinal |
Spoken as an ordinal number. Example: "3" is spoken as "third". |
number_digit |
Each digit is spoken individually. Example: "123" is spoken as "one two three". |
fraction |
Spoken as a fractional number. Example: "3/4" is spoken as "three quarters". |
date |
Spoken as a date. The format attribute specifies the input format: dmy, mdy, ymd, and similar variants. |
time |
Spoken as a time value. Format options: hms12 (12-hour clock) or hms24 (24-hour clock). |
duration |
Spoken as a duration. Format options: hms (hours, minutes, seconds), hm (hours and minutes), ms (minutes and seconds). |
telephone |
Spoken as a telephone number, with appropriate pausing between groups of digits. |
currency |
Spoken as a monetary amount, including the currency unit. |
address |
Spoken as a postal address. |
name |
Spoken as a person's name, with name-appropriate pronunciation handling. |
App-to-Voice Token
The App-to-Voice Token endpoint is specifically for browser-based applications and the EnableX Voice Bot SDK or Voice Bot UI Kit. When you build a web client that connects directly to the EnableX Voice Server — for example, a click-to-call widget or a voice bot interface — you cannot embed your App Key in the browser-side code where it would be publicly visible.
The solution is a two-step flow: your backend calls this endpoint using its server-side App credentials to obtain a short-lived JWT token, then passes that token to your browser client. The client uses the token to authenticate its WebRTC session with the EnableX Voice Server. Your App Key never reaches the browser.
Token issuance is restricted to domains you have explicitly whitelisted in your EnableX project settings. This prevents unauthorized domains from requesting tokens on behalf of your project.
POST https://api.enablex.io/voice/v1/webclient/token
Authorization: Basic base64(APP_ID:APP_KEY)
Content-Type: application/json
Request
The only required field is domain — the domain your Voice Bot SDK web client is hosted on. It must exactly match one of the domains you have whitelisted in your Voice project configuration in the EnableX Portal.
curl -X POST https://api.enablex.io/voice/v1/webclient/token \
-u YOUR_APP_ID:YOUR_APP_KEY \
-H "Content-Type: application/json" \
-d '{"domain": "yourdomain.com"}'
const axios = require('axios');
const response = await axios.post(
'https://api.enablex.io/voice/v1/webclient/token',
{ domain: 'yourdomain.com' },
{
auth: {
username: 'YOUR_APP_ID',
password: 'YOUR_APP_KEY'
}
}
);
// Pass response.data.token to your Voice Bot SDK client
console.log(response.data.token);
import requests
from requests.auth import HTTPBasicAuth
response = requests.post(
'https://api.enablex.io/voice/v1/webclient/token',
auth=HTTPBasicAuth('YOUR_APP_ID', 'YOUR_APP_KEY'),
json={'domain': 'yourdomain.com'}
)
# Pass the token to your Voice Bot SDK client
print(response.json()['token'])
<?php
$payload = json_encode(['domain' => 'yourdomain.com']);
$ch = curl_init('https://api.enablex.io/voice/v1/webclient/token');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_USERPWD, 'YOUR_APP_ID:YOUR_APP_KEY');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
// Pass $result['token'] to your Voice Bot SDK client
echo $result['token'];
Success Response
On success, the endpoint returns a JWT token string. Pass this token to the Voice Bot SDK or Voice Bot UI Kit to establish the client's WebRTC session with the EnableX Voice Server:
{
"token": "Token_Value"
}
Error Responses
Invalid project or domain not whitelisted — the platform returns HTTP 405:
{
"result": 405,
"msg": "Method Not Allowed"
}
Token generation failed — credentials are valid but the token could not be issued internally. The token field is returned as null:
{
"token": null
}
If you receive "result": 405, verify that the domain in your request is listed in your project's whitelisted domains in the Portal and that your App ID and App Key are correct. If you receive "token": null, the platform encountered an internal issue — contact EnableX support.
Tokens are only issued for domains explicitly whitelisted in your EnableX Portal Voice project settings. Add your production and local development domains (e.g. yourdomain.com, localhost) to the allowlist before testing. Requests from unlisted domains will be rejected with a 405 error.