curl --request GET \
--url https://{host}/api/v1/cdrs/{uuid} \
--header 'Authorization: Bearer <token>'const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://{host}/api/v1/cdrs/{uuid}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://{host}/api/v1/cdrs/{uuid}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text){
"billing_mode": "rated",
"billsec": 123,
"call_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"direction": "inbound",
"started_at": "2023-11-07T05:31:56Z",
"transcoded": true,
"transport": "sip",
"agent_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"ai_tokens": 123,
"answered_at": "2023-11-07T05:31:56Z",
"bridge_cause": "<string>",
"caller_number": "<string>",
"campaign_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"client_trunk_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"codec_a": "<string>",
"codec_b": "<string>",
"contact_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"cost_amount": "0.062000",
"cost_amount_original": "0.024000",
"cost_currency": "<string>",
"cost_unconverted": true,
"currency": "<string>",
"destination_number": "<string>",
"dialled_number": "<string>",
"ended_at": "2023-11-07T05:31:56Z",
"fs_node_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"fx_rate": "83.20000000",
"hangup_cause": "<string>",
"jitter_ms": 123,
"loss_pct": 123,
"margin": "0.062000",
"mean_interval_ms": 123,
"media_verdict": "ok",
"mos": 123,
"quality_pct": 123,
"sell_amount": "0.062000",
"sip_status": 123,
"team_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}Get one call
One CDR, including the contact it was with. Cost and margin are withheld unless the caller holds billing:margin, exactly as in the list.
curl --request GET \
--url https://{host}/api/v1/cdrs/{uuid} \
--header 'Authorization: Bearer <token>'const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://{host}/api/v1/cdrs/{uuid}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://{host}/api/v1/cdrs/{uuid}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text){
"billing_mode": "rated",
"billsec": 123,
"call_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"direction": "inbound",
"started_at": "2023-11-07T05:31:56Z",
"transcoded": true,
"transport": "sip",
"agent_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"ai_tokens": 123,
"answered_at": "2023-11-07T05:31:56Z",
"bridge_cause": "<string>",
"caller_number": "<string>",
"campaign_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"client_trunk_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"codec_a": "<string>",
"codec_b": "<string>",
"contact_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"cost_amount": "0.062000",
"cost_amount_original": "0.024000",
"cost_currency": "<string>",
"cost_unconverted": true,
"currency": "<string>",
"destination_number": "<string>",
"dialled_number": "<string>",
"ended_at": "2023-11-07T05:31:56Z",
"fs_node_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"fx_rate": "83.20000000",
"hangup_cause": "<string>",
"jitter_ms": 123,
"loss_pct": 123,
"margin": "0.062000",
"mean_interval_ms": 123,
"media_verdict": "ok",
"mos": 123,
"quality_pct": 123,
"sell_amount": "0.062000",
"sip_status": 123,
"team_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}{
"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.
Path Parameters
Response
The call. Margin fields are withheld unless the caller holds margin:read.
rated, free inbound, outbound, internal codec_a differs from codec_b. A rising transcode ratio is the leading indicator of a capacity problem: it presents as 'the switch feels slow' long before anything errors.
sip, whatsapp Input plus output tokens for the AI that handled this call; absent when no AI did. Read from the conversation rather than the charge, because a charge row exists only where settlement found something to price -- sourcing it from the charge would report "no AI" for a call an AI demonstrably answered. Audio is not added: it is the audio portion of the input, and adding it double-counts.
The far end's hangup cause for the last bridge attempt, e.g. a carrier's CALL_REJECTED. hangup_cause is this leg's own. Absent when there was no bridge or no answer.
The campaign that placed this call, when one did.
The client trunk the call came in on, when a client's own PBX sent it (the Client trunks module). Absent on every other call. Filter on it with GET /cdrs?client_trunk_id=. Since 2026-09-30.
The customer this call was with.
Exact decimal amount as a string. Never a JSON number: rates are fractions of a cent and binary floating point cannot represent them exactly.
"0.062000"
What the carrier charged, in cost_currency, before conversion. cost_amount is this multiplied by fx_rate. Omitted without margin:read.
"0.024000"
The currency the CARRIER charged in. Omitted without margin:read.
True when no exchange rate existed for the pair: cost_amount is then the carrier's RAW figure and margin is a subtraction across two currencies, which means nothing. Present only when true, and only with margin:read.
ISO 4217 code that sell_amount, cost_amount and margin are all in: the call's own currency, which is its sell rate's. Absent on calls recorded before it was written.
What the caller keyed in, when routing read it as a different number (919216217231 routed as +919216217231). Absent when it was dialled as routed.
The switch node that carried this call. Absent on calls recorded before nodes were written to the record, and on a call the control plane could not attribute to one.
The exchange rate in force when the call was settled: one unit of cost_currency in currency. Stored on the call, so a margin does not move when a later rate is entered. Omitted without margin:read.
"83.20000000"
Peak inbound jitter in milliseconds. If every leg of every call shows it at once, the cause is the node being scheduled late rather than any one network.
Percent of inbound packets that never arrived. ABSENT, not zero, when too little audio arrived to judge: FreeSWITCH scores a leg that received nothing as quality 100 and MOS 4.50, and zero is a measurement this leg does not have.
Exact decimal amount as a string. Never a JSON number: rates are fractions of a cent and binary floating point cannot represent them exactly.
"0.062000"
Mean gap between arriving packets. About 20 on a healthy 20 ms stream; double the negotiated ptime is a ptime mismatch.
What the RTP layer said about the audio that ARRIVED on the caller's leg. The codec and the duration both look healthy on a call nobody could understand; this does not. lossy_inbound and jittery_inbound are statistics of what reached the switch, so the damage was done before it got here. Absent for a leg with no RTP session.
ok, unanswered, no_inbound_media, no_outbound_media, wrong_advertised_address, lossy_inbound, jittery_inbound, unpaced_sender, ptime_mismatch, degraded_audio Derived from quality_pct alone, so it is the same measurement on a different scale and not an independent opinion.
FreeSWITCH's own (received - flaws) / received.
Exact decimal amount as a string. Never a JSON number: rates are fractions of a cent and binary floating point cannot represent them exactly.
"0.062000"
The far end's final SIP response for the last bridge attempt: 403, 503, 486. Absent when none was received.