If you need to create interoperability among two or more communication systems which depend upon the "Available" or "Unavailable" state of a RingCentral User, you may be asking yourself one or more of the following questions:
- Can you use the RingCentral API to accomplish this goal?
- What tools are available for developers to use to achieve this goal?
- How do you develop with these tools and services to accomplish this goal?
In this article, we will answer these questions...
Let's start by answering the easy question, "Can you use the RingCentral API to accomplish this goal"?
Heck yeah you can do this! It is actually pretty easy once you understand the API a bit (which we'll cover).
Here is a working example, but it requires the Device API resource to be enabled for the respective RingCentral app. If you would like to use this example, but require that permission, please submit a case or email devSupport[at]ringcentral.com with your Application's name, RingCentral Account info, and a message you need the Devices API resource enabled for this app. https://github.com/bdeanindy/Message-Dispatcher
What tools are available for developers to use to achieve this goal?
- RingCentral Developer Portal - Create New App (or you can use an existing app in the Sandbox):
https://developers.ringcentral.com/my-account.html#/create-app - Extension API Resource:
https://developer.ringcentral.com/api-explorer/latest/index.html#/Extension - Subscription API Resource:
https://developer.ringcentral.com/api-explorer/latest/index.html#/!/Push_Notifications/createSubscription - Presence API Resource:
https://developer.ringcentral.com/api-explorer/latest/index.html#/!/Presence/getPresenceStatus - Developer Guide - Get Extension(s) Presence status by ID:
https://developer.ringcentral.com/api-docs/latest/index.html#!#RefGetExtensionPresence
RingCentral JS SDK:
https://github.com/ringcentral/ringcentral-js - RingCentral JS SDK Helpers:
https://github.com/ringcentral/ringcentral-js-helpers - Node.js:
https://nodejs.org/
How do you develop with these tools and services to accomplish this goal? (using Node.js for example)
- [optional] Setup your app/integration's environment variables. I use the DotEnv package myself to simplify local development and to easily configure deployment environment variables in production with my chosen task runner such as Grunt or Gulp. You will need the following environment variables:
- RC_APP_KEY
- RC_APP_SECRET
- RC_API_SERVER
- RC_USERNAME
- RC_PASSWORD
- RC_EXTENSION
- PAGE
- PER_PAGE
You can retrieve the API Keys for your application defined in the RingCentral Developer Portal:
Developer Portal->My Apps->{{APP_NAME}}->Credentials
You will need the following values: API Key, and API Secret
Since you are just getting started developing this, you should be using the Sandbox URL from below.
I've listed both, just in case you need them:
- Sandbox URL: https://platform.devtest.ringcentral.com/restapi/v1.0/oauth/token
- Production URL: https://platform.devtest.ringcentral.com/restapi/v1.0/oauth/token - Install the RingCentral JS SDK and RingCentral JS SDK Helpers modules in your Node.js app
npm install --save ringcentral ringcentral-helpers
- Create a variable to cache extension data and maintain state of the various Extensions you wish to monitor
var monitoredExtensions = [];
var _cachedInitEventFilters = []; - Implement one of the RingCentral SDKs to save you time and simplify your development with our APIs.
I'm going to use the RingCentral JS SDK and RingCentral JS SDK Helpers...var RC = require('ringcentral');
var RCH = require('ringcentral-helpers');
var Extension = RCH.extension();
var Presence = RCH.presence(); - Instantiate the SDK
// Must define each of these in app's environment vars, I use the dotenv package myself
var sdk = new RC({
appKey: process.env.RC_APP_KEY,
appSecret: process.env.RC_APP_SECRET,
server: process.env.RC_API_SERVER
}); - Create a reference variable to the RingCentral Platform Singleton
var platform = sdk.platform(); - Create a new Subscription
var subscription = sdk.createSubscription();
- Authenticate with the RingCentral API using your app's API Keys, making sure to use the correct URL associated with your app.
// Must define each of these in your app's environment variables, I use the dotenv package myself
platform.login({
username: process.env.RC_USERNAME,
password: process.env.RC_PASSWORD,
extension: process.env.RC_EXTENSION
}); - Use our Extension API resource within an app/integration to fetch the list of extensions from an account to obtain the associated Extension ID properties (being aware of the difference between an extension's number and an extension's ID, as the ID is predominantly used throughout the various RingCentral API resources in routes).
You should cache this list to refer to it later. This will improve performance by eliminating latency from subsequent requests, and is handy if your app/integration does not expect to be in sync with any new Extensions which have been added since the initial request you use to bootstrap your app/integration with data. I will address this use-case in a later article or an update to this article.function init (ringcentralCredentials) {
var extensions = [];
var pageCounter = 1;
function getExtensionsPage() {
console.log('Requesting page: ', process.env.PAGE);
return platform
.get('/account/~/extension', {
page: process.env.PAGE,
perPage: process.env.PER_PAGE // reduce number to speed bootstrapping
})
.then(function(response) {
var data = response.json();
extensions = extensions.concat(data.records);
if (data.navigation.nextPage) {
pageCounter++;
return getExtensionsPage(); // this will be chained
} else {
return extensions;
}
});
}
return getExtensionsPage()
.then(function(extensions) {
console.log('Now you have all the extensions');
// You can optionally filter the extensions, this is helpful if you only want a subset of extensions to monitor.
// Then we'll organize this data into our cached extensions variable
return extensions.filter(getEnabledUsers).map(organize);
})
.then(startSubscription)
.catch(function(e) {
console.error(e);
throw e;
});
}; - Once you have the list of Extension IDs for which you need to monitor their state data, filter for the ones you want to monitor.
// Our example will only monitor extensions where type === 'User', and status === 'Enabled'
function getEnabledUsers (extension) {
return ( -1 !== 'User'.indexOf(extension.type) && -1 !== 'Enabled'.indexOf(extension.status) );
} - Map across the list of extensions and append an Event Filters on each Extension in the cached list which you will need to supply to the Subscription.
function organize(ext, idx, extensions) {
var eventFilter = generateEventFilter(ext.id);
monitoredExtensions[idx] = ext;
monitoredExtensions[idx]['eventFilter'] = eventFilter;
_cachedInitEventFilters.push(eventFilter);
}
// Adding the detailed telephony information so we get more data from our presence events
function generateEventFilter(item) {
return '/account/~/extension/' + item.extension.id + '/presence?detailedTelephonyState=true';
} - Add eventFilters to the Subscription instance, and register the Subscription. Subscriptions expect to be provided an array of strings, each string is the relative path associated with an Extension to obtain the Presence data. When providing these to a Subscription, they are called "Event Filters" or "eventFilters". Event Filters are used by a Subscription to identify the Extensions the Subscription should watch within a RingCentral account you wish to receive Presence Event data (aka: Notifications).
This is the best-practice, and recommended way to determine if an Extension is on a call, or available. Some developers have tried long-polling a list of Extensions' active-calls property from our API, but this is frowned upon and makes your application less performant, prone to throttling errors, and generally is a poor substitute for receiving push notifications which contain the real-time Presence Event data for one or more Extensions.
Please note: you do not need to create a new Subscription for each event filter, in fact you will find that only 20 Subscriptions can exist for a given application and authenticated user at any given time as a restriction by the RingCentral API. A single Subscription instance can monitor up to one thousand (1000) Event Filters, but the recommended maximum for performance is 500.function startSubscription(extensions) {
subscription
.setEventFilters(_cachedInitEventFilters)
.register();
} - Create the Subscription's event listeners. This allows us to handle events related to the subscription as well as to monitor inbound Presence Events.
// Register Subscription Event Listeners
subscription.on(subscription.events.notification, handleSubscriptionNotification);
subscription.on(subscription.events.removeSuccess, handleRemoveSubscriptionSuccess);
subscription.on(subscription.events.removeError, handleRemoveSubscriptionError);
subscription.on(subscription.events.renewSuccess, handleSubscriptionRenewSuccess);
subscription.on(subscription.events.renewError, handleSubscriptionRenewError);
subscription.on(subscription.events.subscribeSuccess, handleSubscribeSuccess);
subscription.on(subscription.events.subscribeError, handleSubscribeError);
//Event Listeners
function handleSubscriptionNotification (msg) {
console.log('SUBSCRIPTION NOTIFICATION: ', JSON.stringify(msg));
// Add the logic to control how you maintain state for the extensions
monitoredExtensions.forEach(ext, idx, arr) {
if (msg.extension.id === ext.id) ext[state] = msg.presenceStatus;
}
// [optionally] You can add logic to do things such as an HTTP POST for psuedo-webhooks
}
function handleRemoveSubscriptionSuccess (msg) {
// called when a subscription has been successfully removed (unregistered)
}
function handleRemoveSubscriptionError (msg) {
// called when a subscription has been failed removal (unregistered)
}
function handleSubscriptionRenewSuccess (msg) {
// called when a subscription has been successfully renewed to continue receiving Notifications
}
function handleSubscriptionRenewError (msg) {
// called when a subscription has failed being renewed to continue receiving Notifications
}
function handleSubscribeSuccess (msg) {
// called when a subscription as been successfully registered
}
function handleSubscribeError (msg) {
// called when a subscription has failed to register
}
Our Extension + Subscription API resources can be used within an app/integration to fetch extensions from an account to obtain their ID, and then to create a single Subscription which expects Presence Event filters. The Subscription has eventHandlers which can be used when new notification (Presence Events) happen within the account for the watched Extensions.
The event data from the above notifications can be used to update state for connectivity relating to another communication service of your workflow and used as a gate to determine if someone is available and if a call should be forwarded.
Optionally you could bootstrap this during initialization by calling the Presence API resource for each extension to establish initial state.
This should help to demystify Subscriptions and how to use them.
Please feel free to comment or ask questions if you require further clarification.