Call supervision, monitoring and streaming

Last updated: 2024-01-08Contributors
Edit this page

RingCentral Call Monitoring allows a person to receive a real-time audio stream so they can listen in on a call. The primary use case is a supervisor wishing to monitor and provide feedback on an agent's performance.

The Call Supervise API allows a developer to connect to an active phone call and subscribe to an audio stream programatically. Some use cases for this API include:

  • To provide a real-time transcription of a call.
  • To use NLP and AI to assist agents in helping resolve cases faster.

Partners today have used this API to provide RingCentral customers with call assistants that provide their agents with real-time suggestions to help provide customers with rapid and accurate recommendations. This scenario is visualized below.

Current Call Supervise API Capabilities

  • One supervisor can supervise multiple active calls at the same time.
  • An active call can be supervised only by one supervisor at a time.
  • A supervisor can only supervise an active call on one device.
  • A supervisor can supervise:
    • All call parties on one audio stream.
    • Each call party on a separate audio stream.

Setup and Manage Call Monitoring Groups

Due to the sensitive nature of call monitoring, permissions to be monitored and to monitor others must be specifically given to extensions within an account. To manage these permissions, you must first create a Call Monitoring Group, and then add users/extensions to that group designating:

  • What extensions/agents can be monitored
  • What extensions/supervisors can monitor others

There are 2 options to create and manage call monitoring groups:

  1. Use the Admin Console. Follow the creating call monitoring groups steps
  2. Use the Call Monitoring API.

Create and manage call monitoring groups

This set of APIs allows you to programmatically create, modify or delete a call monitoring group.

API Endpoint Description
/restapi/v1.0/account/~/call-monitoring-groups  Create an empty call monitoring group. This endpoint will return a unique groupId, which can be used in other endpoints that required the group Id.
/restapi/v1.0/account/~/call-monitoring-groups/{groupId}/bulk-assign Add, update and remove group members from a call monitoring group identified by the groupId.
/restapi/v1.0/account/~/call-monitoring-groups/{groupId}  Update a call monitoring group name.
/restapi/v1.0/account/~/call-monitoring-groups/{groupId}  Delete a call monitoring group identified by the groupId.

The process of a call monitoring group creation using APIs consists of 2 steps.

  1. Create a call monitoring group, where you specify the group name and get back the newly created group id.
  2. Add group members with permissions to the group identified by the groupId returned by the creation API in step 1.

To create a list of group members to be added to a call monitoring group, you need to specify the extension id and the permissions of each member as a supervisor Monitoring or as an agent Monitored (remember that a member can be both as a supervisor and as an agent).

You can call the company extensions API to read the extensions and grab the extension id of an extension that you want to be a member of the call monitoring group.

The example below shows you how to create a new call monitoring group and add a list of members to the group. It creates a list of members by reading the company user extensions and add all admin users as supervisors and other users as agents. In reality, you can decide the size of your call monitoring groups and define supervisors and agents based on your own logic.

Sample codes

Running the code

  • Edit the variables in ALL CAPS with your app and user credentials before running the code.
  • If you run on your production account, remember to use the app credentials for production and change the RingCentral server URL to "https://platform.ringcentral.com"
const RC = require('@ringcentral/sdk').SDK

// Instantiate the SDK and get the platform instance
var rcsdk = new RC({
    server: 'https://platform.devtest.ringcentral.com',
    clientId: 'SANDBOX-APP-CLIENTID',
    clientSecret: 'SANDBOX-APP-CLIENTSECRET'
});
var platform = rcsdk.platform();

/* Authenticate a user using a personal JWT token */
platform.login({ jwt: 'SANDBOX-JWT' })

platform.on(platform.events.loginSuccess, () => {
    // Specify the name of a monitoring group that a supervisor can supervise
    create_call_monitoring_group("Demo group")
})

platform.on(platform.events.loginError, function(e){
    console.log("Unable to authenticate to platform. Check credentials.", e.message)
    process.exit(1)
});

/*
* Create a call monitoring group
*/
async function create_call_monitoring_group(groupName){
  try {
    let bodyParams = {
      'name': groupName
    }
    let endpoint = "/restapi/v1.0/account/~/call-monitoring-groups"
    let resp = await platform.post(endpoint, bodyParams)
    var jsonObj = await resp.json()
    console.log("Call monitoring group created:", jsonObj)
    add_call_monitoring_group_members(jsonObj.id)
  }catch (e){
    console.log("Unable to list call monitoring groups.", e.message)
  }
}

/*
* Add members to a call monitoring group
*/
async function add_call_monitoring_group_members(groupId){
  try {
    let newMembersList = await read_account_extensions()
    let bodyParams = {
      'addedExtensions': newMembersList
    }
    let endpoint = `/restapi/v1.0/account/~/call-monitoring-groups/${groupId}/bulk-assign`
    var resp = await platform.post(endpoint, bodyParams)
    console.log("Members added.")
  }catch (e){
    console.log("Unable to add mumbers to this call monitoring group.", e.message)
  }
}

/*
* Read the account user extensions and create a list of supervisors and agents based on their role.
*/
async function read_account_extensions(){
  try {
    let queryParams = {
      'type': ['User'],
      'status': ["Enabled"]
    }
    let endpoint = "/restapi/v1.0/account/~/extension"
    let resp = await platform.get(endpoint, queryParams)
    var jsonObj = await resp.json()
    var extensionList = []
    for (var user of jsonObj.records){
      var extension = {
        id: user.id,
        permissions: []
      }
      if (user.permissions.admin.enabled == true)
        extension.permissions.push("Monitoring")
      else
        extension.permissions.push("Monitored")
      extensionList.push(extension)
    }
    return extensionList
  }catch (e){
    console.log("Unable to read account extensions.", e.message)
  }
}

/*
* Delete call monitoring group
*/
async function delete_call_monitoring_group(groupId){
  try {
    let endpoint = `/restapi/v1.0/account/~/call-monitoring-groups/${groupId}`
    var resp = await platform.delete(endpoint)
    console.log("Call monitoring group is deleted")
  }catch (e){
    console.log("Unable to delete this call monitoring group.", e.message)
  }
}
from ringcentral import SDK
import json

#
# Create a call monitoring group
#
def create_call_monitoring_group(groupName):
    try:
        endpoint = "/restapi/v1.0/account/~/call-monitoring-groups"
        bodyParams = {
            'name': groupName
        }
        resp = platform.post(endpoint, bodyParams)
        jsonObj = resp.json()
        print (f'Call monitoring group created: {jsonObj.name}')
        add_call_monitoring_group_members(jsonObj.id)
    except Exception as e:
      print ("Unable to call list call monitoring groups." + str(e))

#
# Add members to a call monitoring group
#
def add_call_monitoring_group_members(groupId):
    try:
        newMembersList = read_account_extensions()
        bodyParams = {
            'addedExtensions': newMembersList
        }
        endpoint = f'/restapi/v1.0/account/~/call-monitoring-groups/{groupId}/bulk-assign'
        resp = platform.post(endpoint, bodyParams)
        print ("Members added")
    except Exception as e:
        print ("Unable to read members of this call monitoring group." + str(e))

#
# Read the account user extensions and create a list of supervisors and agents based on their role.
#
def read_account_extensions():
    try:
        queryParams = {
            'type': ["User"],
            'status' : "Enabled"
        }
        endpoint = '/restapi/v1.0/account/~/extension'
        resp = platform.get(endpoint, queryParams)
        jsonObj = resp.json()
        extensionList = []
        for user in jsonObj.records:
            extension = {
                'id': user.id,
                'permissions': []
              }
            if user.permissions.admin.enabled == True:
                extension['permissions'].append("Monitoring")
            else:
                extension['permissions'].append("Monitored")
            extensionList.append(extension)
        return extensionList
    except Exception as e:
        print ("Unable to read company extensions." + str(e))

#
# Delete a call monitoring group
#
def delete_call_monitoring_group(groupId):
    try:
        endpoint = f'/restapi/v1.0/account/~/call-monitoring-groups/{groupId}'
        resp = platform.delete(endpoint)
        print ("Call monitoring group deleted.")
    except Exception as e:
        print ("Unable to delete this call monitoring group." + str(e))

# Authenticate a user using a personal JWT token
def login():
  try:
      platform.login( jwt= "SANDBOX-JWT" )
      create_call_monitoring_group("Demo Group - Python")
  except Exception as e:
      print ("Unable to authenticate to platform. Check credentials. " + str(e))

# Instantiate the SDK and get the platform instance
rcsdk = SDK("SANDBOX-APP-CLIENTID", "SANDBOX-APP-CLIENTSECRET", "https://platform.devtest.ringcentral.com")
platform = rcsdk.platform()

login()
<?php
require('vendor/autoload.php');

// Instantiate the SDK and get the platform instance
$rcsdk = new RingCentral\SDK\SDK( 'SANDBOX-APP-CLIENTID', 'SANDBOX-APP-CLIENTSECRET', 'https://platform.devtest.ringcentral.com' );
$platform = $rcsdk->platform();

/* Authenticate a user using a personal JWT token */
$platform->login(["jwt" => 'SANDBOX-JWT']);
create_call_monitoring_group("Demo Group - PHP");

/*
* Read all call monitoring groups
*/
function create_call_monitoring_group($groupName){
  global $platform;
  try{
    $bodyParams = array (
      'name' => $groupName
    );
    $endpoint = "/restapi/v1.0/account/~/call-monitoring-groups";
    $resp = $platform->post($endpoint, $bodyParams);
    $jsonObj = $resp->json();
    print ("Call monitoring group created." . PHP_EOL);
    add_call_monitoring_group_members($jsonObj->id);
  }catch (\RingCentral\SDK\Http\ApiException $e) {
    print_r ("Unable to create a call monitoring group. " . $e->getMessage() . PHP_EOL);
  }
}

/*
* Add members to a call monitoring group
*/
function add_call_monitoring_group_members($groupId){
  global $platform;
  try {
    $newMembersList = read_account_extensions();
    $bodyParams = array (
      'addedExtensions' => $newMembersList
    );
    $endpoint = "/restapi/v1.0/account/~/call-monitoring-groups/" . $groupId ."/bulk-assign";
    $resp = $platform->post($endpoint, $bodyParams);
    print ("Members added." . PHP_EOL);
  }catch (\RingCentral\SDK\Http\ApiException $e) {
    print_r ("Unable to add members to this call monitoring group. " . $e->getMessage() . PHP_EOL);
  }
}

/*
* Read the account user extensions and create a list of supervisors and agents based on their role.
*/
function read_account_extensions(){
  global $platform;
  try {
    $queryParams = array (
      'type' => array ('User'),
      'status' => "Enabled"
    );
    $endpoint = "/restapi/v1.0/account/~/extension";
    $resp = $platform->get($endpoint, $queryParams);
    $jsonObj = $resp->json();
    $extensionList = [];
    foreach ($jsonObj->records as $user){
      $extension = array (
        'id' => $user->id,
        'permissions' => []
      );
      if ($user->permissions->admin->enabled == true)
        array_push($extension['permissions'], "Monitoring");
      else
        array_push($extension['permissions'], "Monitored");
      array_push($extensionList, $extension);
    }
    return $extensionList;
  }catch (\RingCentral\SDK\Http\ApiException $e) {
    print ("Unable to read account extensions." .$e->getMessage . PHP_EOL);
  }
}

/*
* Delete a call monitoring group
*/
function delete_call_monitoring_group($groupId){
  global $platform;
  try {
    $endpoint = "/restapi/v1.0/account/~/call-monitoring-groups/" . $groupId;
    $resp = $platform->delete($endpoint);
    print ("Call monitoring group is deleted." . PHP_EOL);
  }catch (\RingCentral\SDK\Http\ApiException $e) {
    print_r ("Unable to delete this call monitoring group. " . $e->getMessage() . PHP_EOL);
  }
}
?>
require 'ringcentral'
require 'json'

#
# Create a call monitoring group
#
def create_call_monitoring_group(groupName)
    begin
        bodyParams = {
            'name': groupName
        }
        endpoint = "/restapi/v1.0/account/~/call-monitoring-groups"
        resp = $platform.post(endpoint, payload: bodyParams)
        jsonObj = resp.body
        puts ("Call monitoring group created: " + jsonObj['name'] + "/" + jsonObj['id'])
        add_call_monitoring_group_members(jsonObj['id'])
    rescue StandardError => e
      puts ("Unable to create a call monitoring groups." + e.to_s)
    end
end

#
# Add members to a call monitoring group
#
def add_call_monitoring_group_members(groupId)
    begin
        newMembersList = read_account_extensions()
        bodyParams = {
            'addedExtensions': newMembersList
        }
        endpoint = "/restapi/v1.0/account/~/call-monitoring-groups/" + groupId + "/bulk-assign"
        resp = $platform.post(endpoint, payload: bodyParams)
        jsonObj = resp.body
        puts ("Members added")
    rescue StandardError => e
        puts ("Unable to read members of this call monitoring group." + e.to_s)
    end
end

#
# Read the account user extensions and create a list of supervisors and agents based on their role.
#
def read_account_extensions()
    begin
        queryParams = {
            'type': ["User"],
            'status': "Enabled"
        }
        endpoint = '/restapi/v1.0/account/~/extension'
        resp = $platform.get(endpoint, queryParams)
        jsonObj = resp.body
        extensionList = []
        for user in jsonObj['records'] do
            extension = {
                'id': user['id'],
                'permissions': []
              }
            if user['permissions']['admin']['enabled'] == true
                extension['permissions'] = ["Monitoring"]
            else
                extension['permissions'] = ["Monitored"]
            end
            extensionList << extension
        end
        return extensionList
    rescue StandardError => e
        puts ("Unable to read company extensions." + e.to_s)
    end
end

#
# Delete a call monitoring group
#
def delete_call_monitoring_group(groupId)
    begin
        endpoint = "/restapi/v1.0/account/~/call-monitoring-groups/" + groupId
        resp = $platform.delete(endpoint)
        puts ("Call monitoring group deleted")
    rescue StandardError => e
        puts ("Unable to delete this call monitoring group." + e.to_s)
    end
end


# Authenticate a user using a personal JWT token
def login()
  begin
    $platform.authorize( jwt: "SANDBOX-JWT" )
    create_call_monitoring_group("Demo Group - Ruby")
  rescue StandardError => e
    puts ("Unable to authenticate to platform. Check credentials. " + e.to_s)
  end
end

# Instantiate the SDK and get the platform instance
$platform = RingCentral.new( "SANDBOX-APP-CLIENTID", "SANDBOX-APP-CLIENTSECRET", "https://platform.devtest.ringcentral.com" )

login()
using System;
using System.IO;
using System.Threading.Tasks;
using System.Collections.Generic;
using RingCentral;
using Newtonsoft.Json;

namespace CreateAndManageCallMonitoringGroup {
  class Program {
    static RestClient restClient;
    static async Task Main(string[] args)
    {
      try
      {
        // Instantiate the SDK
        restClient = new RestClient("SANDBOX-APP-CLIENT-ID", "SANDBOX-APP-CLIENT-SECRET", "https://platform.devtest.ringcentral.com");

        // Authenticate a user using a personal JWT token
        await restClient.Authorize("SANDBOX-JWT");

        await create_call_monitoring_group("Demo Group - C#");
      }
      catch (Exception ex)
      {
        Console.WriteLine("Unable to authenticate to platform. Check credentials. " + ex.Message);
      }
    }
    /*
    * Create a call monitoring group
    */
    static private async Task create_call_monitoring_group(String groupName)
    {
      try
      {
        var bodyParams = new CreateCallMonitoringGroupRequest();
        bodyParams.name = groupName;
        var resp = await restClient.Restapi().Account().CallMonitoringGroups().Post(bodyParams);
        Console.WriteLine($"Call monitoring group created. Group id/name: {resp.id} / {resp.name}");
        await add_call_monitoring_group_members(resp.id);
      }
      catch (Exception ex)
      {
        Console.WriteLine($"Unable to list call monitoring groups. {ex.Message}");
      }
    }
    /*
    * Add members to a call monitoring group
    */
    static private async Task add_call_monitoring_group_members(String groupId)
    {
      try
      {
        var newMembersList = await read_account_extensions();
        var bodyParams = new CallMonitoringBulkAssign();
        bodyParams.addedExtensions = newMembersList;
        var resp = await restClient.Restapi().Account().CallMonitoringGroups(groupId).BulkAssign().Post(bodyParams);
        Console.WriteLine("Members added.");
      }
      catch (Exception ex)
      {
        Console.WriteLine($"Unable to add mumbers to this call monitoring group. {ex.Message}");
      }
    }
    /*
    * Read the account user extensions and create a list of supervisors and agents based on their role.
    */
    static private async Task<CallMonitoringExtensionInfo[]> read_account_extensions()
    {
      List<CallMonitoringExtensionInfo> extensionList = new List<CallMonitoringExtensionInfo>();
      var queryParams = new ListExtensionsParameters();
      queryParams.type = new String[] { "User" };
      queryParams.status = new String[] { "Enabled" };
      try
      {
        var resp = await restClient.Restapi().Account().Extension().List(queryParams);
        foreach (var record in resp.records)
        {
          CallMonitoringExtensionInfo ext = new CallMonitoringExtensionInfo();
          ext.id = record.id.ToString();
          if (record.permissions.admin.enabled == true)
          {
            ext.permissions = new String[] { "Monitoring" };
          }
          else
          {
            ext.permissions = new String[] { "Monitored" };
          }
          extensionList.Add (ext);
        }
      }
      catch (Exception ex)
      {
        Console.WriteLine($"Unable to read account extensions. {ex.Message}");
      }
      return extensionList.ToArray();
    }
    /*
    * Delete call monitoring group
    */
    static private async Task delete_call_monitoring_group(String groupId)
    {
      try
      {
        var resp = await restClient.Restapi().Account().CallMonitoringGroups(groupId).Delete();
        Console.WriteLine("Call monitoring group deleted.");
      }
      catch (Exception ex)
      {
        Console.WriteLine($"Unable to delete this call monitoring group. {ex.Message}");
      }
    }
  }
}
package CreateAndManageCallMonitoringGroup;

import java.io.IOException;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;

import com.ringcentral.*;
import com.ringcentral.definitions.*;

public class CreateAndManageCallMonitoringGroup {
  static RestClient restClient;

  public static void main(String[] args) {
    var obj = new CreateAndManageCallMonitoringGroup();
    try {
      // Instantiate the SDK
      restClient = new RestClient("SANDBOX-APP-CLIENT-ID", "SANDBOX-APP-CLIENT-SECRET", "https://platform.devtest.ringcentral.com");

      // Authenticate a user using a personal JWT token
      restClient.authorize("SANDBOX-JWT");

      obj.create_call_monitoring_group("Demo Group - Java");

    } catch (RestException e) {
      System.out.println(e.getMessage());
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  /*
  * Create a call monitoring group
  */
  public void create_call_monitoring_group(String groupName) throws RestException, IOException {
    try {
      var bodyParams = new CreateCallMonitoringGroupRequest();
      bodyParams.name = groupName;
      var resp = restClient.restapi().account().callMonitoringGroups().post(bodyParams);
      System.out.println("Call monitoring group created. Group id/name:" + resp.id + "/" + resp.name);
      add_call_monitoring_group_members(resp.id);
    } catch (RestException ex) {
      System.out.println("Unable to list call monitoring groups. " + ex.getMessage());
    }
  }
  /*
  * Add members to a call monitoring group
  */
  public void add_call_monitoring_group_members(String groupId) throws RestException, IOException {
    try {
      var newMembersList = read_account_extensions();
      var bodyParams = new CallMonitoringBulkAssign();
      bodyParams.addedExtensions = newMembersList;
      restClient.restapi().account().callMonitoringGroups(groupId).bulkAssign().post(bodyParams);
      System.out.println("Members added.");
    } catch (RestException ex) {
      System.out.println("Unable to add mumbers to this call monitoring group. " + ex.getMessage());
    }
  }
  /*
  * Read the account user extensions and create a list of supervisors and agents based on their role.
  */
  public CallMonitoringExtensionInfo[] read_account_extensions() throws RestException, IOException {
    ArrayList<CallMonitoringExtensionInfo> extensionList = new ArrayList<>();
    try {
      var queryParams = new ListExtensionsParameters();
      queryParams.type = new String[] { "User" };
      queryParams.status = new String[] { "Enabled" };
      var resp = restClient.restapi().account().extension().list(queryParams);
      for (var record : resp.records) {
        CallMonitoringExtensionInfo ext = new CallMonitoringExtensionInfo();
        ext.id = record.id.toString();
        if (record.permissions.admin.enabled == true)
        {
          ext.permissions = new String[] { "Monitoring" };
        }
        else
        {
          ext.permissions = new String[] { "Monitored" };
        }

        extensionList.add (ext);
      }
    } catch (RestException ex) {
      System.out.println("Unable to read account extensions. " + ex.getMessage());
    }
    CallMonitoringExtensionInfo members[]= extensionList.toArray(new CallMonitoringExtensionInfo[extensionList.size()]);
    return members;
  }
  /*
  * Delete call monitoring group
  */
  public void delete_call_monitoring_group(String groupId) throws RestException, IOException {
    try {
      restClient.restapi().account().callMonitoringGroups(groupId).delete();
      System.out.println("Call monitoring group deleted.");
    } catch (RestException ex) {
      System.out.println("Unable to delete this call monitoring group. " + ex.getMessage());
    }
  }
}

Read call monitoring groups and group members

These APIs allows you to query all call monitoring groups under your RingCentral account and read the members of a particular call monitoring group. You can identify the agents and the supervisors by checking the permissions of each member as Monitored or Monitoring permission, respectively.

API Endpoint Description
/restapi/v1.0/account/~/call-monitoring-groups  List all created call monitoring groups. This endpoint returns just the group names and group ids.
/restapi/v1.0/account/~/call-monitoring-groups/{groupId}/members List members of a call monitoring group identified by the groupId.

A call supervision application should use these APIs to detect the supervisors (monitoring extensions) and the agents (monitored extensions) and use the id of a supervisor to detect a supervisor device and use the id of the agents to fetch agents' active calls.

The example blow shows you how to read all call monitoring groups under an account and list all the members of each group.

Sample codes

Running the code

  • Edit the variables in ALL CAPS with your app and user credentials before running the code.
  • If you run on your production account, remember to use the app credentials for production and change the RingCentral server URL to "https://platform.ringcentral.com"
const RC = require('@ringcentral/sdk').SDK

// Instantiate the SDK and get the platform instance
var rcsdk = new RC({
    server: 'https://platform.devtest.ringcentral.com',
    clientId: 'SANDBOX-APP-CLIENTID',
    clientSecret: 'SANDBOX-APP-CLIENTSECRET'
});
var platform = rcsdk.platform();

/* Authenticate a user using a personal JWT token */
platform.login({ jwt: 'SANDBOX-JWT' })

platform.on(platform.events.loginSuccess, () => {
  // Specify the name of a monitoring group that a supervisor can supervise
    read_call_monitoring_groups()
})

platform.on(platform.events.loginError, function(e){
    console.log("Unable to authenticate to platform. Check credentials.", e.message)
    process.exit(1)
});

/*
* Read all call monitoring groups
*/
async function read_call_monitoring_groups(){
  try {
    let endpoint = "/restapi/v1.0/account/~/call-monitoring-groups"
    let resp = await platform.get(endpoint)
    var jsonObj = await resp.json()
    for (var group of jsonObj.records){
      console.log(`Call monitoring group name: ${group.name}`)
      await read_call_monitoring_group_members(group.id)
    }
  }catch (e){
    console.log("Unable to list call monitoring groups.", e.message)
  }
}

/*
* Read a call monitoring group members
*/
async function read_call_monitoring_group_members(groupId){
  try {
    let endpoint = `/restapi/v1.0/account/~/call-monitoring-groups/${groupId}/members`
    var resp = await platform.get(endpoint)
    var jsonObj = await resp.json()
    console.log("Call monitoring group members:")
    for (var member of jsonObj.records){
      console.log(member)
    }
  }catch (e){
    console.log("Unable to read members of this call monitoring group.", e.message)
  }
}
from ringcentral import SDK
import json

#
# Read all call monitoring groups
#
def read_call_monitoring_groups():
    try:
        endpoint = "/restapi/v1.0/account/~/call-monitoring-groups"
        resp = platform.get(endpoint)
        jsonObj = resp.json()
        for group in jsonObj.records:
            print (f'Call monitoring group name: {group.name} / {group.id}')
            read_call_monitoring_group_members(group.id)
    except Exception as e:
      print ("Unable to call list call monitoring groups." + str(e))

#
# Read a call monitoring group members
#
def read_call_monitoring_group_members(groupId):
    try:
        endpoint = f'/restapi/v1.0/account/~/call-monitoring-groups/{groupId}/members'
        resp = platform.get(endpoint)
        jsonObj = resp.json_dict()
        print ("Call monitoring group members:")
        for member in jsonObj['records']:
            print (json.dumps(member, indent=2, sort_keys=True))
    except Exception as e:
        print ("Unable to read members of this call monitoring group." + str(e))

# Authenticate a user using a personal JWT token
def login():
  try:
      platform.login( jwt= "SANDBOX-JWT" )
      read_call_monitoring_groups()
  except Exception as e:
      print ("Unable to authenticate to platform. Check credentials. " + str(e))

# Instantiate the SDK and get the platform instance
rcsdk = SDK("SANDBOX-APP-CLIENTID", "SANDBOX-APP-CLIENTSECRET", "https://platform.devtest.ringcentral.com")
platform = rcsdk.platform()

login()
<?php
require('vendor/autoload.php');

// Instantiate the SDK and get the platform instance
$rcsdk = new RingCentral\SDK\SDK( 'SANDBOX-APP-CLIENTID', 'SANDBOX-APP-CLIENTSECRET', 'https://platform.devtest.ringcentral.com' );
$platform = $rcsdk->platform();

/* Authenticate a user using a personal JWT token */
$platform->login(["jwt" => 'SANDBOX-JWT']);
read_call_monitoring_groups();

/*
* Read all call monitoring groups
*/
function read_call_monitoring_groups(){
  global $platform;
  try{
    $endpoint = "/restapi/v1.0/account/~/call-monitoring-groups";
    $resp = $platform->get($endpoint);
    $jsonObj = $resp->json();
    foreach ($jsonObj->records as $group){
      print ("Call monitoring group name: " . $group->name . "/" . $group->id . PHP_EOL);
      read_call_monitoring_group_members($group->id);
    }
  }catch (\RingCentral\SDK\Http\ApiException $e) {
    print_r ("Unable to call list call monitoring groups. " . $e->getMessage() . PHP_EOL);
  }
}

/*
* Read a call monitoring group members
*/
function read_call_monitoring_group_members($groupId){
  global $platform;
  try {
    $endpoint = "/restapi/v1.0/account/~/call-monitoring-groups/" . $groupId ."/members";
    $resp = $platform->get($endpoint);
    $jsonObj = $resp->json();
    print ("Call monitoring group members:" . PHP_EOL);
    foreach ($jsonObj->records as $member){
      print (json_encode($member, JSON_PRETTY_PRINT) . PHP_EOL);
    }
  }catch (\RingCentral\SDK\Http\ApiException $e) {
    print_r ("Unable to read members of this call monitoring group. " . $e->getMessage() . PHP_EOL);
  }
}
?>
require 'ringcentral'
require 'json'

#
# Read all call monitoring groups
#
def read_call_monitoring_groups()
    begin
        endpoint = "/restapi/v1.0/account/~/call-monitoring-groups"
        resp = $platform.get(endpoint)
        jsonObj = resp.body
        for group in jsonObj['records'] do
            puts ("Call monitoring group name: " + group['name'] + "/"+ group['id'])
            read_call_monitoring_group_members(group['id'])
        end
    rescue StandardError => e
      puts ("Unable to call list call monitoring groups." + e.to_s)
    end
end
#
# Read a call monitoring group members
#
def read_call_monitoring_group_members(groupId)
    begin
        endpoint = "/restapi/v1.0/account/~/call-monitoring-groups/" + groupId + "/members"
        resp = $platform.get(endpoint)
        jsonObj = resp.body
        puts ("Call monitoring group members:")
        for member in jsonObj['records'] do
            puts JSON.pretty_generate(member)
        end
    rescue StandardError => e
        puts ("Unable to read members of this call monitoring group." + e.to_s)
    end
end


# Authenticate a user using a personal JWT token
def login()
  begin
    $platform.authorize( jwt: "SANDBOX-JWT" )
    read_call_monitoring_groups()
  rescue StandardError => e
    puts ("Unable to authenticate to platform. Check credentials. " + e.to_s)
  end
end

# Instantiate the SDK and get the platform instance
$platform = RingCentral.new( "SANDBOX-APP-CLIENTID", "SANDBOX-APP-CLIENTSECRET", "https://platform.devtest.ringcentral.com" )

login()
using System;
using System.IO;
using System.Threading.Tasks;
using System.Collections.Generic;
using RingCentral;
using Newtonsoft.Json;

namespace CallMonitoringGroup {
  class Program {
    static RestClient restClient;
    static async Task Main(string[] args)
    {
      try
      {
        // Instantiate the SDK
        restClient = new RestClient("SANDBOX-APP-CLIENT-ID", "SANDBOX-APP-CLIENT-SECRET", "https://platform.devtest.ringcentral.com");

        // Authenticate a user using a personal JWT token
        await restClient.Authorize("SANDBOX-JWT");

        await read_call_monitoring_groups();
      }
      catch (Exception ex)
      {
        Console.WriteLine("Unable to authenticate to platform. Check credentials. " + ex.Message);
      }
    }
    /*
    * Read all call monitoring groups
    */
    static private async Task read_call_monitoring_groups()
    {
      try
      {
        var resp = await restClient.Restapi().Account().CallMonitoringGroups().Get();
        foreach (var group in resp.records)
        {
          Console.WriteLine($"Call monitoring group name/id: {group.name}/{group.id}");
          await read_call_monitoring_group_members(group.id);
        }
      }
      catch (Exception ex)
      {
        Console.WriteLine($"Unable to list call monitoring groups. {ex.Message}");
      }
    }
    /*
    * Read a call monitoring group members
    */
    static private async Task read_call_monitoring_group_members(String groupId)
    {
      try
      {
        var resp = await restClient.Restapi().Account().CallMonitoringGroups(groupId).Members().Get();
        Console.WriteLine("Call monitoring group members:");
        foreach (var member in resp.records)
        {
          Console.WriteLine(JsonConvert.SerializeObject(member));
        }
      }
      catch (Exception ex)
      {
        Console.WriteLine($"Unable to read members of this call monitoring group. {ex.Message}");
      }
    }
  }
}
package CallMonitoringGroup;

import java.io.IOException;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;

import com.ringcentral.*;
import com.ringcentral.definitions.*;

public class CallMonitoringGroup {
  static RestClient restClient;

  public static void main(String[] args) {
    var obj = new CallMonitoringGroup();
    try {
      // Instantiate the SDK
      restClient = new RestClient("SANDBOX-APP-CLIENT-ID", "SANDBOX-APP-CLIENT-SECRET", "https://platform.devtest.ringcentral.com");

      // Authenticate a user using a personal JWT token
      restClient.authorize("SANDBOX-JWT");

      obj.read_call_monitoring_groups();

    } catch (RestException e) {
      System.out.println(e.getMessage());
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  /*
  * Read all call monitoring groups
  */
  public void read_call_monitoring_groups() throws RestException, IOException {
    try {
      var resp = restClient.restapi().account().callMonitoringGroups().get();
      for (var group : resp.records)
      {
        System.out.println("Call monitoring group name/id: " + group.name + "/" + group.id);
        read_call_monitoring_group_members(group.id);
      }
    } catch (RestException ex) {
      System.out.println("Unable to list call monitoring groups. " + ex.getMessage());
    }
  }
  /*
  * Read a call monitoring group members
  */
  public void read_call_monitoring_group_members(String groupId) throws RestException, IOException {
    try {
      var resp = restClient.restapi().account().callMonitoringGroups(groupId).members().get();
      System.out.println("Call monitoring group members:");
      for (var member : resp.records)
      {
        String jsonStr = new Gson().toJson(member, new TypeToken<Object>(){}.getType());
        System.out.println(jsonStr);
      }

    } catch (RestException ex) {
      System.out.println("Unable to read members of this call monitoring group. " + ex.getMessage());
    }
  }
}

Using the Supervision API

Once a Call Monitoring group has been configured, you can use one of the Call Supervision APIs below to supervise phone calls:

  • Supervise Call Session: This method allows a supervisor to listen to both caller and callee parties from a single audio stream. Thus, if you feed the audio data to a real-time transcription you will not be able to identify the speakers. But if you want to save the audio to a file, this will be simple as you can just write the audio data to a file.
  • Supervise Call Party: This method allows a supervisor to decide which call party to supervise. If you want to listen to both call parties from different audio streams, you will need to call the API twice with a correct party Id. Eventually, you will get 2 separate audio streams. Thus, you can feed the audio data to a real-time transcription per audio channel and this will help you to identify the speakers precisely. However, if you want to save both audio streams to a single file, you must implement extra code to merge two audio channels into one channel, or combine separate audio streams into a single multichannel before writing the audio data to a file.

Path Parameters

Parameter Description
telephonySessionId The unique telephony session id of an active call.
partyId The unique id of a call party. This path parameter is required for the Supervise Call Party API.

Jump to this section for how to detect the telephonySessionId and the partyId of an active call.

Required Body Parameters

Parameter Description
mode Currently, the only supported mode is Listen.
supervisorDeviceId The device Id of the supervisor's phone device.
agentExtensionId The extension id of the agent whose call you can monitor. Jump to this section.

How to find a supervisor device Id

Provided that you have setup a phone device for a supervisor extension using the account admin portal. You can learn how to setup a third-party device for the RingCentral account.

To retrieve the supervisor's devices, authorize your app with the supervisor login credentials and call the List Extension Devices API. Parse the device info object under the records list and grab the device id of a device you want to use for the call supervision.

Notes

  • The device status must be "Online" in order to use it!
  • Currently, the device Ids of the RC (Desktop and Web based) app and a WebRTC phone are dynamic and thus, they can not be detected by the API above!
{
"records": [
        {
           "uri":"https://platform.ringcentral.com/restapi/v1.0/account/80964xxxx/device/60727004",
           "id":"60727004",
           "type":"SoftPhone",
           "sku":"DV-1",
           "name":"Softphone - Digital Line",
           "serial":"LMRC8531",
           "computerName":"LMRC8531",
           "status":"Online",
           "extension":{
              "uri":"https://platform.ringcentral.com/restapi/v1.0/account/80964xxxx/extension/80964xxxx",
              "id":80964xxxx,
              "extensionNumber":"102"
           }
        },
        { ... }
    ]
}

If you want to build your own soft phone using the RingCentral soft phone SDK, jump to this section.

How to get a call telephony session Id and party Ids

You will need the telephonySessionId and the partyId (in case of supervising a call party) of an active call. There are several methods to detect user extensions' active calls.

Out of all the methods mentioned above, the most effective way to detect active calls in real-time is to use the Telephony Session Events Notifications method. This method not only helps you effectively detect active calls of all monitored extensions specified in the call monitoring groups, but it also gives the call status that tells you when you can start to supervise an active call.

To avoid receiving redundant telephony events for user extensions that are not under the "Can be monitored" list of a call monitoring group, you should subscribe for telephony session events for just the user extensions specified in the call monitoring group configurations. Attempting to make supervision calls for users you do not have the permission to supervise will result in errors.

The APIs listed above may not give you the other call party partyId. Thus, if you intend to supervise all call parties using the Supervise Call Party API, you will need both caller's and callee's partyId. In that case, you can call the Get Call Session Status API to get each call party status and the partyId.

Call the Supervise Call Session API

Assume that an agent has an active call and the supervisorDeviceId (e.g. the device id of the RingCentral soft-phone or a desk phone) and the agentExtensionId have been detected, the following example shows how to grab the call's telephonySessionId and call the supervise API to supervise a call session.

Sample codes

Running the code

  • Edit the variables in ALL CAPS with your app and user credentials before running the code.
  • If you run on your production account, remember to use the app credentials for production and change the RingCentral server URL to "https://platform.ringcentral.com"
const RC = require('@ringcentral/sdk').SDK

// Instantiate the SDK and get the platform instance
var rcsdk = new RC({
    server: 'https://platform.devtest.ringcentral.com',
    clientId: 'SANDBOX-APP-CLIENTID',
    clientSecret: 'SANDBOX-APP-CLIENTSECRET'
});
var platform = rcsdk.platform();

/* Authenticate a user using a personal JWT token */
platform.login({ jwt: 'SANDBOX-JWT' })

platform.on(platform.events.loginSuccess, async () => {
    let supervisorDeviceId = "TEST-SUPERVISOR-DEVICEID"
    let agentExtensionId = "TEST-AGENT-EXTENSIONID"
    await read_agent_active_calls(agentExtensionId, supervisorDeviceId)
})

platform.on(platform.events.loginError, function(e){
    console.log("Unable to authenticate to platform. Check credentials.", e.message)
    process.exit(1)
});

/*
* Read agent active calls
*/

async function read_agent_active_calls(agentExtensionId, supervisorDeviceId){
  try{
    let endpoint = `/restapi/v1.0/account/~/extension/${agentExtensionId}/active-calls`
    let resp = await platform.get(endpoint)
    let jsonObj = await resp.json()
    for (var record of jsonObj.records){
      if (record.result == "In Progress"){
        submit_call_supervise_request(record.telephonySessionId, agentExtensionId, supervisorDeviceId)
        break
      }
    }
  }catch(e) {
    console.log("Unable to read agent's active calls.", e.message)
  }
}

/*
* Supervise an active call session
*/
async function submit_call_supervise_request(telephonySessionId, agentExtensionId, supervisorDeviceId){
  try{
    let endpoint = `/restapi/v1.0/account/~/telephony/sessions/${telephonySessionId}/supervise`
    var bodyParams = {
            mode: 'Listen',
            supervisorDeviceId: supervisorDeviceId,
            agentExtensionId: agentExtensionId
          }
    let resp = await platform.post(endpoint, bodyParams)
    let jsonObj = await resp.json()
    console.log(jsonObj)
  }catch(e) {
    console.log("Unable to supervise this call.", e.message)
  }
}
from ringcentral import SDK
import json

#
# Read agent active calls
#
def read_agent_active_calls(agentExtensionId, supervisorDeviceId):
    try:
        endpoint = f'/restapi/v1.0/account/~/extension/{agentExtensionId}/active-calls'
        resp = platform.get(endpoint)
        jsonObj = resp.json()
        for record in jsonObj.records:
            if record.result == "In Progress":
                submit_call_supervise_request(record.telephonySessionId, agentExtensionId, supervisorDeviceId)
                break
    except Exception as e:
        print ("Unable to read agent's active calls. " + str(e))


#
# Supervise an active call session
#
def submit_call_supervise_request(telephonySessionId, agentExtensionId, supervisorDeviceId):
    try:
        endpoint = f'/restapi/v1.0/account/~/telephony/sessions/{telephonySessionId}/supervise'
        bodyParams = {
            'mode': 'Listen',
            'supervisorDeviceId': supervisorDeviceId,
            'agentExtensionId': agentExtensionId
          }
        resp = platform.post(endpoint, bodyParams)
        jsonObj = resp.json_dict()
        print(json.dumps(jsonObj, indent=2, sort_keys=True))
    except Exception as e:
      print ("Unable to supervise this call. " + str(e))


# Authenticate a user using a personal JWT token
def login():
  try:
      platform.login( jwt= "SANDBOX-JWT" )

      supervisorDeviceId = "TEST-SUPERVISOR-DEVICEID"
      agentExtensionId = "TEST-AGENT-EXTENSIONID"
      read_agent_active_calls(agentExtensionId, supervisorDeviceId)
  except Exception as e:
      print ("Unable to authenticate to platform. Check credentials. " + str(e))

# Instantiate the SDK and get the platform instance
rcsdk = SDK("SANDBOX-APP-CLIENTID", "SANDBOX-APP-CLIENTSECRET", "https://platform.devtest.ringcentral.com")
platform = rcsdk.platform()

login()
<?php
require('vendor/autoload.php');

// Instantiate the SDK and get the platform instance
$rcsdk = new RingCentral\SDK\SDK( 'SANDBOX-APP-CLIENTID', 'SANDBOX-APP-CLIENTSECRET', 'https://platform.devtest.ringcentral.com' );
$platform = $rcsdk->platform();

/* Authenticate a user using a personal JWT token */
$platform->login(["jwt" => 'SANDBOX-JWT']);
$supervisorDeviceId = "TEST-SUPERVISOR-DEVICEID";
$agentExtensionId = "TEST-AGENT-EXTENSIONID";

read_agent_active_calls($agentExtensionId, $supervisorDeviceId);

/*
* Read agent active calls
*/
function read_agent_active_calls($agentExtensionId, $supervisorDeviceId){
  global $platform;
  try{
    $endpoint = "/restapi/v1.0/account/~/extension/".$agentExtensionId."/active-calls";
    $resp = $platform->get($endpoint);
    $jsonObj = $resp->json();
    foreach ($jsonObj->records as $record){
      if ($record->result == "In Progress"){
        submit_call_supervise_request($record->telephonySessionId, $agentExtensionId, $supervisorDeviceId);
        break;
      }
    }
  }catch (\RingCentral\SDK\Http\ApiException $e) {
    print_r ("Unable to read agent's active calls. " . $e->getMessage() . PHP_EOL);
  }
}

/*
* Supervise an active call session
*/
function submit_call_supervise_request($telephonySessionId, $agentExtensionId, $supervisorDeviceId)
{
  global $platform;
  try{
    $endpoint = "/restapi/v1.0/account/~/telephony/sessions/" . $telephonySessionId . "/supervise";
    $bodyParams = array (
            'mode' => 'Listen',
            'supervisorDeviceId' => $supervisorDeviceId,
            'agentExtensionId' => $agentExtensionId
          );
    $resp = $platform->post($endpoint, $bodyParams);
    print_r (json_encode($resp->json(), JSON_PRETTY_PRINT));
  }catch (\RingCentral\SDK\Http\ApiException $e) {
    print_r ('Unable to supervise this call. ' . $e->getMessage() . PHP_EOL);
  }
}
?>
require 'ringcentral'

#
# Read agent active calls
#
def read_agent_active_calls(agentExtensionId, supervisorDeviceId)
    begin
      endpoint = "/restapi/v1.0/account/~/extension/" + agentExtensionId + "/active-calls"
      resp = $platform.get(endpoint)
      jsonObj = resp.body
      for record in jsonObj['records'] do
        if (record['result'] == "In Progress")
          submit_call_supervise_request(record['telephonySessionId'], agentExtensionId, supervisorDeviceId)
          break
        end
      end
    rescue StandardError => e
      puts ("Unable to read agent's active calls. " + e.to_s)
    end
end


#
# Supervise an active call session
#
def submit_call_supervise_request(telephonySessionId, agentExtensionId, supervisorDeviceId)
    begin
        endpoint = "/restapi/v1.0/account/~/telephony/sessions/" + telephonySessionId + "/supervise"
        bodyParams = {
                       mode: "Listen",
                       supervisorDeviceId: supervisorDeviceId,
                       agentExtensionId: agentExtensionId,
                     }
        resp = $platform.post(endpoint, payload: bodyParams)
        jsonObj = resp.body
        puts (jsonObj)
    rescue StandardError => e
      puts ("Unable to supervise this call. " + e.to_s)
    end
end


# Authenticate a user using a personal JWT token
def login()
  begin
    $platform.authorize( jwt: "SANDBOX-JWT" )
    supervisorDeviceId = "TEST-SUPERVISOR-DEVICEID"
    agentExtensionId = "TEST-AgGENT-EXTENSIONID"
    read_agent_active_calls(agentExtensionId, supervisorDeviceId)
  rescue StandardError => e
    puts ("Unable to authenticate to platform. Check credentials. " + e.to_s)
  end
end

# Instantiate the SDK and get the platform instance
$platform = RingCentral.new( "SANDBOX-APP-CLIENTID", "SANDBOX-APP-CLIENTSECRET", "https://platform.devtest.ringcentral.com" )

login()
using System;
using System.IO;
using System.Threading.Tasks;
using System.Collections.Generic;
using RingCentral;
using Newtonsoft.Json;

namespace CallSupervision {
  class Program {
    static RestClient restClient;
    static async Task Main(string[] args){
      try
      {
        // Instantiate the SDK
        restClient = new RestClient("SANDBOX-APP-CLIENT-ID", "SANDBOX-APP-CLIENT-SECRET", "https://platform.devtest.ringcentral.com");

        // Authenticate a user using a personal JWT token
        await restClient.Authorize("SANDBOX-JWT");

        var supervisorDeviceId = "TEST-SUPERVISOR-DEVICEID";
        var agentExtensionId = "TEST-AGENT-EXTENSIONID";
        await read_agent_active_calls(agentExtensionId, supervisorDeviceId);
      }
      catch (Exception ex)
      {
        Console.WriteLine("Unable to authenticate to platform. Check credentials. " + ex.Message);
      }
    }
    /*
    * Read agent active calls
    */
    static private async Task read_agent_active_calls(String agentExtensionId, String supervisorDeviceId)
    {
      try
      {
        var resp = await restClient.Restapi().Account().Extension(agentExtensionId).ActiveCalls().Get();
        foreach (var record in resp.records)
        {
          if (record.result == "In Progress")
          {
            await submit_call_supervise_request(record.telephonySessionId, agentExtensionId, supervisorDeviceId);
            break;
          }
        }
      }
      catch (Exception ex)
      {
        Console.WriteLine($"Unable to read agent's active calls. {ex.Message}");
      }
    }
    /*
    * Supervise an active call session
    */
    static private async Task submit_call_supervise_request(String telephonySessionId, String agentExtensionId, String supervisorDeviceId)
    {
      try
      {
        var bodyParams = new SuperviseCallSessionRequest();
        bodyParams.mode = "Listen";
        bodyParams.supervisorDeviceId = supervisorDeviceId;
        bodyParams.agentExtensionId = agentExtensionId;
        var resp = await restClient.Restapi().Account().Telephony().Sessions(telephonySessionId).Supervise().Post(bodyParams);
        Console.WriteLine(JsonConvert.SerializeObject(resp));
      }
      catch (Exception ex)
      {
        Console.WriteLine($"Unable to supervise this call. {ex.Message}");
      }
    }
  }
}
package CallSupervision;

import java.io.IOException;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;

import com.ringcentral.*;
import com.ringcentral.definitions.*;

public class CallSupervision {
  static RestClient restClient;

  public static void main(String[] args) {
    var obj = new AnalyzeInteraction();
    try {
      // Instantiate the SDK
      restClient = new RestClient("SANDBOX-APP-CLIENT-ID", "SANDBOX-APP-CLIENT-SECRET", "https://platform.devtest.ringcentral.com");

      // Authenticate a user using a personal JWT token
      restClient.authorize("SANDBOX-JWT");

      var supervisorDeviceId = "TEST-SUPERVISOR-DEVICEID";
      var agentExtensionId = "TEST-AGENT-EXTENSIONID";
      obj.read_agent_active_calls(agentExtensionId, supervisorDeviceId);
    } catch (RestException e) {
      System.out.println(e.getMessage());
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  /*
  * Read agent active calls
  */
  public void read_agent_active_calls(String agentExtensionId, String supervisorDeviceId) throws RestException, IOException {
    try {
      var resp = restClient.restapi().account().extension(agentExtensionId).activeCalls().get();
      for (var record : resp.records)
      {
        if (record.result.equals("In Progress")) {
          submit_call_supervise_request(record.telephonySessionId, agentExtensionId, supervisorDeviceId);
          break;
        }
      }
    } catch (RestException ex) {
      System.out.println("Unable to read agent's active calls. " + ex.getMessage());
    }
  }
  /*
  * Supervise an active call session
  */
  public void submit_call_supervise_request(String telephonySessionId, String agentExtensionId, String supervisorDeviceId)  throws RestException, IOException {
    try {
      var bodyParams = new SuperviseCallSessionRequest();
      bodyParams.mode = "Listen";
      bodyParams.supervisorDeviceId = supervisorDeviceId;
      bodyParams.agentExtensionId = agentExtensionId;
      var resp = restClient.restapi().account().telephony().sessions(telephonySessionId).supervise().post(bodyParams);
      String jsonStr = new Gson().toJson(resp, new TypeToken<Object>(){}.getType());
      System.out.println(jsonStr);
    } catch (RestException ex) {
      System.out.println("Unable to supervise this call. " + ex.getMessage());
    }
  }
}

Build you own soft-phone device

You can build your own supervisor SIP phone, which will receive the call streaming audio data. Currently, we provide three soft-phone SDKs that help you to build your own SIP phone.

Follow the instructions on each project to install the SDK and copy the sample code to implement your app.

FCC Compliance

If you intend to save the audio stream, please make sure you comply with the FCC guidelines by letting the customer know that the calls will be monitored. The following video demonstrates a working example of the Supervision API using the concepts described here.

Additional Resources