Call Detail Record Application

If you make calls using CarrierX and want to have a detailed information about these calls for a certain period of time, you can retrieve such information with the help of CarrierX API. But, if you have numerous calls, the call detail records that you receive from the API might contain a lot of data about hundreds or even thousands of calls.

In this case, it might be more useful to get the call detail records one by one, starting with the call made on a certain date and ending with the last call record present in the database.

In this guide, you will learn how to write an application allowing you to request the call detail records starting with a certain date.

I. Plan Application Structure

Before we start writing our application, we need to plan how the application is going to behave depending on different responses from CarrierX API. To do that, we suggest the following algorithm that describes the application behavior:

  1. The application sends an API request to get the first call made on a certain date, specified in the request.
  2. CarrierX API responds to the request:
    • If the response does not contain any call detail records, the application prints a message and exits.
    • If the response contains a call detail record, the application extracts the call detail record secure ID (together with any other data we need).
  3. Now, the application uses the received secure ID to send a new request that is going to return the call detail record inserted into the database after the record with the secure ID specified in the request.
    • If the response does not contain any call detail records, the application considers that the previous record was the last one. It prints a dedicated message and exits.
    • If the response contains a call detail record, the application extracts the call detail record secure ID and other required data, and repeats step 3.

This application algorithm can be described with the following block diagram:

Application Algorithm

II. Form API Requests

Now, we need to understand which API requests we will use to achieve our goal.

1. Get First Call

All the details about the calls are stored in Call Detail Record objects. You can get the information about these records using the following GET method:

curl -X GET \
'https://api.carrierx.com/core/v2/calls/call_drs' \
-H 'Authorization: Bearer 5ebc03d6-8b2b-44ad-bf65-72d4f1491dda'

This request will return the list of all the calls made by the partner. If the list is long (in some cases, the lists can include hundreds or even thousands of records) it will take rather a long time and return a lot of information.

To limit the data returned by the request, we will use the following restrictions:

Now, our request looks like this:

curl -X GET \
'https://api.carrierx.com/core/v2/calls/call_drs?limit=1&order=date_stop+asc&filter=date_stop+ge+2021-07-01' \
-H 'Authorization: Bearer 5ebc03d6-8b2b-44ad-bf65-72d4f1491dda'

If the request is successful and there are call detail records matching the restrictions set in the request, the response will look like this:

{
    "count": 1,
    "has_more": true,
    "items": [
        {
            "cic": null,
            "cic_original": null,
            "cic_transformed": null,
            "date_insert": "2021-07-12T12:24:02.070Z",
            "date_start": "2021-07-12T12:21:15.936Z",
            "date_stop": "2021-07-12T12:21:45.689Z",
            "date_talk": "2021-07-12T12:21:20.347Z",
            "direction": "inbound",
            "disconnect_originator": "SRC",
            "diversion_dst": null,
            "diversion_src": null,
            "dr_sid": "bb2aacd2-23d3-418e-9ec0-2fc45c5f9d76",
            "duration": "29.75362",
            "duration_billing": "60",
            "endpoint_sid_dst": "4bb610b4-0e76-4d78-b817-7b23673b221b",
            "endpoint_sid_src": "db58a951-6146-4941-b936-2ba801dcc5d6",
            "identity": null,
            "ip_dst": "10.222.2.132:5060",
            "ip_src": "12.7.192.27",
            "number_billing": "15162065919",
            "number_dst": "15162065919",
            "number_dst_original": null,
            "number_dst_transformed": null,
            "number_external": "15187147330",
            "number_src": "+15187147330",
            "number_src_original": null,
            "number_src_transformed": null,
            "partner_sid": "cee93bf3-5746-43fe-a1a2-822c05fef687",
            "price": "0.0025",
            "price_lcr_dst": "0.0025",
            "price_lcr_src": null,
            "rate": "0.0025",
            "rate_lcr_dst": "0.005",
            "rate_lcr_src": null,
            "sipcallid_dst": "0afd5438792794ef3abb38b432d55a9d@12.7.193.174",
            "sipcallid_src": "1d60ac9c16a61ea62f7306851209b393@12.7.192.27",with
            "trunk_group_sid_dst": "9dd2f46c-a39e-4a78-82af-cccbd4049ebb",
            "trunk_group_sid_src": null,
            "trunk_sid_dst": "998ed21f-edec-45f2-9b00-5273620e5c51",
            "trunk_sid_src": null,
            "type": "telecom",
            "version": 2
        }
    ],
    "limit": 1,
    "offset": 0,
    "pagination": {
        "next": "https://api.carrierx.com/core/v2/calls/call_drs?limit=1&order=date_stop+asc&filter=date_stop+ge+2021-07-01&offset=1"
    },
    "total": null
}

If no records matching the restrictions are present in the database, the response will look like this instead:

{
    "count": 0,
    "has_more": false,
    "items": [],
    "limit": 1,
    "offset": 0,
    "pagination": {},
    "total": null
}

Change the date and send the request again until you get non-empty results.

2. Get Next Calls

What we need in the response is the items array which contains the list of the call detail records. We have restricted this list to only one entry and we need the dr_sid of that single call detail record. In our case, it is "dr_sid": "bb2aacd2-23d3-418e-9ec0-2fc45c5f9d76".

Knowing this call detail record secure ID, we can send our next request to get the calls that were inserted into the database after this call.

We will use the pagination in our new request with the same values for the limit and order parameters as we did in the previous request.

We will additionally use a new after query parameter and set it equal to the dr_sid value from the response:

curl -X GET \
'https://api.carrierx.com/core/v2/calls/call_drs?limit=1&order=date_stop+asc&after=bb2aacd2-23d3-418e-9ec0-2fc45c5f9d76' \
-H 'Authorization: Bearer 5ebc03d6-8b2b-44ad-bf65-72d4f1491dda'

The successful request will return the list of the call detail records with a single entry of the call following the one with the secure ID specified in the request. An empty list in the response will tell us that the call from the request was the last one made.

Knowing all this, we can proceed to writing the application.

III. Write Application

In this section, we will write our application using Python programming language.

1. Send First Request and Get Response

Let’s start with creating a Python code file. We will call it cdr-application.py.

The only Python library we need to import is Requests. It will allow our application to send the requests to CarrierX API and receive the responses from it:

import requests

Now, let’s add the prerequisites for the first request: the authentication header, the URL to which we will send the request, and the query parameters which must be present in the URL. The GET request we need is the requests.get():

import requests

headers = {'Authorization': 'Bearer 5ebc03d6-8b2b-44ad-bf65-72d4f1491dda'}
url = 'https://api.carrierx.com/core/v2/calls/call_drs'
params = {
'limit': '1',
'order': 'date_stop asc',
'filter': 'date_stop ge 2021-07-01'
}
r = requests.get(url, headers=headers, params=params)

The successful request will return an object that we need to parse in order to get the necessary data (in our case, the call detail record secure ID).

2. Parse Response

As we could see in this section, the response contains the call detail records. They are listed in the items array.

To get the necessary key from the Call Detail Record object—that is, dr_sid—we need to get the items array and parse its first element.

We will save the result from the Python builtin JSON decoder (.json()) into the dr_items dictionary variable:

import requests

headers = {'Authorization': 'Bearer 5ebc03d6-8b2b-44ad-bf65-72d4f1491dda'}
url = 'https://api.carrierx.com/core/v2/calls/call_drs'
params = {
'limit': '1',
'order': 'date_stop asc',
'filter': 'date_stop ge 2021-07-01'
}
r = requests.get(url, headers=headers, params=params)
dr_items = r.json()['items']
dr_sid = dr_items[0]['dr_sid']
print(dr_sid)

The first element of the dr_items dictionary will contain the dr_sid we need.

Now let’s run the application using the following command:

python3 ./cdr-application.py

If the application request is successful, the application will print the following result into the console:

bb2aacd2-23d3-418e-9ec0-2fc45c5f9d76

This is the secure ID of the first call detail record we need.

3. Send Next Requests

Now that we have the secure ID of the first record, we can continue with the requests for the records which follow it.

To do this, we create a second request. We use the same syntax, the only difference is in the request query parameters which contain the after query parameter instead of the filter:

import requests

headers = {'Authorization': 'Bearer 5ebc03d6-8b2b-44ad-bf65-72d4f1491dda'}
url = 'https://api.carrierx.com/core/v2/calls/call_drs'
params = {
'limit': '1',
'order': 'date_stop asc',
'filter': 'date_stop ge 2021-07-01'
}
r = requests.get(url, headers=headers, params=params)
dr_items = r.json()['items']
dr_sid = dr_items[0]['dr_sid']
print(dr_sid)

params = {
'limit': '1',
'order': 'date_stop asc',
'after': dr_sid
}
r = requests.get(url, headers=headers, params=params)
dr_items = r.json()['items']
dr_sid = dr_items[0]['dr_sid']
print(dr_sid)

The successful application requests will print two calls secure IDs, like this:

bb2aacd2-23d3-418e-9ec0-2fc45c5f9d76
012e1d6a-75ac-43ec-bacc-68f1ef1c60a3

4. Poll until Last Record

Great! We see that our application can send the requests we need and returns relevant data. Now we need to poll the call detail records until the API returns no new entries.

We will do this using the while Python loop.

It works like this:

So, in addition to the while loop we need the condition. In our case, it will be checking if len(r.json()['items']) is more than 0. If it is so, then it means that it contains a Call Detail Record object inside it. If it is 0, it means that this array is empty, i.e., no new calls are available. In this case, we break the loop and the application says No more new calls:

import requests

headers = {'Authorization': 'Bearer 5ebc03d6-8b2b-44ad-bf65-72d4f1491dda'}
url = 'https://api.carrierx.com/core/v2/calls/call_drs'
params = {
'limit': '1',
'order': 'date_stop asc',
'filter': 'date_stop ge 2021-07-01'
}
r = requests.get(url, headers=headers, params=params)
dr_items = r.json()['items']
dr_sid = dr_items[0]['dr_sid']
print(dr_sid)

while True:
    params = {
    'limit': '1',
    'order': 'date_stop asc',
    'after': dr_sid
    }
    r = requests.get(url, headers=headers, params=params)
    if len(r.json()['items']):
        dr_items = r.json()['items']
        dr_sid = dr_items[0]['dr_sid']
        print(dr_sid)
    else:
        print('No more new calls')
        break

Let’s run the application again. If the application requests are successful, it will print the following output into the console:

bb2aacd2-23d3-418e-9ec0-2fc45c5f9d76
012e1d6a-75ac-43ec-bacc-68f1ef1c60a3
56fb6b94-811e-4952-b242-4e06f4e95b7e
No more new calls

5. Add More Checks

But what if the first request returns zero results, meaning that no calls were made since the date in the request? In this case, the application will throw an error.

To avoid such errors, let’s add the code that will check if the first request returns valid results.

We will do it the same way we did with the while loop—checking the length of the items array:

headers = {'Authorization': 'Bearer 5ebc03d6-8b2b-44ad-bf65-72d4f1491dda'}
url = 'https://api.carrierx.com/core/v2/calls/call_drs'
date = '2021-07-01'
params = {
'limit': '1',
'order': 'date_stop asc',
'filter': f'date_stop ge {date}'
}
r = requests.get(url, headers=headers, params=params)
dr_items = r.json()['items']
if len(dr_items):
    dr_sid = dr_items[0]['dr_sid']
    print(dr_sid)
else:
    print(f'No calls since {date}')

We also moved the date from the first request to the date variable, and now we can both use it with the request query parameters and display it as a part of the No calls since message if there are no call detail records for this date.

The application must execute the second and all the following requests only if the first request was also successful. This is why we move our while loop inside the if condition of the first request.

The code will look like the following:

import requests

headers = {'Authorization': 'Bearer 5ebc03d6-8b2b-44ad-bf65-72d4f1491dda'}
url = 'https://api.carrierx.com/core/v2/calls/call_drs'

date = '2021-07-01'
params = {
'limit': '1',
'order': 'date_stop asc',
'filter': f'date_stop ge {date}'
}
r = requests.get(url, headers=headers, params=params)
dr_items = r.json()['items']
if len(dr_items):
    dr_sid = dr_items[0]['dr_sid']
    print(dr_sid)
    while True:
        params = {
        'limit': '1',
        'order': 'date_stop asc',
        'after': dr_sid
        }
        r = requests.get(url, headers=headers, params=params)
        if len(r.json()['items']):
            dr_items = r.json()['items']
            dr_sid = dr_items[0]['dr_sid']
            print(dr_sid)
        else:
            print('No more new calls')
            break
else:
    print(f'No calls since {date}')

If we run our application, it will display the results like at the previous step:

bb2aacd2-23d3-418e-9ec0-2fc45c5f9d76
012e1d6a-75ac-43ec-bacc-68f1ef1c60a3
56fb6b94-811e-4952-b242-4e06f4e95b7e
No more new calls

If we change the date to some later one when there were no calls (e.g., July 15, 2021), the application will print the following text into the console:

No calls since 2021-07-15

6. Save Results to CSV File

Now that the application works as intended, we can save the results into a CSV file.

Python csv library will help us create a CSV file and save the results into it.

The use of the csv library is not too complicated:

  1. import the library;
  2. open the file for writing;
  3. write the header row;
  4. write the data rows.

First, we add the csv library to the existing import statement like this:

import requests, csv

We create the output CSV file using the with statement:

with open('calls.csv', 'w', encoding='UTF8') as csv_file:
    csv_writer = csv.writer(csv_file)

We will add the following extended information about each call to the the secure IDs of the call detail records: the call date and time, calling party phone number, called party phone number, call direction, duration in seconds, and price in US dollars.

We can take all this information from the Call Detail Record object fields.

The header row will include the names of the attributes. We will create this row like this:

csv_header = ['dr_sid','date_start','number_src','number_dst','direction','duration','price']
csv_writer.writerow(csv_header)

After that we write the data rows. For the first request this will look like this:

csv_row = [dr_items[0]['dr_sid'],dr_items[0]['date_start'],dr_items[0]['number_src'],dr_items[0]['number_dst'],dr_items[0]['direction'],dr_items[0]['duration'],dr_items[0]['price']]
csv_writer.writerow(csv_row)

For the subsequent requests, we first increase the number of records returned in each request. When you have dozens of thousands records in the database, polling one request at a time can take very long time.

We can set a bigger number of the returned records modifying the limit value.

Let’s set it to 100. In this case, the parameters for the subsequent requests will look like this:

params = {
'limit': '100',
'order': 'date_stop asc',
'after': dr_sid
}

After that, we need to iterate through each of the batch of the records using the for loop and save the results into our CSV file:

for item in dr_items:
    dr_sid = dr_items[len(dr_items) - 1]['dr_sid']
    csv_row = [item['dr_sid'],item['date_start'],item['number_src'],item['number_dst'],item['direction'],item['duration'],item['price']]
    csv_writer.writerow(csv_row)

We will print the call secure ID with the index to the console. We added the i variable that will print the number of the record into the console and increment with each new record:

print(f"{i}. {item['dr_sid']}")

The resulting code might look like this:

import requests, csv

headers = {'Authorization': 'Bearer 5ebc03d6-8b2b-44ad-bf65-72d4f1491dda'}
url = 'https://api.carrierx.com/core/v2/calls/call_drs'

date = '2021-07-01'
i = 1
params = {
'limit': '1',
'order': 'date_stop asc',
'filter': f'date_stop ge {date}'
}
r = requests.get(url, headers=headers, params=params)
dr_items = r.json()['items']
if len(dr_items):
    with open('calls.csv', 'w', encoding='UTF8') as csv_file:
        csv_writer = csv.writer(csv_file)
        csv_header = ['dr_sid','date_start','number_src','number_dst','direction','duration','price']
        csv_writer.writerow(csv_header)
        dr_sid = dr_items[0]['dr_sid']
        csv_row = [dr_items[0]['dr_sid'],dr_items[0]['date_start'],dr_items[0]['number_src'],dr_items[0]['number_dst'],dr_items[0]['direction'],dr_items[0]['duration'],dr_items[0]['price']]
        csv_writer.writerow(csv_row)
        print(f"{i}. {dr_items[0]['dr_sid']}")
        while True:
            params = {
            'limit': '100',
            'order': 'date_stop asc',
            'after': dr_sid
            }
            r = requests.get(url, headers=headers, params=params)
            if len(r.json()['items']):
                dr_items = r.json()['items']
                for item in dr_items:
                    i += 1
                    dr_sid = dr_items[len(dr_items) - 1]['dr_sid']
                    csv_row = [item['dr_sid'],item['date_start'],item['number_src'],item['number_dst'],item['direction'],item['duration'],item['price']]
                    csv_writer.writerow(csv_row)
                    print(f"{i}. {item['dr_sid']}")
            else:
                print('No more new calls')
                break
else:
    print(f'No calls since {date}')

Now run the resulting application.

If everything is OK, the application will print an output like this into the console:

1. bb2aacd2-23d3-418e-9ec0-2fc45c5f9d76
2. 012e1d6a-75ac-43ec-bacc-68f1ef1c60a3
3. 56fb6b94-811e-4952-b242-4e06f4e95b7e
No more new calls

and create the calls.csv file which is going to contain the information like this:

dr_sid,date_start,number_src,number_dst,direction,duration,price
bb2aacd2-23d3-418e-9ec0-2fc45c5f9d76,2021-07-12T12:21:15.936Z,+15187147330,15162065919,inbound,29.75362,0.0025
012e1d6a-75ac-43ec-bacc-68f1ef1c60a3,2021-07-12T12:28:30.490Z,+15187147330,15162065919,inbound,46.84222,0.0025
56fb6b94-811e-4952-b242-4e06f4e95b7e,2021-07-14T14:22:36.191Z,+15187147330,15162065515,inbound,37.4222,0.0025

If we change the date to some later one when there were no calls, the application will print the following text into the console, just like it did at step 5:

No calls since 2021-07-15

You can change the data that the application returns or even save all the call detail record fields into the resulting CSV file. To do that, change the code like this:

import requests, csv

headers = {'Authorization': 'Bearer 5ebc03d6-8b2b-44ad-bf65-72d4f1491dda'}
url = 'https://api.carrierx.com/core/v2/calls/call_drs'

date = '2021-07-01'
i = 1
params = {
'limit': '1',
'order': 'date_stop asc',
'filter': f'date_stop ge {date}'
}
r = requests.get(url, headers=headers, params=params)
dr_items = r.json()['items']
if len(dr_items):
    with open('calls.csv', 'w', encoding='UTF8') as csv_file:
        csv_writer = csv.writer(csv_file)
        csv_head = []
        for key in dr_items[0]:
            csv_head.append(key)
        csv_writer.writerow(csv_head)
        dr_sid = dr_items[0]['dr_sid']
        csv_row_full = []
        for key in dr_items[0]:
            csv_row_full.append(dr_items[0][key])
        csv_writer.writerow(csv_row_full)
        print(f"{i}. {dr_items[0]['dr_sid']}")
        while True:
            params = {
            'limit': '100',
            'order': 'date_stop asc',
            'after': dr_sid
            }
            r = requests.get(url, headers=headers, params=params)
            if len(r.json()['items']):
                dr_items = r.json()['items']
                for item in dr_items:
                    i += 1
                    csv_row_full = []
                    dr_sid = dr_items[len(dr_items) - 1]['dr_sid']
                    for key in item:
                        csv_row_full.append(item[key])
                    csv_writer.writerow(csv_row_full)
                    print(f"{i}. {item['dr_sid']}")
            else:
                print('No more new calls')
                break
else:
    print(f'No calls since {date}')

This will save all the call detail record names as the CSV header row and their values as data rows.

You can additionally allow the input of the start date when the application starts, or add the date interval (the calls start and end dates) to find all the calls made between certain dates, or change anything in the app to your liking.

IV. Further Reading

You have created an application allowing you to view the detailed information about your calls starting with a certain date!

Refer to the Call Detail Record object API reference to get the complete list of the attributes and methods used with it.