How to Get a New Call Recording Notification?

RingCentral push notification is an event listener service which allows RingCentral customers to subscribe for getting notification of an event when something happens to their RingCentral account. Those events such as when there is an incoming SMS message, when a user receives an incoming call, or when a user status has changed, etcetera.

However, there is no push notification for the event of a new call recording has been created. In this blog, I am going to show you how to use the Account Presence Push Notification to detect when there is a new call recording then download the audio file to a local folder.

First of all, we need a RingCentral app with the permission and get the and ready for platform access authentication. If you don’t know how to create a RingCentral app, please read this tutorial.

The Account Presence Event provides us with different user statuses. In this application, we are interested only at the which has the following value:

‘NoCall’ | ‘CallConnected’ | ‘Ringing’ | ‘OnHold’ | ‘ParkedCall’

When an extension (a user) under an account has no active call, its is ‘NoCall’. When there is an incoming or outgoing call on an extension, the is changed from ‘NoCall’ to ‘Ringing’. Then, if the incoming/outgoing call is accepted, the is changed again from ‘Ringing’ to ‘CallConnected’. Now, when the call is terminated, the telephonyStatus will be changed from ‘CallConnected’ to ‘NoCall’.

With these statuses change sequence, we can implement our application to detect and follow the status change to determine when an active call is terminated. During the call, if the user enabled the call recording, then it takes around 20 seconds after the call ended, for the system to finalize the call record and save it to the call logs database. At that point in time, we can call the call-log API to check if there is a call recording logged for that extension during the period of time of that call.

Let’s see how we implement that in our code using the Node JS SDK.

First, we create the SDK instance and retrieve the platform and the subscription objects.

var rcsdk = new RC({
    server:RC.server.sandbox,
    appKey: process.env.CLIENT_ID_SB,
    appSecret:process.env.CLIENT_SECRET_SB
})
var platform = rcsdk.platform()
var subscription = rcsdk.createSubscription()

Then, we implement a login function to login our RingCentral account

function login(){
    platform.login({
        username: process.env.USERNAME_SB,
        password: process.env.PASSWORD_SB
    })
    .then(function(resp){
        subscribeForNotification()
    })
    .catch(function(e){
        console.log(e)
        throw e
    })
}

Please note that we want to get events of all extensions (users) under the same company account. Therefore, the username and password we used for logging in the account must be from a user who has the admin role or the main company user.

Subscribe for push notification

After logged in successfully, we call the function to register for the account presence event push notification.

function subscribeForNotification(){
    var eventFilter = ['/restapi/v1.0/account/~/presence']
    subscription.setEventFilters(eventFilter)
    .register()
    .then(function(resp){
        console.log('ready to get account presence events')
    })
    .catch(function(e){
        throw e
    })
}

Within the function, we register for the account presence event.

Then we set a function to receive notification message.

subscription.on(subscription.events.notification, callback)function callback(msg){
    var user = {}
    user['extensionId'] = msg.body.extensionId
    user['telephonyStatus'] = msg.body.telephonyStatus
    user['startTime'] = ""
    checkTelephonyStatusChange(user)
}

Detect presence event

Whenever there is a presence event notification, the function is called and we parse the to access the presence event payload. There are many data fields from the payload, but in this particular application, we are interested at only the which will be used to identify a user, and the which will be used to detect the current telephone status of the user.

Now, we implement the function to determine an incoming call, an active call and finally, the call termination. Remember that we are getting notification for all users under the same account, that is why we will need a list of users to keep track of each user statuses.

var users = []
function checkTelephonyStatusChange(user){
    var newUser = true
    for (var i=0; i<users.length; i++){
      if (users[i].extensionId == user.extensionId){
        newUser = false
        if (users[i].telephonyStatus == "NoCall" &&
            user.telephonyStatus == "Ringing"){
          users[i].telephonyStatus = user.telephonyStatus
          var date = new Date()
          var dateFrom = date.toISOString()
          users[i].startTime = dateFrom.replace('/', ':')
          console.log("has an incoming call")
          break
        }
        if (users[i].telephonyStatus == "Ringing" &&
            user.telephonyStatus == "CallConnected"){
          users[i].telephonyStatus = user.telephonyStatus
          console.log("has accepted a call")
          break
        }
        if (users[i].telephonyStatus == "Ringing" &&
            user.telephonyStatus == "NoCall"){
          users[i].telephonyStatus = user.telephonyStatus
          console.log("has a missed call")
          break
        }
        if (users[i].telephonyStatus == "CallConnected" &&
            user.telephonyStatus == "NoCall"){
          users[i].telephonyStatus = user.telephonyStatus
          console.log("connected call is terminated")
          var date = new Date()
          var stopTime = date.toISOString()
          stopTime = stopTime.replace('/', ':')
          // cause a 30 sec delay then check for call recordings
          setTimeout(function(){
            readExtensionCallLogs(users[i].extensionId,
                                  users[i].startTime, stopTime)
          }, 20000)
          break
        }
     }
  }
  if (newUser){
      if (user.telephonyStatus == "Ringing"){
        var date = new Date()
        var dateFrom = date.toISOString()
        user.startTime = dateFrom.replace('/', ':')
        console.log("has an incoming call")
      }
      users.push(user)
  }
}

When there is no telephone call, the telephony status of a user is ‘NoCall’. So when there is an incoming call to a phone number of that user, the telephony status is changed from ‘NoCall’ to ‘Ringing’. If there is no answer to the incoming call, the telephony status will be changed from ‘Ringing’ to ‘NoCall’. If the user accepts an incoming call, the telephony status will be changed from ‘Ringing’ to ‘CallConnected’.

Inside the function above, we examine the telephony status of a user. We also check if that user has been added to the users list. If the user is found from the list, we check the following status sequence:

1. If the old telephony status of that user is ‘NoCall’ and the new telephony status is ‘Ringing’, then we create a timestamp and add it to the user object. At this point in time, the user object would have the user extension id, the current telephony status as ‘Ringing’ and the timestamp when it starts ringing.

2. If the old telephony status of that user is ‘Ringing’ and the new telephony status is ‘CallConnected’, then we replace the old telephony status with the new value ‘CallConnected’ and wait for the telephony status changed again. At this point in time, the user object would have the user extension id, the current telephony status as ‘CallConnected’ and the timestamp when it starts ringing.

3. If the old telephony status of that user is ‘CallConnected’ and the new telephony status is ‘NoCall’, then we replace the old telephony status with the new value of ‘NoCall’, and we assume that the call has been terminated. Now, we create the timestamp and we wait for about 20 seconds before calling the function, where we will call the call-log API to detect a call recording.

If the user is not found from the list, we assume that the old telephony status is ‘NoCall’ and we detect if the current telephony status of that user is ‘Ringing’ then we create the timestamp and add it to the user object then add the user object to the list. At this point in time, the user object would have the user extensionId, the current telephony status as ‘Ringing’ and the timestamp when it starts ringing.

Read extension call logs

When calling the function, we passed the the when the call started ringing, and the when the call terminated. We use the to create an endpoint for reading the call log of that particular extension. We also specify the and the for the and query parameters respectively, to fetch call log information within that period of time. It is the period of time between the call started ringing and the call terminated.

var endpoint = '/account/~/extension/'+ extensionId +'/call-log'
var params = {}
params['dateFrom'] = startTime
params['dateTo'] = stopTime
params[recordingType] = 'All'

We also want to read call log which has a call recording only. To do that, we set the value of the parameter to ‘All’.

Now, we read the call log and iterate through the records array to find a new call recording.

platform.get(endpoint, params)
  .then(function(resp){
    async.each(resp.json().records,
      function(record, callback){
        console.log("THIS CALL HAS A RECORDING")
        saveAudioFile(record)
      },
      function(err){
        console.log("No new record.")
      }
    );
  })
  .catch(function(e){
    var err = e.toString();
    console.log(err)
  })

Finally, it’s up to us to process the call log information and the call recording content. In this application, we save the call recording binary file to a local folder.

var fs = require('fs')
function saveAudioFile(record){
  platform.get(record.recording.contentUri)
    .then(function(res) {
      return res.response().buffer();
    })
    .then(function(buffer) {
      var destFile = './recordings/'
      destFile += record.recording.id + '.mp3'
      fs.writeFileSync(destFile, buffer);
      console.log("CALL RECORDING SAVED TO: " + destFile)
    })
    .catch(function(e){
      console.log(e)
    })
}

You can follow the step by step tutorial or clone the full project from our Github repository.

Learn more about our Developer Program here: https://developer.ringcentral.com/

RingCentral Developers

Cloud Business Communications