Use Message Sync API to archive your SMS, Fax and Voicemail Messages

Embbnux Ji
RingCentral Developers
4 min readMay 15, 2018
message sync

Messages are not just critical for the modern day business, but extremely useful in ensuring quality and customer satisfaction. With RingCentral’s API, you can easily retrieve message data for analysis, archival and compliance. The API provides access to all messages include voicemail, SMS and fax.

RingCentral’s Message Store API provides a common way to access message data and is often used with time filters to retrieve data on a periodic basis. The Message Sync API is an alternate way to retrieve the same data with an easier approach using a sync token. When calling the API with a sync token, you will receive new and updated messages from your previous sync, easing the retrieval process. RingCentral uses and recommends the Message Sync API for efficient syncing purposes.

This article will show how to sync all your messages data from RingCentral using our Data API using a demo app. You can also find the source code in this Github Repo.

Prerequisites

  1. A free RingCentral Developer account to create a RingCentral app
  2. Node.js with version ≥ 8
  3. NPM or yarn

Create a new RingCentral app

Before we can use the RingCentral API, we will need to create or login to our RingCentral Developers account and create an app. To create an app, follow the steps in this tutorial.

Step 2: Choose Your App Type & Platform Type

Next, you need to choose Private and Server-only (No UI) type. This will enable your app to use the Password flow Authorization flow. We will run our code in a Node.js server.

private-server-only-app

Step 3: Add Permissions

Add Read Messages and Webhook Subscriptions permissions to your app.

Setup the Demo App

$ git clone git@github.com:embbnux/ringcentral-message-sync-demo.git
$ cd ringcentral-message-sync-demo

Create a .env file in project root path with your app and user credentials:

RINGCENTRAL_SERVER_URL=your_ringcentral_app_server, eg: https://platform.devtest.ringcentral.com
RINGCENTRAL_CLIENT_ID=your_ringcentral_app_client_id
RINGCENTRAL_CLIENT_SECRET=your_ringcentral_app_client_secret
RINGCENTRAL_USERNAME=your_ringcentral_phone_number
RINGCENTRAL_EXTENSION=your_ringcentral_extension, eg: 101
RINGCENTRAL_PASSWORD=your_ringcentral_password

Start the server:

$ yarn start

How the App works

First, the app uses the RingCentral JS SDK to handle authorization.

async function initSDK() {
let authorizationData;
if (fs.existsSync(authorizationFilePath)) {
authorizationData = JSON.parse(fs.readFileSync(authorizationFilePath, 'utf-8'));
}
rcsdk = new RingCentral({
appKey: process.env.RINGCENTRAL_CLIENT_ID,
appSecret: process.env.RINGCENTRAL_CLIENT_SECRET,
server: process.env.RINGCENTRAL_SERVER_URL
});
// set authorization token to sdk
if (authorizationData) {
rcsdk.platform().auth().setData(authorizationData);
} else {
try {
const response = await rcsdk.platform().login({
username: process.env.RINGCENTRAL_USERNAME,
extension: process.env.RINGCENTRAL_EXTENSION,
password: process.env.RINGCENTRAL_PASSWORD,
});
fs.writeFileSync(authorizationFilePath, JSON.stringify(response.json(), null, 2));
} catch (e) {
console.error('loginFail:', e.message);
throw e;
}
}
rcsdk.platform().addListener('refreshSuccess', () => {
const tokenData = rcsdk.platform().auth().data();
fs.writeFileSync(authorizationFilePath, JSON.stringify(tokenData, null, 2));
});
}

Next, the app uses the Message Sync API to fetch messages from RingCentral Platfrom.

async function syncMessages() {
let syncInfo;
if (fs.existsSync(syncInfoFilePath)) {
syncInfo = JSON.parse(fs.readFileSync(syncInfoFilePath, 'utf-8'));
}
let syncParams;
if (!syncInfo) {
// Full sync in first time without syncToken
const dateFrom = new Date();
dateFrom.setDate(dateFrom.getDate() - 30);
syncParams = {
syncType: 'FSync',
dateFrom: dateFrom.toISOString(),
};
} else {
syncParams = {
syncToken: syncInfo.syncToken,
syncType: 'ISync'
};
}
try {
const response = await rcsdk.platform().get('/restapi/v1.0/account/~/extension/~/message-sync', syncParams);
const newData = response.json();
syncInfo = newData.syncInfo;
fs.writeFileSync(syncInfoFilePath, JSON.stringify(syncInfo, null, 2));
let messages = [];
if (fs.existsSync(messagesFilePath)) {
messages = JSON.parse(fs.readFileSync(messagesFilePath, 'utf-8'));
}
if (newData.records && newData.records.length > 0) {
messages = messages.concat(newData.records);
fs.writeFileSync(messagesFilePath, JSON.stringify(messages, null, 2));
}
} catch (e) {
console.error('syncFail:', e.message);
}
}

The first time the app syncs the data we need to perform a full sync using the Fsync sync type to get the base data. After that, we can use a syncToken to perform incremental sync using the Isync sync type to retrieve new and updated data.

The max number of messages that sync endpoint returns is 250. When there are records more than 250, it will only return newest 250 records. If there are older records the olderRecordsExist property will be present and, to get older requests, we can make requests setting the dateTo property as the earliest date in our response. Following is example code shows how to handle this.

async function syncAPI(params) {
const response = await rcsdk.platform().get('/restapi/v1.0/account/~/extension/~/message-sync', params);
const { records, syncInfo } = response.json();
if (!syncInfo.olderRecordsExist || records.length === 0) {
return { records, syncInfo };
}
const oldestCreationTime = (new Date(records[records.length - 1].creationTime)).toISOString();
const {
recordCount,
...paramsWithoutRecordCount
} = params;
const oldData = await syncAPI({
...paramsWithoutRecordCount,
dateTo: oldestCreationTime
});
return {
records: records.concat(oldData.records),
syncInfo,
}
}

For this demo, we sync messages every 5 minutes. But you can use a Webhook to keep your data up-to-date.

More Information

You can get more information on the message sync API from the API Reference:

Hopefully this article was helpful. Please let us know what you think by leaving your questions and comments below.

--

--