Building a Real-Time Call Report App

Phong Vu
RingCentral Developers
8 min readAug 25, 2020

In my previous blog, I explained what a telephony session is, talked about how to register for the event notification and how to capture and parse the notification payload. In this blog, I will walk you through the essential steps to build a real-time call report app using the telephony session event notifications. Let’s get started.

Prerequisite and preparation

  1. You must have a RingCentral sandbox account or a RingCentral production account to try the demo app.
  2. You know how to setup your sandbox account to make outbound and receive inbound calls.
  3. Have a password flow type app or know how to create a new app to get the app client id and client secret that needed for the demo.

I will be using Node JS, the Express Web application framework and the RingCentral Node JS SDK to build the app.

Note: The code snippets shown in this article are shorter and just for illustration. They may not work directly with copy/paste. I recommend you download the entire project from here.

Read account’s extensions

The first step is to read all extensions under an account and create an extensions list where we will keep the extension id and name. The following code snippet shows you how to read all the extensions’ information from an account, parse each extension’s id and name and keep them in the “extensionList” array. The code also shows you how to read all extensions of a large company that has thousands of extensions. Because each API call will return a maximum of 1000 extensions (records), therefore, we will check the “nextPage” property and call the “readAccountExtensions(uri)” function recursively with the “uri” of the next page until there is no more next page to call.

Subscribe for telephony session event notifications

The next step is to subscribe for the telephony session event notifications. As I already discussed in my first blog, it is up to you to decide how to specify the subscription filters. If you want to subscribe for multiple extensions but not all the extensions under your account, you can use the extensions list to create the event filters array with only the extensions you want to monitor. In this demo, we are using the account level to subscribe for all extensions. We also specify the “expiresIn” parameter to the maximum value (20 years) so our notification will work continuously without the need to renew it.

Note when running the demo with ngrok tunnel:

  1. To run the demo app on your local machine, the tunnel session will be timed out after a few hours (depends on the service plan) that makes the webhook address unreachable. In that case, you will need to delete the old subscription and create a new one.
  2. Depending on the ngrok service plan, the connections limit can range from 40 to maximum of 120 connections / minute. If you run the demo on a busy account, you will easily hit the limit and the subscription will be blocked. In that case, reduce the numbers of notifications by avoiding using the subscription at the account level.

Implement an Event Handler class

To make the code reusable, we are going to implement the notification events handler in a separate EventHandler class.

After getting a list of all extensions, we create the “eventHandler” object and inside the evenHandler constructor, we create a new list of extensions named “monitoredExtensionList” which contains a list of extension objects. Each extension object contains the extension id, extension name and an “activeCalls” array. The reason we define the “activeCalls” as an array is that an extension can have multiple active calls simultaneously. If you don’t want to handle call events of some extensions, you can exclude them while creating the “monitoredExtensionList”.

Now, let’s define data members of an active call object:

The active call object above should contain enough information for our demo app. You can always modify it to meet your real app’s requirements.

Receive and parse telephony session events

Every time we receive a notification event, we call the “processNotification()” function where we will parse the event payload to identify if it is a new call event or a call stage changed event of an existing call, then we extract the call data and create or update the active call data accordingly.

For every event, we will parse the event payload, read the extension id from the “parties” object. If the extension id exists, we will use the extension id to find the extension object from the list of monitored extensions. Then we use the party id to look for an active call object from the “activeCalls” array of that extension object. If the active call object is found, we will parse the call info and update that active call object. Otherwise, we create a new active call object and add it to the “activeCalls” array of that extension object.

Create an active call object

A call session notification starts with a “Setup” event and ends with a “Disconnected” event. Basically, we can create a new active call object whenever we receive a “Setup” event. However, as I explained in my first blog, notification events may not arrive in the same order as they were generated due to some network situation. The out-of-order issue does not happen very often, but we should still deal with that when it happens. There are 2 ways to solve the problem. The first way is to put all events from the same call session into an array then sort the array by the sequence numbers or by the event timestamp. The second way is to check the “activeCalls” array to see if an active call exists or not. If there is no active call with the same call party id, then we create a new active call object and add it to the “activeCalls” array. As we are preparing for the events out-of-order situation, we will check the event status from both cases. See the illustrated code below:

Capture call stage timestamp

We already defined the timestamp data fields in an active call object, now we can capture the “eventTime” of each event and convert the time string into a call stage timestamp as follows:

As you can see, I also captured the local time timestamps when the call starts ringing, connecting and holding. Those additional timestamps will be used for calculating and displaying real-time durations that I will explain in the next section.

Calculate call durations

We defined the call duration data fields in an active call object, now we calculate the call durations using the stage timestamps as follows:

The call ringing duration is the time difference between the “call.ringTimestamp” and the “call.connectTimestamp”. We can consider the ringing duration as the agent’s respond time to an inbound call.

When the call is put on-hold by the agent, we will receive an event with the status code as “Hold”. And when the agent un-holds the call, we will receive an event with the status code as “Answered”. Thus, the call holding duration is the time difference between the “call.holdTimestamp” and the timestamp when the call stage changes from “Hold” to “Answered” as shown in the code above.

In our demo app, we want to display call ringing duration, call hold duration and call talk duration in real time. That is why we use the local timestamps to calculate the durations as shown below:

The “pollActiveCalls()” function is called every time (every second in this demo) when the client side sends a request for active calls’ info.

Detect call termination

When a call is terminated, we will receive two notification events, one for the agent and one for the customer. It would be interesting to know which party hangs up the call and particularly if the call was terminated during on-hold stage. We can rely on the order of the “Disconnected” event sequences to determine who hangs up the call. We also use the call.status to determine if the call is terminated during on-hold stage.

Define and detect call actions and call result

We define a set of call actions to classify the final status of a call as “Connected”, “Cancelled”, “Missed Call”, “Voicemail” or “Parked”. When a call is terminated, we use the call status changed sequences to specify the call final status and call final result as shown below:

Save call data to a database

Except the total call hold duration, all other call durations can be calculated dynamically using the call stage timestamps. That is why we don’t need to save all call data from the active call object into our database.

Display call information

In our demo app, we implement 3 dashboards (Real Time, Call Stats and Reporting) so that we can present call information in different ways. to display call info in real time, we poll for the active calls info every second and we display them on the dashboard as shown in the screenshot below.

Real Time Call Info

We can also display call statistics on a table:

Calls Stats

Or using graphics:

Real time Call Report

Check out the implementation of the client side from the main.js, calllogs.js and report.js JavaScript files.

Setup and run the demo app on your local machine

Follow the instructions from the README file to clone and setup the project and run the demo. If you don’t want to build your own project and still want to run the live demo on your sandbox or production account, open this link and login your RingCentral account with an admin user credentials to setup the monitored extensions list (only an admin user can run the settings to add extensions to the monitored extension list), once you have setup, any other users in your account can login to see the live demo.

Please let us know what you think by leaving your questions and comments below. To learn even more about other features we have make sure to visit our developer site and if you’re ever stuck make sure to go to our developer forum.

Want to stay up to date and in the know about new APIs and features? Join our Game Changer Program and earn great rewards for building your skills and learning more about RingCentral!

--

--