Obtaining a RingCX access token with a RingCentral Login

This is the default authentication flow for current RingCX Voice API integrations. It applies when the RingCX user signs in with a linked RingCentral / RingEX login.

The flow has two steps:

  1. Obtain a RingCentral access token from the RingCentral platform.
  2. Exchange that RingCentral access token for a RingCX access token.

Use the RingCX access token, not the original RingCentral access token, when you call RingCX Voice APIs.

RingCX Voice APIs are rooted at: https://engage.ringcentral.com/voice/api/

Create an app

Create an app in the RingCentral Developer Portal. You can use the "Create RingCX App" button below, enter a name and description if needed, and click "Create". If you do not have a RingCentral account, you will be prompted to create one.

Important

You need a production RingCentral account that is linked to your RingCX account. A RingCentral account created only in the Developer Portal will not provide access to production RingCX Voice APIs.

Create RingCX App

  1. Log in or create an account if you have not done so already.
  2. Go to Console/Apps and click the "Create App" button.
  3. Select the app type, "REST API App (most common)," then click Next.
  4. Give your app a name and description, then scroll to the "Auth" section.
  5. In the "Auth" section:
    • Select "JWT auth flow" for the authentication method.
    • Under "Issue refresh tokens?", make sure "Yes" is selected.
  6. In the "Security" section, specify the following application scope:
    • Read Accounts
  7. In the same "Security" section, make sure the app is private and can only be called using credentials from the same RingCentral account.

When you are done, you will be taken to the app's dashboard. Save the Client ID and Client Secret. You will use them to authenticate with RingCentral in the next step.

Retrieve a RingCentral access token

Use the RingCentral authentication flow configured for your app to retrieve a RingCentral access token. For server-side RingCX integrations, JWT auth is the recommended RingCentral auth flow.

See the RingCentral authentication guide for the current RingCentral OAuth instructions.

The RingCentral access token proves the RingCentral user/app identity. It is not the token you send to RingCX Voice API endpoints. After you retrieve it, exchange it for a RingCX access token.

Retrieve a RingCX access token

Call the RingCX token exchange endpoint with the RingCentral access token.

The token exchange and refresh endpoints are authentication endpoints. The examples below use the Engage auth host. After you have a RingCX access token, use the RingCX Voice API host for API calls.

Request

POST https://engage.ringcentral.com/api/auth/login/rc/accesstoken?includeRefresh=true
Content-Type: application/x-www-form-urlencoded

rcAccessToken=<ringCentralAccessToken>&rcTokenType=Bearer

Where:

  • <ringCentralAccessToken> is the RingCentral access token returned by the RingCentral authentication flow.
  • rcTokenType must be Bearer.
  • includeRefresh=true includes a RingCX refreshToken in the response.
curl -X POST 'https://engage.ringcentral.com/api/auth/login/rc/accesstoken?includeRefresh=true' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'rcAccessToken=<ringCentralAccessToken>' \
  -d 'rcTokenType=Bearer'
package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/url"
)

// RingCXToken is an example and does not cover all the
// properties in the API response.
type RingCXToken struct {
    AccessToken string `json:"accessToken"`
    TokenType   string `json:"tokenType"`
}

func RcToRingCXToken(rctoken string) (string, error) {
    res, err := http.PostForm(
        "https://engage.ringcentral.com/api/auth/login/rc/accesstoken?includeRefresh=true",
        url.Values{"rcAccessToken": {rctoken}, "rcTokenType": {"Bearer"}})
    if err != nil {
        return "", err
    }
    defer res.Body.Close()
    if res.StatusCode >= 300 {
        return "", fmt.Errorf("Invalid Token Response [%v]", res.StatusCode)
    }
    ringCXToken := RingCXToken{}
    bytes, err := ioutil.ReadAll(res.Body)
    if err != nil {
        return "", err
    }
    err = json.Unmarshal(bytes, &ringCXToken)
    return ringCXToken.AccessToken, err
}

func main() {
    rctoken := "VALID-RINGCENTRAL-ACCESS-TOKEN"

    ringcxToken, err := RcToRingCXToken(rctoken)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(ringcxToken)
}
var https = require('https')
var querystring = require('querystring')

var RC_ACCESS_TOKEN = "VALID-RINGCENTRAL-ACCESS-TOKEN"

var url = "engage.ringcentral.com"
var path = '/api/auth/login/rc/accesstoken?includeRefresh=true'
var body = querystring.stringify({
  rcAccessToken: RC_ACCESS_TOKEN,
  rcTokenType: 'Bearer'
})
var headers = {
  'Content-Type': 'application/x-www-form-urlencoded',
  'Content-Length': Buffer.byteLength(body)
}

var options = {host: url, path: path, method: 'POST', headers: headers};
var post_req = https.request(options, function(res) {
      var response = ""
      res.on('data', function (chunk) {
          response += chunk
      }).on("end", function(){
          if (res.statusCode == 200){
              var tokensObj = JSON.parse(response)
              console.log(tokensObj.accessToken)
          }else{
              console.log(response)
          }
      });
    }).on('error', function (e) {
        console.log(e)
    })
post_req.write(body);
post_req.end();
import json
import requests

RC_ACCESS_TOKEN = "VALID-RINGCENTRAL-ACCESS-TOKEN"
url = "https://engage.ringcentral.com/api/auth/login/rc/accesstoken?includeRefresh=true"
body = {
    "rcAccessToken": RC_ACCESS_TOKEN,
    "rcTokenType": "Bearer"
}
headers = {
              'Content-Type': 'application/x-www-form-urlencoded'
          }
try:
    res = requests.post(url, headers=headers, data=body)
    if res.status_code == 200:
        jsonObj = json.loads(res.content)
        print (jsonObj['accessToken'])
    else:
        print (res.content)
except Exception as e:
    raise ValueError(e)
<?php
$RC_ACCESS_TOKEN = "VALID-RINGCENTRAL-ACCESS-TOKEN";

$url = "https://engage.ringcentral.com/api/auth/login/rc/accesstoken?includeRefresh=true";
$body = http_build_query(array(
    'rcAccessToken' => $RC_ACCESS_TOKEN,
    'rcTokenType' => 'Bearer'
));
$headers = array ('Content-Type: application/x-www-form-urlencoded');

try{
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_POST, TRUE);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
    curl_setopt($ch, CURLOPT_TIMEOUT, 600);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
    $strResponse = curl_exec($ch);
    $curlErrno = curl_errno($ch);
    if ($curlErrno) {
        throw new Exception($curlErrno);
    } else {
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        if ($httpCode == 200) {
            $tokensObj = json_decode($strResponse);
            print ($tokensObj->accessToken);
        }else{
            print ($strResponse);
        }
    }
}catch (Exception $e) {
    throw $e;
}

Response

The response contains the RingCX accessToken that you use for RingCX Voice API calls. If you include includeRefresh=true, the response also contains a refreshToken that can be used to obtain a new RingCX access token.

Save the following values:

  • accessToken: The RingCX bearer token for API requests.
  • refreshToken: The RingCX refresh token, returned when includeRefresh=true.
  • mainAccountId and account IDs in agentDetails: Useful when choosing the account or sub-account for API calls.

The following is an abbreviated response.

{
  "refreshToken":"<rcRingCXRefreshToken>",
  "accessToken":"<rcRingCXAccessToken>",
  "tokenType":"Bearer",
  "mainAccountId":"333333",
  "agentDetails":[
    {
      "agentId":111111,
      "firstName":"John",
      "lastName":"Wang",
      "email":"john.wang@example.com",
      "username":"john.wang@example.com",
      "agentType":"AGENT",
      "rcUserId":222222,
      "accountId":"333333",
      "accountName":"RingForce",
      "agentGroupId":null,
      "allowLoginControl":true,
      "allowLoginUpdates":true
    }
  ],
  "adminId":1111,
  "adminUrl":"/voice/admin/",
  "agentUrl":"/voice/agent/",
  "ssoLogin":true
}

Token lifetime and rate limits

RingCX access tokens expire after 5 minutes. The token exchange endpoint is limited to 5 requests per minute.

Do not request a new RingCX access token before every API call. Cache and reuse the token until it is close to expiration, then obtain a new token. If your application has multiple workers or servers, coordinate token renewal so they do not all try to re-authenticate at the same time.

RingCX does not automatically refresh the token for you. If an API call returns 401 Unauthorized because the token expired, your application must obtain a new valid RingCX token before retrying the request.

Example RingCX API call

Use the RingCX access token in the Authorization header.

GET https://engage.ringcentral.com/voice/api/v1/admin/users
Authorization: Bearer <ringcxAccessToken>

Get accounts

You can retrieve the RingCX accounts available to the authenticated user. This request uses the RingCX access token returned by the token exchange.

GET https://engage.ringcentral.com/voice/api/v1/admin/accounts
Authorization: Bearer <ringcxAccessToken>

Here is an example cURL command:

curl -X GET 'https://engage.ringcentral.com/voice/api/v1/admin/accounts' \
  -H 'Authorization: Bearer <ringcxAccessToken>'

The response includes the RingCX account IDs you need for RingCX Voice API requests:

  • accountId is the RingCX sub-account ID. Most operational API calls are performed against a sub-account.
  • mainAccountId is the RingCX main account ID. The main account is the top-level account.

Some RingCX APIs also require the RingCentral account UID. To retrieve that value, call the RingCentral account endpoint with the RingCentral/RingEX access token from Retrieve a RingCentral access token.

GET https://platform.ringcentral.com/restapi/v1.0/account/~
Authorization: Bearer <ringCentralAccessToken>

In that response, the id value is the RingCentral account UID. Use the RingCentral/RingEX access token for this request, not the RingCX access token.

Refresh a RingCX access token

If you requested a refresh token with includeRefresh=true, call the refresh endpoint to obtain a new RingCX access token.

Request

POST https://engage.ringcentral.com/api/auth/token/refresh
Content-Type: application/x-www-form-urlencoded

refresh_token=<ringcxRefreshToken>

Where:

  • <ringcxRefreshToken> is the RingCX refresh token returned by the token exchange or previous refresh response.

Response

The response contains a new accessToken and a new refreshToken. Save the new refresh token for the next refresh request.

The following is an abbreviated response.

{
  "refreshToken":"<newRcRingCXRefreshToken>",
  "accessToken":"<rcRingCXAccessToken>",
  "tokenType":"Bearer",
  "mainAccountId":"333333",
  "agentDetails":[
    {
      "agentId":111111,
      "firstName":"John",
      "lastName":"Wang",
      "email":"john.wang@example.com",
      "username":"john.wang@example.com",
      "agentType":"AGENT",
      "rcUserId":222222,
      "accountId":"333333",
      "accountName":"RingForce",
      "agentGroupId":null,
      "allowLoginControl":true,
      "allowLoginUpdates":true
    }
  ],
  "adminId":1111,
  "adminUrl":"/voice/admin/",
  "agentUrl":"/voice/agent/",
  "ssoLogin":true
}

If you did not request or store a refresh token, repeat the RingCentral-token-to-RingCX-token exchange with a valid RingCentral access token.

If the RingCX access token is expired, API requests may return:

401 Unauthorized

Jwt is expired