curl --request POST \
--url https://{host}/api/v1/calls \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"callback_url": "<string>",
"destination": "0255551234",
"extension": "1001",
"from": {
"agent_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"extension": "<string>",
"user_email": "<string>"
},
"organisation_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"reference": "<string>",
"to": "<string>"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
callback_url: '<string>',
destination: '0255551234',
extension: '1001',
from: {
agent_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
extension: '<string>',
user_email: '<string>'
},
organisation_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
reference: '<string>',
to: '<string>'
})
};
fetch('https://{host}/api/v1/calls', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://{host}/api/v1/calls"
payload = {
"callback_url": "<string>",
"destination": "0255551234",
"extension": "1001",
"from": {
"agent_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"extension": "<string>",
"user_email": "<string>"
},
"organisation_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"reference": "<string>",
"to": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"call_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"reference": "<string>",
"request_id": "<string>"
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}Click to call
Answers once the agent has picked up (up to 30 seconds); ?dry_run=true checks instead — the extension has a phone registered to ring, the number resolves, an outside number has a route and credit — and returns the plan, placing nothing. Rings the agent first, then dials the destination when they answer. Dialling the destination first would leave the called party waiting in silence, and if no agent answered they were called for nothing.\n\nThe answered leg enters the ordinary router, so trunk selection and the billing gate apply exactly as to a handset dialling out.
curl --request POST \
--url https://{host}/api/v1/calls \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"callback_url": "<string>",
"destination": "0255551234",
"extension": "1001",
"from": {
"agent_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"extension": "<string>",
"user_email": "<string>"
},
"organisation_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"reference": "<string>",
"to": "<string>"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
callback_url: '<string>',
destination: '0255551234',
extension: '1001',
from: {
agent_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
extension: '<string>',
user_email: '<string>'
},
organisation_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
reference: '<string>',
to: '<string>'
})
};
fetch('https://{host}/api/v1/calls', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://{host}/api/v1/calls"
payload = {
"callback_url": "<string>",
"destination": "0255551234",
"extension": "1001",
"from": {
"agent_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"extension": "<string>",
"user_email": "<string>"
},
"organisation_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"reference": "<string>",
"to": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"call_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"reference": "<string>",
"request_id": "<string>"
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}Authorizations
Every request sends Authorization: Bearer <token>. The token is either a panel session (a JWT from /auth/login, 12 hours) or an API key ft_<id>_<secret>. An API key is accepted only from an address on its IP allowlist (403 ip_not_allowed otherwise; 403 ip_allowlist_required for an old key that has none), is limited to its rate per minute (429 rate_limited with Retry-After; X-RateLimit-Limit/Remaining/Reset on every response), and at most 60 call placements a minute.
Headers
Any string up to 255 characters. A retry with the same key and the same body gets the first answer again (with Idempotent-Replayed: true) instead of doing it twice; the same key with a different body is 409 idempotency_mismatch; while the first is still running, 409 idempotency_in_progress. Kept 24 hours.
255Query Parameters
true: check everything, place nothing, and return the plan.
Body
Who rings first (extension, or from) and the number to call (destination, or to).
Where this call's events are POSTed, signed with the organisation's callback secret (see /integration/callback-secret): call.started, call.answered, call.ended, recording.ready, and call.failed if it never connected.
"0255551234"
"1001"
Who rings first, by one of: extension number, agent id, or the login email of a person linked to an agent.
Show child attributes
Show child attributes
Optional when your scope has exactly one organisation.
Your own id for this call. On every event and callback for it, and on its CDR.
128The number to call; the same as destination.