This request returns a list of call detail records.
| URL | https://api.telecomx.dk/pbx/cdr | |
|---|---|---|
| Method | GET or POST | |
| Access level | PERSONAL if user received or made the call VIEWER, MANAGE, OWNER if calls belongs to the customer RESELLER if customer belongs to the reseller or ADMIN. |
|
| Query | offset | [optional] Index of the first item to return, default 0. |
| limit | [optional] The number of items to return, default 100, min 1, max 10000. |
|
| filter | [optional] Search query that will match against aNumber, aName, bNumber and bName. |
|
| customer | [optional] Customer the calls belongs to, defaults to users own customer. |
|
| employee | [optional] Employee to show calls for, overridden by extension. |
|
| extension | [optional] Extension to show calls for. |
|
| terminatedBy | [optional] Who terminated the call: A for caller, B for callee or S for system. |
|
| terminatedReason | [optional] How was the call terminated: CANCEL - no answer, cancelled by caller HANGUP - hangup after answered BUSY - callee was busy UNREACHABLE - callee could not be called ERROR - call ended due to an error BLOCKED - call blocked due to suspicion of fraud or blacklisted by user NOT_FOUND - called number does not exist FAILED - any of UNREACHABLE, ERROR, BLOCKED, NOT_FOUND. |
|
| from | [optional] Start date/time to include calls from, defaults to midnight today. |
|
| to | [optional] End date/time to include calls until, defaults to from + 24 hours. |
|
| direction | [optional] Direction of calls to include: IN or OUT. Required extension or that user has access level PERSONAL. |
|
| recording | [optional] True to also include if a call has been recorded. |
|
| answeredByCallee | [optional] True to only return calls that was answered by the callee (not by system/voicemail etc). |
|
| cdrNote | [optional] True to only return calls with a CDR note. |
|
| format | [optional] Data format: JSON, CSV, EXCEL. Defaults to JSON. |
|
| group | [optional] call returns one row per physical call, with the call's transfer legs nested in legs. leg (default) returns one row per call leg, as this endpoint has always done. Ignored for CSV and EXCEL, which are always ungrouped. See Call grouping below. |
|
| includeRtpStats | [optional] True to also return the raw per-leg RTP counters. ADMIN, or RESELLER_ADMIN with the TELE employee feature - ignored for everybody else. |
|
https://api.telecomx.dk/pbx/cdr https://api.telecomx.dk/pbx/cdr?customer=1234578901234567890ABCD&extension=12345678901234567890AAAA&terminatedReason=CANCEL&filter=203 https://api.telecomx.dk/pbx/cdr?from=2019-01-01T00:00:00.000Z&to=2019-01-02T00:00:00.000Z
| JSON object | ||
|---|---|---|
| offset | Number | Index of the first CDR returned. |
| limit | Number | Number of CDR records to return. Note that the actual number of returned items may be lower. |
| total | Number | Number of records that can be returned when offset and limit is not considered. This is to be used for paging through the data. With group=call this counts CALLS, not legs, and offset/limit page over calls as well. |
| cdrs | Array | Array of CDR records, see definition below. |
| CDR (JSON) | ||
|---|---|---|
| _id | Id | Unique ID of call. |
| pbx | String | Hostname of server that handled the call. |
| channelId | String | Name of channel on server. |
| callId | String | SIP Call ID, if available. |
| started | Date | Date and time when the call started. |
| answered | Date | Date and time when the call was answered, null if it was not. |
| answeredByCallee | Boolean | True if call was answered by the callee. False if not answered or answered by system. |
| ended | Date | Date and time when the call ended. |
| customer | Id | Id of customer. |
| terminatedBy | String | Who terminated the call: A for caller, B for callee or S for system. |
| terminatedReason | String | How was the call terminated: CANCEL - no answer, cancelled by caller HANGUP - hangup after answered BUSY - callee was busy UNREACHABLE - callee could not be called ERROR - call ended due to an error BLOCKED - call blocked due to suspicion of fraud or if blacklisted by user NOT_FOUND - called number does not exist |
| cdrNote | String | CDR note as set during the call. |
| callRef | String | Grouping key shared by every row of the same physical call. Null on calls handled before call grouping was introduced. |
| parentChannelId | String | channelId of the leg this one was spawned from. Absent on the call's first leg. |
| legType | String | How this leg came into existence: ORIGIN, BLIND_TRANSFER, INTERPBX, LOCAL or PICKUP. |
| legCount | Number | group=call only. Number of legs this row covers - 1 for a call that was never transferred. |
| legs | Array | group=call only, and only present when legCount is greater than 1. The individual legs, each a CDR object as described here, ordered by started. |
| quality | Object | Audio quality badge - only returned for TX staff, see Call audio quality |
| quality.verdict | String | OK, DEGRADED, BAD, NO_AUDIO or NOT_MEASURED. |
| quality.measured | Boolean | True when at least one leg of the call could be judged. False is not the same as OK. |
| quality.severity | Number | Numeric rank of the verdict (0-4), for sorting only. |
| quality.worst | Object | The single worst finding: code, role, party, direction, percentMissing. Null when there is nothing to report. |
| rtpStats | Array | Raw per-leg RTP counters. Only returned when includeRtpStats is set and the user is TX staff (ADMIN, or RESELLER_ADMIN with TELE). |
| caller | Object | Caller data |
| caller.number | String | Callers phone number. |
| caller.name | String | Callers name, if available. |
| caller.extension | Id | Id of extension that made the call, if the call was made on a SIP or MVNO phone. |
| caller.sipPhone | Id | Id of the SIP phone the call was made on. Null if it was not. |
| caller.privacy | Boolean | True if callers number shall be hidden. |
| caller.type | String | Type of caller: EXTERNAL, SYSTEM, SIPPHONE, MOBILE, EXTERNALPHONE. |
| caller.divertedBy | String | If call was diverted, this is the number of the diverter, |
| callee | Object | Callee data |
| callee.number | String | Callees phone number. |
| callee.name | String | Callees name, if available. |
| callee.extension | Id | Id of extension that received the call, if it was received on a SIP or MVNO phone. |
| recording | Array | List of call recordings - if requested and the call was recorded |
| recording[]._id | Id | Id of recording. |
| recording[].by | String | Who recorded the call: EMPLOYEE or COMMON. |
| recording[].employee | Id | Id of employee the recording belongs to, if by is COMMON. |
| recording[].length | Number | Length of recording in seconds. |
| recording[].followUp | Boolean | True if follow up has been set. |
| recording[].followUpNote | String | Follow up note - if any. |
{ "offset": 0, "limit": 100, "total": 7, "cdrs": [ { "_id": "5d035c832d357b369b6f7aff", "pbx": "pbx3.telecomx.dk", "channelId": "1560501368.50818", "callId": "9f09cceff8250a4e", "started": "2019-06-14T08:36:08.791Z", "answered": null, "answeredByCallee": false, "ended": "2019-06-14T08:36:19.523Z", "caller": { "number": "210", "name": "Per Møller", "extension": "5cfd1d10327c3b67a69883c6", "sipPhone": "5c5aaa7a83a3bc74d5891d29" }, "callee": { "number": "432", "name": "Michael Hansen", "extension": "5c52a38ddd7b982b96d4aacc" }, "recording": [ { "_id": "12345678901234567890AAEE", "by": "EMPLOYEE", "employee": "12345678901234567890FEDE", "length": 120, "followUp": true, "followUpNote": "Call back and make invoice" } ], "customer": "52f4fd7734697b28ccaf77ec", "terminatedBy": "A", "terminatedReason": "HANGUP", "cdrNote": "This is a note", "quality": { "verdict": "DEGRADED", "measured": true, "severity": 2, "worst": { "code": "INBOUND_DELIVERY_LOW", "role": "caller", "party": { "kind": "SIP_PHONE", "model": "Mitel-6867i", "raw": "SIPPHONE/Mitel-6867i" }, "direction": "INBOUND", "percentMissing": 16 } } }, { ... } ] }
| Error code | Message | Description |
|---|---|---|
| 404 | extension | Extension not found |
| 403 | extension | Access to extension is denied |
| 403 | access_denied | Insufficient access level |
| 500 | internal_error | <Unspecified> |
One physical call produces several CDR records: a phone-initiated blind transfer, a hop to another
PBX node, a dialplan re-entry, a pickup or a steal each start a new call leg, and a transfer made
from the app closes the running record and starts another one. Listed flat, those records look like
several overlapping calls from the same number - which is what group=call exists to fix.
With group=call:
started is the earliest leg's, ended the latest leg's, answered the first answer,
and ''callee'', ''terminatedBy'' and ''terminatedReason'' come from the LAST leg - where the call actually ended up. * ''answeredByCallee'' is true when ANY leg was answered by a person. It is the field to render "answered" from: a caller who waited in a queue and gave up has ''answered'' set (the system answered, to play audio) but ''answeredByCallee'' false, and ''terminatedReason'' says HANGUP. * ''quality'', where the caller is entitled to see it at all, is the worst badge across the legs, and ''recording'' is the union of the legs' recordings. * A call is returned WHOLE when any one of its legs matches the filters, so filtering on an extension or a number returns the other legs of that call as well. * Access is unaffected. A PERSONAL user still only sees calls that touch their own extensions. * Records with no ''callRef'' - every call handled before call grouping was introduced - form a group of one and come back exactly as they do with ''group=leg''.
CSV and EXCEL exports are always ungrouped, one line per leg, regardless of group.
Audio quality is TX staff only (changed 27-08-2026). For ADMIN, and for RESELLER_ADMIN with
the TELE employee feature, every CDR carries a quality badge and the CSV and EXCEL exports carry
the verdict in a Lydkvalitet column. For everybody else the quality field is absent entirely
and the export has no such column - an integration that reads the column by name must tolerate it
not being there.
Full per-leg detail for one call is available from CDR quality, and the calls with the worst audio in a period from Call audio quality : Calls.