Making Calls Using RingOut

Last updated: 2023-04-11Contributors
Edit this page

The RingOut option enables the users to make a call from any other outside number (not RingCentral number) by means of the RingCentral account, when it is not convenient for them to use the RingCentral number. This feature is available for softphone, web service and mobile applications.

The user specifies a certain number under the forwarding number list, starts RingOut and enters the required called number. RingCentral makes a call to the specified forwarding number and connects the user with the called number.

The API treats a two-legged RingOut call as a resource that can be created, retrieved, or deleted via the POST, GET and DELETE methods correspondingly.

Sample Code to Get Started with RingOut

The two-legged RingOut call can be created via the following request:

POST /restapi/v1.0/account/~/extension/~/ring-out
Content-Type: application/json
Authorization: Bearer <access-token>

{
    "from": {"phoneNumber": "13445554444"}, /* from parameter is optional if there is a default number in user's forwarding numbers */
    "to": {"phoneNumber": "13455553434"}, /* to parameter is required */
    "playPrompt": true /* optional field */
}
const RC_SDK = require('@ringcentral/sdk').SDK
const path = require('path')
// Remember to modify the path of your .env file location!
require('dotenv').config({ path: path.resolve(__dirname, '../.env') })

const CALLER       = process.env.RINGOUT_CALLER
const RECIPIENT    = process.env.RINGOUT_RECIPIENT

var rcsdk = new RC_SDK({
    'server':       process.env.RC_SERVER_URL,
    'clientId':     process.env.RC_APP_CLIENT_ID,
    'clientSecret': process.env.RC_APP_CLIENT_SECRET
});
var platform = rcsdk.platform();
platform.login({ 'jwt':  process.env.RC_USER_JWT })

platform.on(platform.events.loginSuccess, () => {
  call_ringout()
})

/*
* Place a ring-out call
*/
async function call_ringout() {
  try {
    var resp = await platform.post('/restapi/v1.0/account/~/extension/~/ring-out', {
      'from': { 'phoneNumber': CALLER },
      'to': { 'phoneNumber': RECIPIENT },
      'playPrompt': false
    })
    var jsonObj = await resp.json()
    console.log("Call placed. Call status: " + jsonObj.status.callStatus)
  } catch (e) {
    console.log("Unable to place a ring-out call.", e.message)
  }
}
// End of Quick Start Code Section

/**********************************************************
***********************************************************
 TEST SECTION - THESE FUNTIONS ARE NOT SHOWN IN DEV GUIDE
***********************************************************
 Code snippet section for boostrap testing purpose
**********************************************************/
const sleep = async (ms) => {
  await new Promise(r => setTimeout(r, ms));
}

exports.platform = platform;

boostrap_test_function()
async function boostrap_test_function(){
  console.log("boostrap_test_function")

  // await sleep(2000)
  // console.log("Test create a call monitoring group")
  // require ('./code-snippets/create-update-call-monitoring-group.js')
  // return
  //
  // await sleep(2000)
  // console.log("Test List Call Monitoring Groups")
  // require ('./code-snippets/call-monitoring-group.js')
  // return

  await sleep(2000)
  console.log("Test Supervise Call Session")
  require ('./code-snippets/call-supervision.js')
  return

/*
  await sleep(2000)
  console.log("Test Enrollment Speaker Identification")
  require ('./code-snippets/enrollment.js')
  return

  await sleep(2000)
  console.log("Test Summarization")
  require ('./code-snippets/summarize.js')
  return

  await sleep(2000)
  console.log("Test Speaker Diarization")
  require ('./code-snippets/speaker-diarization.js')
  return


  await sleep(2000)
  console.log("Test Speaker Identification")
  require ('./code-snippets/speaker-identifier.js')
  return

  await sleep(2000)
  console.log("Test Enrollment Extra")
  require ('./code-snippets/enrollment-extra.js')
  return

  await sleep(2000)
  console.log("Test Punctuation")
  require ('./code-snippets/punctuation.js')
  return
*/
}
from ringcentral import SDK
import os,sys,urllib.parse,json
from pathlib import Path
from dotenv import load_dotenv
load_dotenv()

CALLER    = os.environ.get('RINGOUT_CALLER')
RECIPIENT = os.environ.get('RINGOUT_RECIPIENT')

#
#  Place a ring-out call
#
def call_ring_out():
    try:
        bodyParams = {
            'from' : { 'phoneNumber': CALLER },
            'to'   : { 'phoneNumber': RECIPIENT },
            'playPrompt' : False
        }
        endpoint = "/restapi/v1.0/account/~/extension/~/ring-out"
        resp = platform.post(endpoint, bodyParams)
        jsonObj = resp.json()
        print(f'Call placed. Call status: {jsonObj.status.callStatus}')
    except Exception as e:
        print ("Unable to place a ring-out call. " + str(e))

# Instantiate the SDK and get the platform instance
rcsdk = SDK( os.environ.get('RC_APP_CLIENT_ID'),
             os.environ.get('RC_APP_CLIENT_SECRET'),
             os.environ.get('RC_SERVER_URL') )
platform = rcsdk.platform()

# Authenticate a user using a personal JWT token
def login():
    try:
      platform.login( jwt=os.environ.get('RC_USER_JWT') )
      #call_ring_out()
    except Exception as e:
      sys.exit("Unable to authenticate to platform. Check credentials." + str(e))

login()
##############################
# End of quick start section
##############################


########
# Code snippet section for boostrap testing purpose
########
import time
def boostrap_test_function():
    time.sleep (2)
    import importlib

    # time.sleep (2)
    # print ("Test create call monitoring group")
    # ms = importlib.import_module("code-snippets.create-update-call-monitoring-group")
    # ms.platform = platform
    # ms.create_call_monitoring_group("Demo Group - Python")
    # #ms.delete_call_monitoring_group('8125017')
    #
    # time.sleep (2)
    # print ("Test list call monitoring groups and members")
    # ms = importlib.import_module("code-snippets.call-monitoring-group")
    # ms.platform = platform
    # ms.read_call_monitoring_groups()

    time.sleep (2)
    print ("Test supervise call session")
    ms = importlib.import_module("code-snippets.call-supervision")
    ms.platform = platform
    supervisorDeviceId = "802636634016"
    agentExtensionId = "62295327016"

    ms.read_agent_active_calls(agentExtensionId, supervisorDeviceId)

    #print ("")
    # time.sleep (2)
    #ms = importlib.import_module("code-snippets.enrollment")
    #ms.platform = platform
    #contentFile = os.environ.get('ENROLLMENT_CONTENT_3')
    #ms.enrollment(contentFile)

    #print ("")
    # time.sleep (2)
    #ms = importlib.import_module("code-snippets.enrollment-extra")
    #ms.platform = platform
    #ms.read_enrollments()

# must be on the last line!
boostrap_test_function()
<?php
// Remember to modify the path ./../ pointing to the location where the RingCentral SDK was installed and the .env file was saved!
require('./../vendor/autoload.php');
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . './../');
$dotenv->load();

$rcsdk = new RingCentral\SDK\SDK( $_ENV['RC_APP_CLIENT_ID'],
                                  $_ENV['RC_APP_CLIENT_SECRET'],
                                  $_ENV['RC_SERVER_URL'] );

$platform = $rcsdk->platform();

// Authenticate a user using a personal JWT token
try {
  $platform->login( [ "jwt" => $_ENV['RC_USER_JWT'] ] );
  //call_ring_out();
} catch (\RingCentral\SDK\Http\ApiException $e) {
  exit("Unable to authenticate to platform. Check credentials. " . $e->getMessage() . PHP_EOL);
}

/*
* Place a ring-out call
*/
function call_ring_out(){
  global $platform;
  $CALLER       = $_ENV['RINGOUT_CALLER'];
  $RECIPIENT    = $_ENV['RINGOUT_RECIPIENT'];

  try {
    $bodyParams = array(
      'from' => array('phoneNumber' => $CALLER ),
      'to' => array('phoneNumber' => $RECIPIENT),
      'playPrompt' => false
    );
    $endpoint = "/account/~/extension/~/ring-out";
    $resp = $platform->post($endpoint, $bodyParams);
    print_r ("Call placed. Call status: " . $resp->json()->status->callStatus);
  }catch (\RingCentral\SDK\Http\ApiException $e) {
    // Getting error messages using PHP native interface
    print 'HTTP Error: ' . $e->getMessage() . PHP_EOL;
    // Another way to get message, but keep in mind, that there could be no response if request has failed completely
    print 'Unable to place a ring-out call. ' . $e->apiResponse->response()->error() . PHP_EOL;
  }
}
?>


<?php
/**********************************************************
 Code snippet section for boostrap testing purpose
**********************************************************/
boostrap_test_function();
function boostrap_test_function(){

  sleep(2);
  print_r ("Test code snippets". PHP_EOL);
  //require_once (__DIR__ .'/code-snippets/create-update-call-monitoring-group.php');
  // sleep(2);
  // print_r ("Test fetching stt task status". PHP_EOL);
  // require_once (__DIR__ .'/code-snippets/create-update-call-monitoring-group.php');
  //
  // sleep(2);
  // print_r ("Test list call monitoring group". PHP_EOL);
  //require_once (__DIR__ .'/code-snippets/call-monitoring-group.php');
  require_once (__DIR__ .'/code-snippets/call-supervision.php');
}
?>
using System;
using System.Threading.Tasks;
using RingCentral;

namespace Call_Ringout
{
  class Program
  {
    static RestClient restClient;
    static async Task Main(string[] args)
    {
      restClient = new RestClient(
      Environment.GetEnvironmentVariable("RC_APP_CLIENT_ID"),
      Environment.GetEnvironmentVariable("RC_APP_CLIENT_SECRET"),
      Environment.GetEnvironmentVariable("RC_SERVER_URL"));
      await restClient.Authorize(Environment.GetEnvironmentVariable("RC_USER_JWT"));
      await call_ringout();
    }

    static private async Task call_ringout()
    {
      var parameters = new MakeRingOutRequest();
      parameters.from = new MakeRingOutCallerInfoRequestFrom {
        phoneNumber = Environment.GetEnvironmentVariable("RINGOUT_CALLER")
      };
      parameters.to = new MakeRingOutCallerInfoRequestTo {
        phoneNumber = Environment.GetEnvironmentVariable("RINGOUT_RECIPIENT")
      };
      parameters.playPrompt = false;

      var resp = await restClient.Restapi().Account().Extension().RingOut().Post(parameters);
      Console.WriteLine("Call Placed. Call status" + resp.status.callStatus);
    }
  }
}
import com.ringcentral.*;
import com.ringcentral.definitions.*;
import java.io.IOException;

public class RingoutQuickStart {
    static String RINGOUT_CALLER    = System.getenv("RINGOUT_CALLER");
    static String RINGOUT_RECIPIENT = System.getenv("RINGOUT_RECIPIENT");
    static RestClient rc;

    public static void main(String[] args) {
        var obj = new RingoutQuickStart();
        rc = new RestClient( System.getenv("RC_APP_CLIENT_ID"),
                             System.getenv("RC_APP_CLIENT_SECRET"),
                             System.getenv("RC_SERVER_URL") );
        try {
        rc.authorize(System.getenv("RC_USER_JWT"));
            obj.call_ringout();
        } catch (RestException | IOException e) {
            e.printStackTrace();
        }
    }
    public void call_ringout() throws RestException, IOException {
        MakeRingOutRequest requestBody = new MakeRingOutRequest();
        requestBody.from(new MakeRingOutCallerInfoRequestFrom().phoneNumber(
              RINGOUT_CALLER ));
        requestBody.to(new MakeRingOutCallerInfoRequestTo().phoneNumber(
          RINGOUT_RECIPIENT));
        requestBody.playPrompt = false;

        var response = rc.restapi().account().extension().ringOut().post(requestBody);
        System.out.println("Call Placed. Call status: " + response.status.callStatus);
    }
}
require 'ringcentral'
require 'dotenv'
require 'json'

# Remember to modify the path to where you saved your .env file!
Dotenv.load("./../.env")

CALLER    = ENV['RINGOUT_CALLER']
RECIPIENT = ENV['RINGOUT_RECIPIENT']

#
#  Place a ring-out call
#
def call_ring_out()
    bodyParams = {
        'from': { 'phoneNumber': CALLER },
        'to': { 'phoneNumber': RECIPIENT },
        'playPrompt': false
    }
    endpoint = "/restapi/v1.0/account/~/extension/~/ring-out"
    begin
      resp = $platform.post(endpoint, payload: bodyParams)
      body = resp.body
      puts "Call placed. Call status: " + resp.body['status']['callStatus']
    rescue StandardError => e
      puts ("Unable to place a ring-out call. " + e.to_s)
    end
end

# Instantiate the SDK and get the platform instance
$platform = RingCentral.new( ENV['RC_APP_CLIENT_ID'], ENV['RC_APP_CLIENT_SECRET'], ENV['RC_SERVER_URL'] )

# Authenticate a user using a personal JWT token
def login()
  begin
    $platform.authorize(jwt: ENV['RC_USER_JWT'])
    #call_ring_out()
  rescue StandardError => e
    puts ("Unable to authenticate to platform. Check credentials." + e.to_s)
  end
end

login()
##############################
# End of quick start section
##############################


########
# Code snippet section for boostrap testing purpose
########
def boostrap_test_function()

    # sleep(2)
    # puts "Test create a call monitoring group"
    # require_relative './code-snippets/create-update-call-monitoring-group'
    # create_call_monitoring_group("Demo Group - Ruby")
    #
    # sleep(2)
    # puts "Test list call monitoring groups"
    # require_relative './code-snippets/call-monitoring-group'
    # read_call_monitoring_groups()

    sleep(2)
    puts "Test supervise call session"
    require_relative './code-snippets/call-supervision'
    supervisorDeviceId = "802636634016"
    agentExtensionId = "62295327016"
    read_agent_active_calls(agentExtensionId, supervisorDeviceId)
end

boostrap_test_function()

Request parameters

Parameter Description
from Refers to the number of the calling party. Required field only if there is no default number in the user's forwarding number list. The phoneNumber attribute should comply with the E.164 standard. As a result of validation of the phoneNumber attribute the server may return the error code: 400 Bad Request - phoneNumber specified in the field is empty or invalid.
to Refers to the called party number. Required field. If this field is missing from the request, the 400 Bad Request error is returned. The phoneNumber attribute should comply with the E.164 standard. As a result of validation of the phoneNumber attribute the server may return the error code: 400 Bad Request - phoneNumber specified in the field is empty or invalid.
playPrompt The audio prompt that the calling party hears when the call is connected. Optional field. It corresponds to the setting in the RingCentral application "Prompt me to dial 1 before connecting" (When selected, the system will ask you to press "1" on your phone's key pad before connecting to the destination number).

Example response

The response can be as follows:

{
    "id": 234343434,
    "uri": "/restapi/v1.0/account/~/extension/~/ringout/234343434",
    "status": {
        "callStatus": "Success",
        "callerStatus": "Success",
        "calleeStatus": "Success"
    }
}

Response parameters

callStatus

Value Description
InProgress Connection is being established
Success Both legs connected (Answered)
CannotReach Failure state (one or both legs are in invalid state for call connection)
NoAnsweringMachine Internal server failure

callerStatus and calleeStatus

Value Description
InProgress Connection to the target leg is being established
Busy Target device is busy
NoAnswer The call has been dropped because of timeout
Rejected
  • RingOut command was canceled by user or
  • RingOut initiated, 1st leg answered, 2nd is ringing, user drops call on the 1st leg - 2nd leg gets 'Rejected'
Success Call party has answered the call
Finished The call was terminated (In Progress > Success > Finished)
GenericError
  • Error code received from PSTN or
  • Internal server error
InternationalDisabled
  • International calling disabled (Call to International number) or
  • Domestic calling disabled (Call with local Country code) or
  • Internal calling disabled (Call within one account)
NoSessionFound RingOut status was requested for RingOut session which does not exist (e.g was already Closed)
Invalid RingOut session state is unknown due to internal failure

Call status is generated as a combination of both call legs statuses (caller and callee):

Caller or Callee statuses are separately generated for the target call party:

RingOut Flow

See how the statuses are changing during successful call on the flowchart below:

Failed call:

How to check on the status of a RingOut call in progress

You can poll the RingOut endpoint to get the status of an ongoing outbound RingOut call.

GET /restapi/v1.0/account/~/extension/~/ring-out/234343434

The response will be similar to the following:

{
    "id": 234343434,
    "uri": "/restapi/v1.0/account/~/extension/~/ringout/234343434",
    "status": {
        "callStatus": "Success",
        "callerStatus": "Success",
        "calleeStatus": "Success"
    }
}

How to cancel a RingOut in progress

RingCentral does not currently support call control for outbound calls. However, you can cancel a RingOut call while the callee's party status is InProgress by making a DELETE request to the RingOut endpoint as shown below:

DELETE /restapi/v1.0/account/~/extension/~/ring-out/234343434

If successful, the platform will respond with a 204.

Deleting the RingOut is only available when the RingOut has been initiated and the call has not yet been connected. For example, if you initiate a RingOut and immediately call the DELETE RingOut endpoint, the call would get terminated as long as the RingOut between the two parties is not connected (first leg has not been established). If the first leg of the call has been initiated, then the DELETE API will not terminate the call.

Setting the caller ID for RingOut calls

RingCentral allows users to select which number to use for their caller ID name (CNAM) value when making calls. However, this can only be done through the RingCentral administrative console, and cannot be set at runtime or programmatically. To set the CallerId for the RingOut API, set the "RingOut from Web" value as available in the Online Account Portal. More information on this can be found in KB Article #3471.