Create WebSocket subscriptions using RingCentral JavaScript SDKs

A quick start guide for RingCentral WebSocket Subscriptions

Tyler Liu
4 min readJan 18, 2023

Please note that, the feature described in this article is currently in beta, there will be no official support from RingCentral at the moment.

What are subscriptions for?

First things first, why do you need to create subscriptions? If your application needs to get notified when something happens (like new sms received, new incoming call…etc), you need to create subscriptions to those events. Creating a subscription is like telling RingCentral server that your application is interested in some events. Then your application will get notified when such such events occur.

Different ways to create subscriptions

At the moment, there are 2 official ways to create subscriptions: WebHook and WebSocket. (There was a legacy way to use PubNub to create subscriptions that we do not recommend any more).

In this article, we will focus on creating subscriptions using WebSocket. If you are interesting in WebHooks, please read this article instead.

Prerequisites

Your RingCentral application needs to have the “WebSocket Subscriptions” permission.

If for some reason you cannot enable this permission for your application, please create a support case here.

For new developers

If you are new to programming against RingCentral API, you are recommended to use @rc-ex/core and @rc-ex/ws.

import RingCentral from '@rc-ex/core';
import WebSocketExtension from '@rc-ex/ws';

const rc = new RingCentral({
server: process.env.RINGCENTRAL_SERVER_URL,
clientId: process.env.RINGCENTRAL_CLIENT_ID,
clientSecret: process.env.RINGCENTRAL_CLIENT_SECRET,
});
const wsExtension = new WebSocketExtension();

const main = async () => {
await rc.authorize({
username: process.env.RINGCENTRAL_USERNAME!,
extension: process.env.RINGCENTRAL_EXTENSION,
password: process.env.RINGCENTRAL_PASSWORD!,
});
// install the WebSocket extension
await rc.installExtension(wsExtension);

// subscribe
await wsExtension.subscribe(
['/restapi/v1.0/account/~/extension/~/message-store'],
evt => {
console.log(JSON.stringify(evt, null, 2));
}
);

// trigger an event, optional
const ext = await rc.restapi().account().extension().get();
await rc
.restapi()
.account()
.extension()
.companyPager()
.post({
from: {extensionId: ext.id?.toString()},
to: [{extensionId: ext.id?.toString()}],
text: 'Hello world!',
});
};

main();

In the sample code above, we are using the RingCentral Extensible SDK. RingCentral Extensible is a SDK with a tiny core (@rc-ex/core) and lots of extensions. It is an endeavor to get rid of bloated SDK. You install extensions on demand. In order to support WebSocket, we also installed the @rc-ex/ws extension.

The code is pretty straightforward and self-explanatory, just remember to read the inline comments in the code snippet. There is a section to trigger an event, which is optional, you probably don’t need it in your own code.

For existing developers

If you are existing user of the official JavaScript SDK @ringcentral/sdk and you’d like to stick to it (instead of switching to the RingCentral Extensible SDK), there is a solution for you:

import {SDK} from '@ringcentral/sdk';
import RingCentral from '@rc-ex/core';
import WebSocketExtension from '@rc-ex/ws';
import RcSdkExtension from '@rc-ex/rcsdk';

const sdk = new SDK({
server: process.env.RINGCENTRAL_SERVER_URL,
clientId: process.env.RINGCENTRAL_CLIENT_ID,
clientSecret: process.env.RINGCENTRAL_CLIENT_SECRET,
});
const platform = sdk.platform();
const rc = new RingCentral();
const rcsdkExtension = new RcSdkExtension({rcSdk: sdk});
const wsExtension = new WebSocketExtension();

const main = async () => {
await platform.login({
username: process.env.RINGCENTRAL_USERNAME,
extension: process.env.RINGCENTRAL_EXTENSION,
password: process.env.RINGCENTRAL_PASSWORD,
});

// install the extensions
await rc.installExtension(rcsdkExtension);
await rc.installExtension(wsExtension);

// subscribe
await wsExtension.subscribe(
['/restapi/v1.0/account/~/extension/~/message-store'],
evt => {
console.log(JSON.stringify(evt, null, 2));
}
);

// trigger an event, optional
const r = await platform.get('/restapi/v1.0/account/~/extension/~');
const ext = await r.json();
platform.post('/restapi/v1.0/account/~/extension/~/company-pager', {
from: {extensionId: ext.id},
to: [{extensionId: ext.id}],
text: 'Hello world!',
});
};

main();

The code is very similar to the code we provided to new developers above. Please read the code explanation of previous section if you haven’t done so.

The different part is we are using @rc-ex/rcsdk to make @ringcentral/sdk an extension of the RingCentral Extensible SDK. You may continue to use @ringcentral/sdk in your projects. And the WebSocket Subscriptions is supported by @rc-ex/ws. @ringcentral/sdk and @rc-ex/ws could work toghether without issue.

For existing developers who don’t want to change their code

Let’s say you are already using both @ringcentral/sdk and @ringcentral/subscriptions and you really do not want to change your code. We have you covered!

@ringcentral/subscriptions is powered by WebSocket instead of PubNub since 5.0.0. And you just upgrade @ringcentral/subscriptions to latest version and no more coding change is required. (at the moment of I write this article, @ringcentral/subscriptions is not available yet. You may use the latest nightly release: https://www.npmjs.com/package/@ringcentral/subscriptions?activeTab=versions A nightly release is a release with version number starting with “0.0.1-”).

Please note that, your RingCentral app needs to have the “WebSocket Subscriptions” permission, please refer to the prerequisites section above.

Since you do not need to change your code, you probably do not need any sample code snippets. But here I provide one anyway:

import {SDK} from '@ringcentral/sdk';
import {Subscriptions} from '@ringcentral/subscriptions';

// init
const sdk = new SDK({
server: process.env.RINGCENTRAL_SERVER_URL,
clientId: process.env.RINGCENTRAL_CLIENT_ID,
clientSecret: process.env.RINGCENTRAL_CLIENT_SECRET,
});
const platform = sdk.platform();
const subscriptions = new Subscriptions({
sdk: sdk,
});

const main = async () => {
// login
await platform.login({
username: process.env.RINGCENTRAL_USERNAME,
extension: process.env.RINGCENTRAL_EXTENSION,
password: process.env.RINGCENTRAL_PASSWORD,
});

// subscribe
const subscription = subscriptions.createSubscription();
subscription.on(subscription.events.notification, evt => {
console.log(JSON.stringify(evt, null, 2));
});
await subscription
.setEventFilters(['/restapi/v1.0/account/~/extension/~/message-store'])
.register();

// trigger an event, optional
const r = await platform.get('/restapi/v1.0/account/~/extension/~');
const ext = await r.json();
platform.post('/restapi/v1.0/account/~/extension/~/company-pager', {
from: {extensionId: ext.id},
to: [{extensionId: ext.id}],
text: 'Hello world!',
});
};

main();

Sample output

We have provided 3 different solutions for different developers and use cases. No matter which solution you choose, the final result should be similar. If everything goes well, a notification message will be printed to console. Below is just a sample, what you get may be different:

{
"uuid": "8790364716118035345",
"event": "/restapi/v1.0/account/809646016/extension/62264425016/message-store",
"timestamp": "2023-01-18T22:07:17.532Z",
"subscriptionId": "2a4aafef-c9e0-4d9f-a34f-120ae5de8684",
"ownerId": "62264425016",
"body": {
"accountId": 809646016,
"extensionId": 62264425016,
"lastUpdated": "2023-01-18T22:07:11.635Z",
"changes": [
{
"type": "Pager",
"newCount": 2,
"updatedCount": 0,
"newMessageIds": [
1890265271016,
1890265273016
]
}
]
}
}

--

--