curl --request GET \
--url https://api.contourvoice.com/v1/calls/{call_id} \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.contourvoice.com/v1/calls/{call_id}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.contourvoice.com/v1/calls/{call_id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.contourvoice.com/v1/calls/{call_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.contourvoice.com/v1/calls/{call_id}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.contourvoice.com/v1/calls/{call_id}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.contourvoice.com/v1/calls/{call_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"agent_id": "3f1c1b2e-6d1a-4c0e-9b2f-1a2b3c4d5e6f",
"analysis": {
"extracted_data": {
"appointment_confirmed": true
},
"summary": "Jane confirmed her appointment for the 15th."
},
"call_id": "9d2f7a3c-8e4b-4f1d-a6c7-2b3c4d5e6f70",
"direction": "outbound",
"disconnection_reason": "agent_hangup",
"disposition": "answered",
"duration_ms": 84000,
"ended_at": "2026-09-10T14:04:35+00:00",
"from_number": "+15555550199",
"metadata": {
"your_order_id": "ORD-8812"
},
"recording_url": "https://storage.googleapis.com/.../recording.mp3?X-Goog-Signature=...",
"source": "api",
"started_at": "2026-09-10T14:03:11+00:00",
"status": "ended",
"to_number": "+15555550100",
"variables": {
"patient_name": "Jane Doe"
}
}{
"detail": "Invalid or expired API key"
}{
"detail": "Call not found."
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"ctx": {},
"input": "<unknown>"
}
]
}Get a call
The call object as of now.
This is the same object every webhook carries, so you can poll it instead
of, or in addition to, receiving events. analysis is null until the call
has been analyzed. The transcript is never included.
curl --request GET \
--url https://api.contourvoice.com/v1/calls/{call_id} \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.contourvoice.com/v1/calls/{call_id}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.contourvoice.com/v1/calls/{call_id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.contourvoice.com/v1/calls/{call_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.contourvoice.com/v1/calls/{call_id}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.contourvoice.com/v1/calls/{call_id}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.contourvoice.com/v1/calls/{call_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"agent_id": "3f1c1b2e-6d1a-4c0e-9b2f-1a2b3c4d5e6f",
"analysis": {
"extracted_data": {
"appointment_confirmed": true
},
"summary": "Jane confirmed her appointment for the 15th."
},
"call_id": "9d2f7a3c-8e4b-4f1d-a6c7-2b3c4d5e6f70",
"direction": "outbound",
"disconnection_reason": "agent_hangup",
"disposition": "answered",
"duration_ms": 84000,
"ended_at": "2026-09-10T14:04:35+00:00",
"from_number": "+15555550199",
"metadata": {
"your_order_id": "ORD-8812"
},
"recording_url": "https://storage.googleapis.com/.../recording.mp3?X-Goog-Signature=...",
"source": "api",
"started_at": "2026-09-10T14:03:11+00:00",
"status": "ended",
"to_number": "+15555550100",
"variables": {
"patient_name": "Jane Doe"
}
}{
"detail": "Invalid or expired API key"
}{
"detail": "Call not found."
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"ctx": {},
"input": "<unknown>"
}
]
}Authorizations
Your organization's API key (sk_cont_...), sent as Authorization: Bearer sk_cont_.... Keep it server-side.
Path Parameters
Response
Successful Response
The call object in every event and in GET /v1/calls/{id}.
Stable id for the life of the call.
outbound or inbound.
queued → in_progress → ended, or failed if the call never went out.
The agent that handled the call.
Null until call.analyzed. Then summary (text) and extracted_data (the fields configured on your agent).
Why the call ended, for example agent_hangup, user_hangup, voicemail, no_answer, max_duration.
What happened on the wire: answered, answering_machine, no_answer, busy, rejected, unreachable, invalid_number, carrier_failure. Null until the call ends.
Call length in milliseconds.
ISO 8601 timestamp. Null until the call ends.
The caller ID.
Your metadata, echoed back untouched.
Presigned link to the recording. Expires after about an hour; null until the recording is available. Use GET /v1/calls/{call_id}/recording for a fresh one.
api for calls you created. Other values mean the call was started from the Contour dashboard or another channel.
ISO 8601 timestamp.
The number dialed.
The variables the call was created with.