News & Announcements User Community Developer Community

Welcome to the RingCentral Community

Please note the community is currently under maintenance and is read-only.

Search
Make sure to review our Terms of Use and Community Guidelines.
  Please note the community is currently under maintenance and is read-only.
Home » Developers
Pause & Resume API Call
Tags: rest api
Aug 26, 2021 at 7:26am   •   6 replies  •  0 likes
Martin Dearne

I am using the test harness call here https://developers.ringcentral.com/api-reference/Call-Control/pauseResumeCallRecording

The attributes are being populated from the webhook subscription response when on a call "/restapi/v1.0/account/~/telephony/sessions" and believe the values being used are correct.

accountId*

Internal identifier of a RingCentral account or tilde (~) to indicate the account logged-in within the current session

telephonySessionId*

Internal identifier of a call session

partyId*

Internal identifier of a call party

recordingId*

Internal identifier of a recording

Using the CURL example the data being sent

--data '{"active":true}'


The response being received for this request is "400 Bad Request" with message "Invalid Request"

I have logged a support ticket for this but wait a response so if anybody could shine some light on this would be appreciated.

6 Answers
answered on Sep 15, 2021 at 3:09am  

Hi Phong,

I now have the credentials working in production and getting the following error: -

{

"error" : "invalid_grant",

"errors" : [ {

"errorCode" : "OAU-140",

"message" : "Invalid resource owner credentials"

} ],

"error_description" : "Invalid resource owner credentials"

}


I believe this may be related to the permissions for the app as the user being used in the app owner


If you could advise here it would be appreciated.


Regards,

Martin Dearne


 0
answered on Aug 31, 2021 at 9:17pm  

Here you go. A quick and dirty .Net sample code. You need to install the latest RingCentral .Net SDK

using System;
using System.IO;
using Newtonsoft.Json.Linq;
using System.Threading;
using System.Threading.Tasks;
using RingCentral;
using RingCentral.Net.Events;
using Newtonsoft.Json;
using RingCentral.Net.PubNub;
using System.Net;
using System.Collections.Specialized;
using System.Linq;

namespace Quick_Sample
{
    class Program
    {
        const string RINGCENTRAL_CLIENTID = "";
        const string RINGCENTRAL_CLIENTSECRET = "";
        const bool   RINGCENTRAL_PRODUCTION = false;

        const string RINGCENTRAL_USERNAME = "";
        const string RINGCENTRAL_PASSWORD = "";
        const string RINGCENTRAL_EXTENSION = "";
        
        static RestClient rcsdk;

        static string[] customerPhoneNumbers = { "+1650xxxxxxx" };

        class CallInfo {
            public String sessionId = ""; // use this to identify the call when handle multiple calls
            public String partyId = ""; // use this to create the recording endpoint
            public String telSessionId = ""; // use this to create the recording endpoint
            public String recordingId = "";
        }
        static CallInfo callInfo;

        static void Main(string[] args)
        {
            rcsdk = new RestClient(RINGCENTRAL_CLIENTID, RINGCENTRAL_CLIENTSECRET, RINGCENTRAL_PRODUCTION);
            rcsdk.Authorize(RINGCENTRAL_USERNAME, RINGCENTRAL_EXTENSION, RINGCENTRAL_PASSWORD).Wait();
            var eventsExtension = new EventsExtension();
            rcsdk.InstallExtension(eventsExtension).Wait();

            eventsExtension.RequestSuccess += EventHandler;
            callInfo = new CallInfo();
            pubnub_notification_record_call().Wait();
        }
        static void EventHandler(object sender, HttpMessages httpMessages)
        {
            var rateLimitRemaining = httpMessages.httpResponseMessage.Headers.First(i => i.Key == "X-Rate-Limit-Remaining").Value.First();
            Console.WriteLine("Remaining: " + rateLimitRemaining);
        }
        static private async Task pubnub_notification_record_call()
        {
            var pubNubExtension = new PubNubExtension();
            await rcsdk.InstallExtension(pubNubExtension);
            try
            {
                var eventFilters = new[]
                {
                    "/restapi/v1.0/account/~/extension/~/telephony/sessions"
                };
                var subscription = await pubNubExtension.Subscribe(eventFilters, message =>
                {
                    dynamic jsonObj = JObject.Parse(message);
                    var body = jsonObj.body;
                    var party = jsonObj.body.parties[0];
                    if (party.extensionId != null)
                    {
                        if (party.direction == "Inbound" && party.status.code == "Answered")
                        {
                            if (party.recordings == null)
                            {
                                var canRecord = customerPhoneNumbers.Where(x => x.ToString() == party.from.phoneNumber);
                                if (canRecord != null)
                                {
                                    Console.WriteLine("Callee Answered => It's time to call recording.");
                                    callInfo.sessionId = body.sessionId;
                                    callInfo.partyId = party.id;
                                    callInfo.telSessionId = body.telephonySessionId;
                                    Console.WriteLine("Can start recording");
                                    // start recording immediately or make a delay
                                    startRecording().Wait();
                                }
                            }
                        }
                        else if (party.status.code == "Disconnected")
                        {
                            // Want to download the call recording after the call ended?
                            if (body.sessionId == callInfo.sessionId && party.recordings != null)
                            {
                                Console.WriteLine("Call has recording => Download it in 30 seconds");
                                Thread.Sleep(30000);
                                readExtensionCallLog(callInfo.sessionId).Wait();
                                Console.WriteLine("Call Disconnected => Reset getCallSessionInfo");
                            }
                        }
                    }
                    else
                    {
                        Console.WriteLine("Remote party event");
                    }
                });

                Console.WriteLine(subscription.SubscriptionInfo.id);
                Console.WriteLine("Subscribed");
                while (true)
                {
                    Console.WriteLine("looping ...");
                    Thread.Sleep(5000);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
        static private async Task startRecording()
        {
            if (callInfo.telSessionId == "" || callInfo.partyId == "")
            {
                return;
            }
            try
            {
                var resp = await rcsdk.Restapi().Account().Telephony().Sessions(callInfo.telSessionId).Parties(callInfo.partyId).Recordings().Post();
                dynamic jsonObj = JObject.Parse(resp);
                callInfo.recordingId = jsonObj.id;
                Console.WriteLine(jsonObj.id);
                // stop recording after 1 min
                Thread.Sleep(60000);
                pauseRecording().Wait();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
        static private async Task pauseRecording()
        {
            if (callInfo.telSessionId == "" || callInfo.partyId == "" || callInfo.recordingId == "")
            {
                return;
            }
            try
            {
                CallRecordingUpdate update = new CallRecordingUpdate();
                update.active = false;
                var resp = await rcsdk.Restapi().Account().Telephony().Sessions(callInfo.telSessionId).Parties(callInfo.partyId).Recordings(callInfo.recordingId).Patch(update);
                callInfo.recordingId = resp.id;
                Console.WriteLine(resp.id);
                // resume recording after 10 secs
                Thread.Sleep(10000);
                resumeRecording().Wait();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
        static private async Task resumeRecording()
        {
            if (callInfo.telSessionId == "" || callInfo.partyId == "" || callInfo.recordingId == "")
            {
                return;
            }
            try
            {
                CallRecordingUpdate update = new CallRecordingUpdate();
                update.active = true;
                var resp = await rcsdk.Restapi().Account().Telephony().Sessions(callInfo.telSessionId).Parties(callInfo.partyId).Recordings(callInfo.recordingId).Patch(update);
                callInfo.recordingId = resp.id;
                Console.WriteLine(resp.id);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
        static private async Task readExtensionCallLog(String sessionId)
        {
            Console.WriteLine("readExtensionCallLog");
            var parameters = new ReadUserCallLogParameters();
            parameters.sessionId = sessionId;
            var path = "recordings/";
            System.IO.Directory.CreateDirectory(path);
            var resp = await rcsdk.Restapi().Account().Extension().CallLog().List(parameters);
            foreach (var record in resp.records)
            {
                var destFile = path + record.recording.id + ".mp3";
                var contentUrl = record.recording.contentUri + "?access_token=" + rcsdk.token.access_token;
                WebClient webClient = new WebClient();
                webClient.DownloadFile(contentUrl, destFile);
                Console.WriteLine("CALL RECORDING SAVED AT: " + destFile);
                
            }
        }
    }
}

 0
answered on Aug 31, 2021 at 8:16am  

Is it possible to have a .net equivalent for the above?


 0
on Aug 31, 2021 at 8:43am   •  1 likes

I can implement it later. Just too busy right now.

answered on Aug 31, 2021 at 7:19am  

Here you go with a quick sample app.

1. Provide your app credentials and user login credentials

2. Put the caller phone number into the 'customerPhoneNumbers' array. This is just a fake condition to decide a recording.

3. Make a subfolder 'recordings'

4. Make a call from that 'customer' number to one of the numbers belong to the authenticated user extension (the one logs in the app)

The demo app will start recording (after 10 secs), pause after 1 min and resume after 10 secs. Then it will download the recording after 30 secs when the call is disconnected.

You need to install the RingCentral JS SDK before running the script.

const RingCentral = require('@ringcentral/sdk').SDK
const Subscriptions = require('@ringcentral/subscriptions').Subscriptions;
var fs = require('fs')
var async = require("async");

RINGCENTRAL_CLIENTID = "";
RINGCENTRAL_CLIENTSECRET = "";
RINGCENTRAL_SERVER = 'https://platform.devtest.ringcentral.com'

RINGCENTRAL_USERNAME = "";
RINGCENTRAL_PASSWORD = ""
RINGCENTRAL_EXTENSION = ""

const rcsdk = new RingCentral({
  server: RINGCENTRAL_SERVER,
  clientId: RINGCENTRAL_CLIENTID,
  clientSecret: RINGCENTRAL_CLIENTSECRET
})

var platform = rcsdk.platform()
const subscriptions = new Subscriptions({
   sdk: rcsdk
});
var subscription = subscriptions.createSubscription();

platform.login({
        username: RINGCENTRAL_USERNAME,
        extension: RINGCENTRAL_EXTENSION,
        password: RINGCENTRAL_PASSWORD
      })

platform.on(platform.events.loginSuccess, async function(e){
    console.log("Login success")
    subscribeForNotification
});

function subscribeForNotification(){
  var eventFilter = ['/restapi/v1.0/account/~/extension/~/telephony/sessions']
  subscription.setEventFilters(eventFilter)
   .register()
   .then(async function(resp){
     console.log('Ready for getting account NSN events')
   })
   .catch(function(e){
     throw e
   })
}

var customerPhoneNumbers = ['+1650xxxxxxx']

var callInfo = {
  sessionId: '', // use this to identify the call when handle multiple calls
  partyId: '', // use this to create the recording endpoint
  telSessionId: '', // use this to create the recording endpoint
  recordingId: ''
}


subscription.on(subscription.events.notification, async function(msg) {
    var body = msg.body
    var party = msg.body.parties[
    if (party.hasOwnProperty("extensionId")){
      console.log(JSON.stringify(msg.body));
      if (party.direction == "Inbound" && party.status.code == "Answered"){
        if (!party.hasOwnProperty('recordings')){
          var canRecord = customerPhoneNumbers.find(customer => customer == party.from.phoneNumber)
          if (canRecord) {
            console.log("Callee Answered => It's time to call recording.")
            callInfo.sessionId = body.sessionId
            callInfo.partyId = party.id
            callInfo.telSessionId = body.telephonySessionId
            // start recording immediately or make a delay
            setTimeout(function(){
              startRecording()
            }, 10000)
          }
        }
      }else if (party.status.code == "Disconnected"){
        // Want to download the call recording after the call ended?
        if (body.sessionId == callInfo.sessionId && party.hasOwnProperty("recordings")){
          console.log("Call has recording => Download it in 30 seconds")
          setTimeout(function(sessionId){
            readExtensionCallLogs(sessionId)
          }, 30000, callInfo.sessionId)
        }
        callInfo.sessionId = ""
        callInfo.partyId = ""
        callInfo.telSessionId = ""
        console.log("Call Disconnected => Reset getCallSessionInfo")
      }
    }else{
      console.log("caller of inbound call")
    }
});


async function startRecording(){
  console.log("startRecording")
  if (callInfo.telSessionId == '' || callInfo.partyId == '')
    return
  var endpoint = `/restapi/v1.0/account/~/telephony/sessions/${callInfo.telSessionId}/parties/${callInfo.partyId}/recordings`
  try {
    var resp = await platform.post(endpoint)
    var jsonObj = await resp.json()
    callInfo.recordingId = jsonObj.id
    console.log(jsonObj)
    // stop recording after 1 min
    setTimeout(function(){
      pauseRecording()
    }, 60000)
  }catch(e){
    console.log(e)
  }
}

async function pauseRecording(){
  console.log("pauseRecording")
  if (callInfo.telSessionId == '' || callInfo.partyId == '')
    return
  var endpoint = `/restapi/v1.0/account/~/telephony/sessions/${callInfo.telSessionId}/parties/${callInfo.partyId}/recordings/${callInfo.recordingId}`
  var params = {
    active: false
  }
  try {
    var resp = await platform.patch(endpoint, params)
    var jsonObj = await resp.json()
    console.log(jsonObj)
    // resume recording after 10 secs
    setTimeout(function(){
      resumeRecording()
    }, 10000)
  }catch(e){
    console.log(e)
  }
}

async function resumeRecording(){
  console.log("resumeRecording")
  if (callInfo.telSessionId == '' || callInfo.partyId == '')
    return
  var endpoint = `/restapi/v1.0/account/~/telephony/sessions/${callInfo.telSessionId}/parties/${callInfo.partyId}/recordings/${callInfo.recordingId}`
  var params = {
    active: true
  }
  try {
    var resp = await platform.patch(endpoint, params)
    var jsonObj = await resp.json()
    console.log(jsonObj)
  }catch(e){
    console.log(e)
  }
}

async function readExtensionCallLogs(sessionId){
  var endpoint = `/restapi/v1.0/account/~/extension/~/call-log`
  console.log(endpoint)
  var params = {
      sessionId: sessionId
  }
  try {
    var resp = await platform.get(endpoint, params)
    var jsonObj = await resp.json()
    console.log(jsonObj)
    async.each(jsonObj.records,
        function(record, callback){
          console.log("THIS CALL HAS A RECORDING: " + record.recording.contentUri)
          saveAudioFile(record)
        },
        function(err){
          console.log("This call does not have a recording.")
        }
    );
  }catch(e){
    var err = e.toString();
    console.log("READ FAILED: " + err)
  }
}


async function saveAudioFile(record){
  var resp = await platform.get(record.recording.contentUri)
  var buffer = await resp.buffer()
  var destFile = './recordings/' + record.recording.id + '.mp3'
  fs.writeFileSync(destFile, buffer);
  console.log("CALL RECORDING SAVED AT: " + destFile)
}

Let me know if you have further questions


 0
answered on Aug 26, 2021 at 1:26pm  

Ye it was myself on the call and authenticated via the website ui to get authorisation token.


 0
answered on Aug 26, 2021 at 11:15am  

Did you authenticate your app with the user's credentials of the user who is a party of the call?


 0



A new Community is coming to RingCentral!

Posts are currently read-only as we transition into our new platform.

We thank you for your patience
during this downtime.

Try Workflow Builder

Did you know you can easily automate tasks like responding to SMS, team messages, and more? Plus it's included with RingCentral Video and RingEX plans!

Try RingCentral Workflow Builder

PRODUCTS
RingEX
Message
Video
Phone
OPEN ECOSYSTEM
Developer Platform
APIs
Integrated Apps
App Gallery
Developer support
Games and rewards

RESOURCES
Resource center
Blog
Product Releases
Accessibility
QUICK LINKS
App Download
RingCentral App login
Admin Portal Login
Contact Sales
© 1999-2024 RingCentral, Inc. All rights reserved. Legal Privacy Notice Site Map Contact Us