Call Logs
Get Call Detail Records (CDR)
Retrieve telephony call detail records with filtering and pagination support
GET
/
api
/
v2
/
platform
/
cdr
/
Get Call Detail Records
curl --request GET \
--url https://unpod.ai/api/v2/platform/cdr/ \
--header 'Authorization: <api-key>' \
--header 'Org-Handle: <org-handle>'import requests
url = "https://unpod.ai/api/v2/platform/cdr/"
headers = {
"Org-Handle": "<org-handle>",
"Authorization": "<api-key>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {'Org-Handle': '<org-handle>', Authorization: '<api-key>'}
};
fetch('https://unpod.ai/api/v2/platform/cdr/', 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://unpod.ai/api/v2/platform/cdr/",
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: <api-key>",
"Org-Handle: <org-handle>"
],
]);
$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://unpod.ai/api/v2/platform/cdr/"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Org-Handle", "<org-handle>")
req.Header.Add("Authorization", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://unpod.ai/api/v2/platform/cdr/")
.header("Org-Handle", "<org-handle>")
.header("Authorization", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://unpod.ai/api/v2/platform/cdr/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Org-Handle"] = '<org-handle>'
request["Authorization"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"count": 643,
"status_code": 200,
"message": "Call logs fetched successfully",
"data": [
{
"id": 33364,
"call_status": "completed",
"end_reason": "call.in-progress.sip-completed-call",
"call_type": "outbound",
"bridge": { "id": 12, "name": "Acme Primary Bridge" },
"creation_time": "2025-11-08T05:32:29Z",
"start_time": "2025-11-08T05:29:43Z",
"end_time": "2025-11-08T05:32:28.686639Z",
"call_duration": 165.686639,
"source_number": "+15551234567",
"destination_number": "+15559876543",
"failure_source": null,
"sip_cause": null
}
]
}
{
"status_code": 401,
"message": "Authentication credentials were not provided."
}
{
"count": 643,
"status_code": 200,
"message": "Call logs fetched successfully",
"data": [
{
"id": 33364,
"call_status": "completed",
"end_reason": "call.in-progress.sip-completed-call",
"call_type": "outbound",
"bridge": { "id": 12, "name": "Acme Primary Bridge" },
"creation_time": "2025-11-08T05:32:29Z",
"start_time": "2025-11-08T05:29:43Z",
"end_time": "2025-11-08T05:32:28.686639Z",
"call_duration": 165.686639,
"source_number": "+15551234567",
"destination_number": "+15559876543",
"failure_source": null,
"sip_cause": null
}
]
}
{
"status_code": 401,
"message": "Authentication credentials were not provided."
}
Get Call Detail Records (CDR)
Retrieve telephony call detail records (CDR) for your organization - only SIP-based telephony calls, not voice agent calls.Prerequisites: Make sure you have your API Token and Org-Handle ready. See Authentication for details.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| Org-Handle | string | Yes | Organization domain handle |
| Authorization | string | Yes | API Key format: Token <token> |
You can get the
Org-Handle by hitting the Get All Organizations API. The domain_handle field in the response is your Org-Handle.Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| page | integer | No | Page number for pagination (default = 1) |
| page_size | integer | No | Number of records per page (default = 20) |
| call_type | string | No | Filter by call direction: inbound or outbound |
| call_status | string | No | Filter by status: completed, notConnected, or failed |
Response Fields
| Field | Type | Description |
|---|---|---|
| count | integer | Total number of CDRs |
| status_code | integer | HTTP status code |
| message | string | Response message |
| data | array | Array of CDR objects |
CDR Object Fields
| Field | Type | Description |
|---|---|---|
| id | integer | Unique call record ID |
| source_number | string | The number that initiated the call |
| destination_number | string | The number that received the call |
| call_type | string | Direction of call: outbound, inbound |
| call_status | string | Outcome: completed, notConnected, failed |
| bridge | object | { id, name } of the bridge, or null |
| creation_time | string | Timestamp when the call was created/scheduled |
| start_time | string | Timestamp when the call was answered |
| end_time | string | Timestamp when the call ended |
| call_duration | number | Duration of the call in seconds |
| end_reason | string | Reason the call ended |
| failure_source | string | Which side a failure originated on (null if none) |
| sip_cause | string | Human-readable SIP cause (null if none) |
This CDR surface returns telephony SIP records only - it intentionally omits
agent / space / organization attribution (voice-agent call logs live elsewhere).
Common Error Codes
| Status Code | Description |
|---|---|
| 200 | Success - CDRs fetched successfully |
| 400 | Bad Request - Invalid filter parameters |
| 401 | Unauthorized - Invalid or missing API token |
| 403 | Forbidden - Invalid organization handle |
| 404 | Not Found - Organization not found |
| 500 | Internal Server Error - Server-side error |
Code Examples
const axios = require('axios');
const headers = {
'Authorization': 'Token your-api-token',
'Org-Handle': 'your-org-handle'
};
// Get CDRs with filters
const getCDRs = async (filters = {}) => {
const params = {
page: 1,
page_size: 20,
...filters
};
const response = await axios.get(
'https://unpod.ai/api/v2/platform/cdr/',
{ headers, params }
);
console.log(`Total CDRs: ${response.data.count}`);
response.data.data.forEach(log => {
console.log(`${log.call_type} call to ${log.destination_number}: ${log.call_status} (${log.call_duration}s)`);
});
return response.data.data;
};
// Example: Get outbound completed calls
getCDRs({
call_type: 'outbound',
call_status: 'completed'
});
import requests
headers = {
'Authorization': 'Token your-api-token',
'Org-Handle': 'your-org-handle'
}
def get_cdrs(page: int = 1, page_size: int = 20, **filters):
"""Get CDRs with optional filters"""
url = 'https://unpod.ai/api/v2/platform/cdr/'
params = {'page': page, 'page_size': page_size, **filters}
response = requests.get(url, headers=headers, params=params)
data = response.json()
print(f"Total CDRs: {data['count']}")
for log in data['data']:
print(f"{log['call_type']} to {log['destination_number']}: {log['call_status']} ({log['call_duration']}s)")
return data['data']
# Example: Get outbound completed calls
get_cdrs(
page=1,
page_size=20,
call_type='outbound',
call_status='completed'
)
# Get CDRs with filters
curl -X GET "https://unpod.ai/api/v2/platform/cdr/?page=1&page_size=20&call_type=outbound&call_status=completed" \
-H "Org-Handle: your-org-handle" \
-H "Authorization: Token your-api-token"
Best Practices
- Pagination: Use
pageandpage_sizefor large datasets to avoid timeouts - Call Status Filtering: Filter by
call_statusto focus on specific outcomes - Org-Handle: Ensure the correct organization handle is included
- Security: Keep API tokens secure and rotate them regularly
Authorizations
Format: Token
Headers
Organization domain handle
Example:
"unpod.tv"
Query Parameters
Example:
1
Example:
20
Available options:
inbound, outbound Example:
"outbound"
Available options:
completed, notConnected, failed Example:
"completed"
Response
List of telephony CDRs
Was this page helpful?
⌘I
Get Call Detail Records
curl --request GET \
--url https://unpod.ai/api/v2/platform/cdr/ \
--header 'Authorization: <api-key>' \
--header 'Org-Handle: <org-handle>'import requests
url = "https://unpod.ai/api/v2/platform/cdr/"
headers = {
"Org-Handle": "<org-handle>",
"Authorization": "<api-key>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {'Org-Handle': '<org-handle>', Authorization: '<api-key>'}
};
fetch('https://unpod.ai/api/v2/platform/cdr/', 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://unpod.ai/api/v2/platform/cdr/",
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: <api-key>",
"Org-Handle: <org-handle>"
],
]);
$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://unpod.ai/api/v2/platform/cdr/"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Org-Handle", "<org-handle>")
req.Header.Add("Authorization", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://unpod.ai/api/v2/platform/cdr/")
.header("Org-Handle", "<org-handle>")
.header("Authorization", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://unpod.ai/api/v2/platform/cdr/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Org-Handle"] = '<org-handle>'
request["Authorization"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"count": 643,
"status_code": 200,
"message": "Call logs fetched successfully",
"data": [
{
"id": 33364,
"call_status": "completed",
"end_reason": "call.in-progress.sip-completed-call",
"call_type": "outbound",
"bridge": { "id": 12, "name": "Acme Primary Bridge" },
"creation_time": "2025-11-08T05:32:29Z",
"start_time": "2025-11-08T05:29:43Z",
"end_time": "2025-11-08T05:32:28.686639Z",
"call_duration": 165.686639,
"source_number": "+15551234567",
"destination_number": "+15559876543",
"failure_source": null,
"sip_cause": null
}
]
}
{
"status_code": 401,
"message": "Authentication credentials were not provided."
}