Get CDR Records
Overview
Get call (and messaging) delivery records from your account.
Authentication
Endpoint
POST
https://api.portal.tsgglobal.world/graphql
Method
After sending messages or creating calls, you can check those by using the cdrRecords
query which returns a paginatedCdrResult
object type. This query is a bit more complex due to the usage of advanced GraphQL features like fragments and cursor based pagination.
Arguments which can be sent are listed below. The exclamation mark (!
)means a parameter is mandatory, otherwise it's optional:
companyId: ID
For which company to list CDRs (only for admin users)
type: CdrType!
The type of the CDR records to list
startDatetime: DateTime!
Will return all records with datetime >= startDatetime
endDatetime: DateTime!
Will return all records with datetime < endDatetime (UTC based), max value is now
cursor: String
Used to paginate, returns results for the passed cursor (if any)
limit: Int
Limit the number of results per page, default is 100, max is 1000
Sample Query
query cdrRecordsQuery(
$type: CdrType!
$startDatetime: DateTime!
$endDatetime: DateTime!
$limit: Int
$cursor: String
) {
cdrRecords(
type: $type
startDatetime: $startDatetime
endDatetime: $endDatetime
limit: $limit
cursor: $cursor
) {
exportLink
paginationInfo {
nextCursor
limit
}
cdrs {
... on ApiCdr {
datetime
from
destination
status
charge
type
}
... on MessageCdr {
datetime
fromNumber
toNumber
direction
status
charge
type
}
... on VoiceCdr {
datetime
callId
sourceAni
destinationNumber
didUsed
sessionTime
status
calledRate
charge
callType
aniIi
}
}
}
}
In this query GraphQL fragments are used (e.g. ... on MessageCdr { ... }
, they are needed because what we return is a union of types for cdrs
, and the fields are depending on the passed type
. In our example below we're using SMS
, but the fragments can be specified for each field in a general query, only change the type.
In our case the type is SMS
, you send it as an uppercase string, example of variables sent:
Sample Variables
{
"type": "SMS",
"startDatetime": "2022-04-26T07:00:00.000Z",
"endDatetime": "2022-04-26T17:13:05.377Z",
"limit": 20
}
Sample Response
{
"data": {
"cdrRecords": {
"cdrs": [
{
"__typename": "MessageCdr",
"account": "ACMEANCHO",
"charge": "0.0095000000",
"cost": "0.0051500000",
"datetime": "2022-01-02T00:00:00Z",
"direction": "SMS / MO Surcharge: T-Mobile - TF",
"fromNumber": "12247512345",
"status": "SENT",
"surcharge": "0.0025000000",
"toNumber": "18447312345",
"uniqueId": "45e868c1-96b2-474c-a60f-98b76d812343"
},
{
"__typename": "MessageCdr",
"account": "ACMEANCHO",
"charge": "0.0010000000",
"cost": "0.0000000000",
"datetime": "2022-01-02T00:00:00Z",
"direction": "SMS / MO: AT&T - LO-ZERO",
"fromNumber": "18134512345",
"status": "SENT",
"surcharge": "0.0000000000",
"toNumber": "19725212345",
"uniqueId": "22854899-1bc9-47b2-9a90-98b76d812344"
},
{
"__typename": "MessageCdr",
"account": "ACMEANCHO",
"charge": "0.0095000000",
"cost": "0.0051500000",
"datetime": "2022-01-02T00:00:01Z",
"direction": "SMS / MT Surcharge: T-Mobile - TF",
"fromNumber": "18777312345",
"status": "SENT",
"surcharge": "0.0025000000",
"toNumber": "16502912345",
"uniqueId": "ce8590ac-0674-4ef8-8a7f-98b76d812345"
},
],
"exportLink": "https://api.portal.tsgglobal.world/export/call_records?sig=abe5dfefb701b284db7b8d18e4673654e788ab42a1ef51bcbc4f153ga218f2fa&end_datetime=2022-01-10T23%3A59%3A59.999Z&fields=account%2Cunique_id%2Ccharge%2Cfrom_number%2Cto_number%2Cstatus%2Cdatetime%2Ccost%2Csurcharge%2Cdirection&start_datetime=2022-01-01T00%3A00%3A00.000Z&type=sms&company_internal_id=ACMEANCHO&new_export=true&user_id=e17bbdf0-e72a-4998-bcfb-8f3e92123456",
"paginationInfo": {
"limit": 100,
"nextCursor": null
}
}
}
}
Pagination
We're using pagination for this endpoint because there can be milions of records for short time frames. To paginate through the results tweak the previous query so it looks like this (notice the new $limit
and $cursor
arguments):
query($type: CdrType!, $startDatetime: DateTime!, $endDatetime: DateTime!, $companyId: ID, $limit: Int, $cursor: String) { cdrRecords(type: $type, startDatetime: $startDatetime, endDatetime: $endDatetime, companyId: $companyId, limit: $limit, cursor: $cursor) {
query($type: CdrType!, $startDatetime: DateTime!, $endDatetime: DateTime!, $companyId: ID, $limit: Int, $cursor: String) {
cdrRecords(type: $type, startDatetime: $startDatetime, endDatetime: $endDatetime, companyId: $companyId, limit: $limit, cursor: $cursor) {
cdrs {
__typename
... on MessageCdr {
account
uniqueId
charge
fromNumber
toNumber
status
datetime
cost
surcharge
direction
}
... on ApiCdr {
account
charge
destination
datetime
status
from
uniqueId
type
}
... on VoiceCdr {
account
charge
destination
datetime
status
uniqueId
aniIi
callId
callType
destinationNumber
sourceAni
calledRate
didUsed
sessionTime
}
}
paginationInfo {
limit
nextCursor
}
exportLink
}
}
and variables (limit=2
and cursor=null
):
{
"type": "SMS",
"startDatetime": "2022-01-01T00:00:00.000Z",
"endDatetime": "2022-01-10T23:59:59.999Z",
"companyId": "65deb62c-bb16-4a9d-927a-ec1abb37a832",
"limit": 2,
"cursor": null
}
The results we get have paginationInfo
filled differently, notice the limit
and nextCursor
properties below:
{
"data": {
"cdrRecords": {
"cdrs": ["...edited for readability..."],
"exportLink": "...edited for readability...",
"paginationInfo": {
"limit": 2,
"nextCursor": "123456"
}
}
}
}
We need to use the nextCursor
value to get the next page, the variables now look like the following:
{
"type": "SMS",
"startDatetime": "2022-01-01T00:00:00.000Z",
"endDatetime": "2022-01-10T23:59:59.999Z",
"companyId": "65deb62c-bb16-4a9d-927a-ec1abb37a832",
"limit": 2,
"cursor": "123456"
}
This will get us the next round of results with another nextCursor
value:
{
"data": {
"cdrRecords": {
"cdrs": ["...edited for readability..."],
"exportLink": "...edited for readability...",
"paginationInfo": {
"limit": 2,
"nextCursor": "554433"
}
}
}
}
We can paginate until the result we get has nextCursor: null
, when it's null
it means we reached the end.
Exporting CDRs
You can export CDRs by using the exportLink
which the cdrRecords
query provides. The easiest way is to click on the link, but you can also do a server-side or client-side download if you want to save the results.
The links in the above example won't work since they're fake but you can generate one with your queries. The export will trigger a CSV file download (via HTTP streams) and include the fields you specified in the query, but the pagination options will be ignored.
Example of an export Link:
https://api.portal.tsgglobal.world/export/call_records? \
sig=abe5dfefb701b284db7b8d18e4673654e788ab42a1ef51bcbc4f153ga218f2fa& \
fields=account,unique_id,charge,from_number,to_number,status,datetime,cost,surcharge,direction& \
start_datetime=2022-01-01T00:00:00.000Z& \
end_datetime=2022-01-10T23:59:59.999Z& \
type=sms& \
company_internal_id=ACMEANCHO& \
new_export=true& \
user_id=e17bbdf0-e72a-4998-bcfb-8f3e92123456
Notes
The export link is protected by a signature and cannot be tampered with, once generated it's public.
The export process has a timeout of 20 minutes, so if you notice the export timing out reduce the startDatetime and endDatetime period to something manageable and try again
Last updated
Was this helpful?