Call Control
This page documents the Call Control endpoint.
For shared REST API environment and authentication requirements, see REST API Overview.
Note
- Click-to-dial can only be initiated by the user that owns the specified user extension. If the authenticated user does not own the extension, a
403 Forbiddenresponse will be returned. - Hangup can only be performed by an active call participant. If the authenticated user is not a participant in the specified call leg, a
403 Forbiddenresponse will be returned.
Endpoints
Call Control
Base path: /v3/call_control/
Rate limit scope: "call-control"
Click to Dial
Initiates a call from the specified user extension to a destination number or extension.
How Click to Dial works
Click to Dial is a two-step process. When you trigger a call, the system does not immediately dial the destination. Instead:
- You click "Call" next to a contact or phone number.
- The system first rings your own phone — you will see a message like "(pick up to call)".
- You pick up your phone.
- Only after you answer, the system automatically dials the destination number and connects you.
POST /v3/call_control/
curl --include --request POST \
--header "Content-Type: application/json" \
--header "Authorization: Bearer {token}" \
-d '{"command": "click-to-dial", "user": {"tenant_id": "b11d749b-8eb7-4236-a068-3d94ba3860d6", "user_extension": "210"}, "payload": {"destination": "200"}}' \
https://api-eu-central-1.phoneserver.dev/v3/call_control/
import requests
import json
# Prepare headers
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer {token}"
}
# Prepare data
myDict = {
"command": "click-to-dial",
"user": {
"tenant_id": "b11d749b-8eb7-4236-a068-3d94ba3860d6",
"user_extension": "210"
},
"payload": {
"destination": "200"
}
}
# Send the request
response = requests.post('https://api-eu-central-1.phoneserver.dev/v3/call_control/', headers=headers, json=json.dumps(myDict))
<?php
// Create a new cURL resource
$ch = curl_init();
// Prepare headers
$headers = array();
$headers[] = 'Content-Type: application/json';
$headers[] = 'Authorization: Bearer {token}';
// Set the header
curl_setopt($ch, CURL_HTTPHEADER, $headers);
// Indicate URL
curl_setopt($ch, CURLOPT_URL, 'https://api-eu-central-1.phoneserver.dev/v3/call_control/');
// Return the transfer as a string
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// Indicate the method
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
// Prepare data
$data = array(
"command" => "click-to-dial",
"user" => "{'tenant_id': 'b11d749b-8eb7-4236-a068-3d94ba3860d6', 'user_extension': '210'}",
"payload" => "{'destination': '200'}",
};
$fields = json_encode($data);
// Set the body
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
// Send the request
$result = curl_exec($ch);
curl_close($ch);
using (var httpClient = new HttpClient())
{
// Prepare request
using (var request = new HttpRequestMessage(new HttpMethod("POST"), "https://api-eu-central-1.phoneserver.dev/v3/call_control/"))
{
// Prepare headers
request.Headers.Add("Content-Type", "application/json");
request.Headers.Add("Authorization", "Bearer {token}");
// Prepare body
request.content = JsonContent.Create(new {
command = "click-to-dial"
user = "{'tenant_id': 'b11d749b-8eb7-4236-a068-3d94ba3860d6', 'user_extension': '210'}"
payload = "{'destination': '200'}"
});
// Send response
var response = await httpClient.SendAsync(request);
}
}
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;
// Prepare body
String jsonString = "{\"command\": \"click-to-dial\", \"user\": {\"tenant_id\": \"b11d749b-8eb7-4236-a068-3d94ba3860d6\", \"user_extension\": \"210\"}, \"payload\": {\"destination\": \"200\"}}";
Client client = ClientBuilder.newClient();
Response response = client.target("https://api-eu-central-1.phoneserver.dev/v3/call_control/")
.request(MediaType.APPLICATION_JSON_TYPE)
.header("Content-Type", "application/json")
.header("Authorization", "Bearer {token}")
.post(Entity.json(jsonString));
const Http = new XMLHttpRequest();
Http.open("POST", "https://api-eu-central-1.phoneserver.dev/v3/call_control/");
// Prepare headers
Http.setRequestHeader("Content-Type", "application/json");
Http.setRequestHeader("Authorization", "Bearer {token}");
// Prepare data
const json = {
"command": "click-to-dial",
"user": {
"tenant_id": "b11d749b-8eb7-4236-a068-3d94ba3860d6",
"user_extension": "210"
},
"payload": {
"destination": "200"
}
}
// Send the request
Http.send(JSON.stringify(json));
// Handle the response
Http.onreadystatechange = (e) => {
console.log(Http.responseText)
}
Request body
| Field | Type | Required | Description |
|---|---|---|---|
command |
string |
Yes | Must be "click-to-dial". |
user.tenant_id |
string |
Yes | The tenant ID of the user initiating the call. |
user.user_extension |
string |
Yes | The extension of the user initiating the call. |
payload.destination |
string |
Yes | The destination to call. Can be an extension (e.g. "200") or a phone number (e.g. "+32470123456"). |
Example
Using values from Example 4 — Answered, Internal Call in the Webhooks documentation, extension 210 (Jane Doe) initiates a click-to-dial to extension 200 (John Doe):
{
"command": "click-to-dial",
"user": {
"tenant_id": "b11d749b-8eb7-4236-a068-3d94ba3860d6",
"user_extension": "210"
},
"payload": {
"destination": "200"
}
}
This call will produce the call events documented in Example 4, where each event contains an id field that serves as the call_leg_id for the Hangup command below.
Error responses
403 Forbidden when the authenticated user does not own the specified user extension.
Hangup
Terminates an active call leg for the specified user.
POST /v3/call_control/
curl --include --request POST \
--header "Content-Type: application/json" \
--header "Authorization: Bearer {token}" \
-d '{"command": "hangup", "user": {"tenant_id": "b11d749b-8eb7-4236-a068-3d94ba3860d6", "user_extension": "210"}, "payload": {"call_leg_id": "b6b87eb5-04e4-4838-81b6-fe6bfa6936bf"}}' \
https://api-eu-central-1.phoneserver.dev/v3/call_control/
import requests
import json
# Prepare headers
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer {token}"
}
# Prepare data
myDict = {
"command": "hangup",
"user": {
"tenant_id": "b11d749b-8eb7-4236-a068-3d94ba3860d6",
"user_extension": "210"
},
"payload": {
"call_leg_id": "b6b87eb5-04e4-4838-81b6-fe6bfa6936bf"
}
}
# Send the request
response = requests.post('https://api-eu-central-1.phoneserver.dev/v3/call_control/', headers=headers, json=json.dumps(myDict))
<?php
// Create a new cURL resource
$ch = curl_init();
// Prepare headers
$headers = array();
$headers[] = 'Content-Type: application/json';
$headers[] = 'Authorization: Bearer {token}';
// Set the header
curl_setopt($ch, CURL_HTTPHEADER, $headers);
// Indicate URL
curl_setopt($ch, CURLOPT_URL, 'https://api-eu-central-1.phoneserver.dev/v3/call_control/');
// Return the transfer as a string
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// Indicate the method
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
// Prepare data
$data = array(
"command" => "hangup",
"user" => "{'tenant_id': 'b11d749b-8eb7-4236-a068-3d94ba3860d6', 'user_extension': '210'}",
"payload" => "{'call_leg_id': 'b6b87eb5-04e4-4838-81b6-fe6bfa6936bf'}",
};
$fields = json_encode($data);
// Set the body
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
// Send the request
$result = curl_exec($ch);
curl_close($ch);
using (var httpClient = new HttpClient())
{
// Prepare request
using (var request = new HttpRequestMessage(new HttpMethod("POST"), "https://api-eu-central-1.phoneserver.dev/v3/call_control/"))
{
// Prepare headers
request.Headers.Add("Content-Type", "application/json");
request.Headers.Add("Authorization", "Bearer {token}");
// Prepare body
request.content = JsonContent.Create(new {
command = "hangup"
user = "{'tenant_id': 'b11d749b-8eb7-4236-a068-3d94ba3860d6', 'user_extension': '210'}"
payload = "{'call_leg_id': 'b6b87eb5-04e4-4838-81b6-fe6bfa6936bf'}"
});
// Send response
var response = await httpClient.SendAsync(request);
}
}
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;
// Prepare body
String jsonString = "{\"command\": \"hangup\", \"user\": {\"tenant_id\": \"b11d749b-8eb7-4236-a068-3d94ba3860d6\", \"user_extension\": \"210\"}, \"payload\": {\"call_leg_id\": \"b6b87eb5-04e4-4838-81b6-fe6bfa6936bf\"}}";
Client client = ClientBuilder.newClient();
Response response = client.target("https://api-eu-central-1.phoneserver.dev/v3/call_control/")
.request(MediaType.APPLICATION_JSON_TYPE)
.header("Content-Type", "application/json")
.header("Authorization", "Bearer {token}")
.post(Entity.json(jsonString));
const Http = new XMLHttpRequest();
Http.open("POST", "https://api-eu-central-1.phoneserver.dev/v3/call_control/");
// Prepare headers
Http.setRequestHeader("Content-Type", "application/json");
Http.setRequestHeader("Authorization", "Bearer {token}");
// Prepare data
const json = {
"command": "hangup",
"user": {
"tenant_id": "b11d749b-8eb7-4236-a068-3d94ba3860d6",
"user_extension": "210"
},
"payload": {
"call_leg_id": "b6b87eb5-04e4-4838-81b6-fe6bfa6936bf"
}
}
// Send the request
Http.send(JSON.stringify(json));
// Handle the response
Http.onreadystatechange = (e) => {
console.log(Http.responseText)
}
Request body
| Field | Type | Required | Description |
|---|---|---|---|
command |
string |
Yes | Must be "hangup". |
user.tenant_id |
string |
Yes | The tenant ID of the user performing the hangup. |
user.user_extension |
string |
Yes | The extension of the active call participant. |
payload.call_leg_id |
string |
Yes | The call leg ID to hang up. This is the same as the id field in call events. |
Example
Using values from Example 4 — Answered, Internal Call, extension 210 (Jane Doe) hangs up the leg (id: b6b87eb5-04e4-4838-81b6-fe6bfa6936bf):
{
"command": "hangup",
"user": {
"tenant_id": "b11d749b-8eb7-4236-a068-3d94ba3860d6",
"user_extension": "210"
},
"payload": {
"call_leg_id": "b6b87eb5-04e4-4838-81b6-fe6bfa6936bf"
}
}
Tip
The call_leg_id corresponds to the id field in call events. For example, in Example 4, extension 210 has call leg ID b6b87eb5-04e4-4838-81b6-fe6bfa6936bf and extension 200 has call leg ID e8461f72-e479-44bd-a312-c51447925a96. Either participant can hang up by using their own call leg.
Error responses
403 Forbidden when the authenticated user is not an active participant in the call.