Skip to main content

I would like to be able to record call dispositions in our CRM system upon call completion. I set up a subscription to receive call related notifications (/restapi/v1.0/account/~/extension/~/presence?detailedTelephonyState=true&sipData=true) which seems to work fine. However, I do not see a link between notifications and call log entries which have the final disposition information attached and/or potential link to the voicemail recording. I can: 1) link notification and call log entries by sessionId for connected calls (both inbound and outbound) I cannot: 1) link "Ringing" or "NoCall" final notification to a Missed (inbound) call 2) link "Ringing" or "NoCall" final notification to a Voicemail (inbound) call 3) related to #2, I am not able to fetch the recording id I see call log ids when I list calls but they do not appear in web-hook notifications. At the same time, sessionId listed on call logs related to case #1 and #2 does not seem to be related to the web-hooks received in those cases. Is there some other way to resolve this issue?

You can subscribe to the Message Store events for a given extension as well (which notifies you when a voicemail, SMS, fax, etc... has been stored for that extension).

You can use NoCall events which have an associated "CallConnected" event (associated via the sessionId property) and a duration > 0 to determine when you need to go and fetch an associated CallLog for greater detail.
Benjamin, Correct me if I'm wrong but I do not see a link between some of the messages. Let's assume an agent has just received a Voicemail. Here's what happens. Two subscription related notifications arrive: 1) Ringing (session id is present in this message) 2) NoCall (call goes to voicemail) - (session id is present in this message) Message store subscription notification arrives: 1) new Voicemail - (no session id or any other meaningful link available). All we have in this case is the agent id. All I want to do is to be able to update records on our end with call outcome/disposition and duration. When I query active call log right when I receive NoCall event notification, I get "Unknown" result and duration. Call is being redirect to voicemail - this is fair. I thought Voicemail notification would contain a link (session id, call id?) to the call which just ended up with a Voicemail message but it does not seem to be happening. It actually gets more complicated since I can query the message store but I will not be able to tell which record is actually related to the notification which triggered my lookup action (no link). I cannot simply assume the first message on the listing is the same one which triggered the message store web-hook. For the same reason, I am not able to link back to the correct record from the call log and check on it's result when it actually becomes available (call log related to NoCall event has result = Voicemail at this point).
Since our API does not have a unique tracer key which can be used to identify/query Call Log and Message Store with currently, development is required to build up the logic to make these types of determinations using extensionId, from.phoneNumber, to.phoneNumber, and timestamps (which isn't the prettiest solution, but until such time as we release a tracer-key which correlates this data, it is the only way I know how to solve this problem).

Using your example from above...I created a subscription with eventFilters for presence (with detailedTelephonyState=true) and eventFilters for message-store. Then I made a call into that number and left a voicemail. Here are the events I received:

NEW INBOUND CALL IS RINGING THE EXTENSION'S PHONE NUMBER...

{ uuid: '61fa2f05-5679-4c57-a1d4-4b8fab2d8254',

  event: '/restapi/v1.0/account/133128004/extension/133165004/presence?detailedTelephonyState=true',

  timestamp: '2016-10-26T16:44:49.441Z',

  subscriptionId: 'a8d72076-a1ce-42b3-86d7-5835ce9df152',

  body: 

   { extensionId: 133165004,

     telephonyStatus: 'Ringing',

     activeCalls: 

      [ { id: '448115d0d81241fcaf4fc0d0b01c0ef9',

          direction: 'Inbound',

          from: '+14158905908',

          to: '+16505819987',

          telephonyStatus: 'Ringing',

          sessionId: '14860368004' },

        [length]: 1 ],

     sequence: 107945,

     presenceStatus: 'Available',

     userStatus: 'Available',

     dndStatus: 'DoNotAcceptDepartmentCalls',

     allowSeeMyPresence: true,

     ringOnMonitoredCall: false,

     pickUpCallsOnHold: false } }

RINGING HAS ENDED RESULTING IN 'NoCall' AND IS CONSIDERED FINAL...

{ uuid: 'e159d749-c8ef-48ac-984f-4a9954bc438a',

  event: '/restapi/v1.0/account/133128004/extension/133165004/presence?detailedTelephonyState=true',

  timestamp: '2016-10-26T16:45:09.434Z',

  subscriptionId: 'a8d72076-a1ce-42b3-86d7-5835ce9df152',

  body: 

   { extensionId: 133165004,

     telephonyStatus: 'NoCall',

     activeCalls: 

      [ { id: '448115d0d81241fcaf4fc0d0b01c0ef9',

          direction: 'Inbound',

          from: '+14158905908',

          to: '+16505819987',

          telephonyStatus: 'NoCall',

          sessionId: '14860368004',

          terminationType: 'final' },

        [length]: 1 ],

     sequence: 107946,

     presenceStatus: 'Available',

     userStatus: 'Available',

     dndStatus: 'DoNotAcceptDepartmentCalls',

     allowSeeMyPresence: true,

     ringOnMonitoredCall: false,

     pickUpCallsOnHold: false } }


1. You would want to subscribe to your extension's presence events (with detailedTelephonyStatus=true on the eventFilters) and message-store events...as you have done.

2. When NoCall events come through and voicemail has been engaged, we can see a new property on the 'activeCalls' object named "terminationType" and it is set to 'final'. The Call Log entry should be added within the next 5-10 minutes. This filtered event could trigger a request queue to fetch this data from Call Log (setting the query parameters: view=Detailed&dateFrom={{TIMESTAMP_OF_NOCALL_EVENT}}) to occur 10 minutes later. By setting the view to detailed we get to see each leg of the call, in this case 'FindMe' had a result of 'NoAnswer'. By setting the dateFrom to be the time of the 'NoCall' event timestamp, we should only receive call log data which has occurred in the appropriate timeframe. We can use the sessionId to match the NoCall event to the callLog record, in this case "14860368004".

The request URL: https://platform.devtest.ringcentral.com/restapi/v1.0/account/~/extension/~/call-log?view=Detailed&a...
The result should look like this:

{  "uri": "https://platform.devtest.ringcentral.com/restapi/v1.0/account/133128004/extension/133165004/call-log...;,
  "records": [
    {
      "uri": "https://platform.devtest.ringcentral.com/restapi/v1.0/account/133128004/extension/133165004/call-log...;,
      "id": "ATquN2QNuxfWhtk",
      "sessionId": "14860368004",
      "startTime": "2016-10-26T16:44:41.829Z",
      "duration": 100,
      "type": "Voice",
      "direction": "Inbound",
      "action": "Phone Call",
      "result": "Voicemail",
      "to": {
        "phoneNumber": "+1650581XXXX",
        "name": "Benno Dean"
      },
      "from": {
        "phoneNumber": "+14158905908",
        "name": "SAN FRANCSCO CA",
        "location": "San Francisco (Juniper), CA"
      },
      "transport": "PSTN",
      "lastModifiedTime": "2016-10-26T16:46:40.001Z",
      "legs": [
        {
          "startTime": "2016-10-26T16:44:41.829Z",
          "duration": 100,
          "type": "Voice",
          "direction": "Inbound",
          "action": "Phone Call",
          "result": "Voicemail",
          "to": {
            "phoneNumber": "+1650581XXXX",
            "name": "Benno Dean"
          },
          "from": {
            "phoneNumber": "+1415890XXXX",
            "name": "SAN FRANCSCO CA",
            "location": "San Francisco (Juniper), CA"
          },
          "transport": "PSTN",
          "legType": "Accept",
          "extension": {
            "uri": "https://platform.devtest.ringcentral.com/restapi/v1.0/account/133128004/extension/133165004";,
            "id": 133165004
          }
        },
        {
          "startTime": "2016-10-26T16:44:47.582Z",
          "duration": 21,
          "type": "Voice",
          "direction": "Outbound",
          "action": "FindMe",
          "result": "No Answer",
          "to": {
            "phoneNumber": "+1317600XXXX",
            "location": "Indianapolis, IN"
          },
          "from": {
            "phoneNumber": "+1415890XXXX",
            "name": "Benno Dean"
          },
          "transport": "PSTN",
          "legType": "FindMe",
          "extension": {
            "uri": "https://platform.devtest.ringcentral.com/restapi/v1.0/account/133128004/extension/133165004";,
            "id": 133165004
          }
        }
      ]
    }
  ],
  "paging": {
    "page": 1,
    "perPage": 100,
    "pageStart": 0,
    "pageEnd": 0
  },
  "navigation": {
    "firstPage": {
      "uri": "https://platform.devtest.ringcentral.com/restapi/v1.0/account/133128004/extension/133165004/call-log...;
    }
  }
}

3. If a caller leaves a voicemail for that extension, we will receive a subsequent message-store event of type "VoiceMail" when a voicemail has been created. You can wait for this event and see if extensionId matches and is within a reasonable amount of time delta from the NoCall event (max of 10 minutes should be reasonable). The extensionId value can be used to match the two events (although you are correct, the sessionId is not present in the message-store event).


CALLER LEFT A VOICEMAIL, NEW MESSAGE STORE EVENT RECEIVED...

{ uuid: 'bc8cfb3d-661e-4393-9210-f3ff5be73ebc',

  event: '/restapi/v1.0/account/133128004/extension/133165004/message-store',

  timestamp: '2016-10-26T16:46:29.437Z',

  subscriptionId: 'a8d72076-a1ce-42b3-86d7-5835ce9df152',

  body: 

   { extensionId: 133165004,

     lastUpdated: '2016-10-26T16:46:22.553+0000',

     changes: 

      [ { type: 'VoiceMail', newCount: 1, updatedCount: 0 },

        [length]: 1 ] } }


We can use this event to trigger a GET request to the event URI (message-store entry containing the voicemail), which will respond with the following type of data (below). We can use the from.phoneNumber and to.phoneNumber to match this data to the NoCall event we received.

[    {
      "uri": "https://platform.devtest.ringcentral.com/restapi/v1.0/account/133128004/extension/133165004/message-...;,
      "id": 2074108004,
      "to": [
        {
          "phoneNumber": "+1650581XXXX",
          "name": "(650) 581-9987 (Benno Dean)",
          "location": "San Mateo, CA"
        }
      ],
      "from": {
        "phoneNumber": "+1415890XXXX",
        "name": "SAN FRANCSCO CA"
      },
      "type": "VoiceMail",
      "creationTime": "2016-10-26T16:46:22.000Z",
      "readStatus": "Unread",
      "priority": "Normal",
      "attachments": [
        {
          "id": 2074108004,
          "uri": "https://media.devtest.ringcentral.com/restapi/v1.0/account/133128004/extension/133165004/message-sto...;,
          "type": "AudioRecording",
          "contentType": "audio/mpeg",
          "vmDuration": 58
        }
      ],
      "direction": "Inbound",
      "availability": "Alive",
      "messageStatus": "Received",
      "lastModifiedTime": "2016-10-26T16:46:22.553Z",
      "vmTranscriptionStatus": "NotAvailable"
    }
  ]


As far as answering your question about not being able to fetch the "recording" of a VoiceMail, I agree that can be confusing for developers, and here is why...

Call Recordings === Call Log.Recordings
VoiceMails === Message-Store.Entries of type "VoiceMail"

The disambiguation between Call Recordings and VoiceMail is:

Call Recordings are audio files which are created when a call is answered, and the agent either presses the "Record" button -or- the account has automatic call recording configured. Call recordings are ONLY saved when the length of the call recording > 30 seconds. These audio call recording files (and related metadata for these audio call recordings) are accessible from the Call Log API resource.

VoiceMails are audio files which are created when a call is NOT answered. These audio voice mail files (and related metadata) are accessible from the Message-Store API resource.


I know there is a lot to consume here, but after reading it...does all this make sense to you and answer your questions?
Benjamin, We came up with this solution yesterday and are implementing right now. It does make sense considering the limitations. Thanks for the detailed explanation - makes me a little more confident this is the right way to do it.
Glad I was able to help get you back to writing code. 🙂

Reply