Build a Stunning RCS Reminder App With Node SDK and Vonage
Published on January 15, 2025

Rich Communication Services (RCS) are coming not only to iOS 18 but also to Vonage. Starting in early fall 2024, you can enable sending RCS through the Messages API. RCS is just as easy to use as the Vonage Node SDK.

Rich Communications Services (RCS) is an advanced messaging protocol that enhances traditional SMS and MMS with features like real-time messaging, image and video sharing, and file transfers. It supports interactive elements such as group chats, typing notifications, and message status updates. Unlike SMS, which uses cellular networks, RCS works over cellular data and Wi-Fi, offering a more versatile and modern messaging experience similar to popular apps like WhatsApp and Facebook Messenger. Check out this link for more information on how RCS works with Vonage.

To showcase how you can use RCS with Vonage’s messaging API, we will build a simple app to remind us to drink water. The result will look something like this:

A screen grab of the message. We see the title “Drink Water Reminder,” a message that reads: “Did you drink water today?, and a button to press with the word “Yes"Drink Water Reminder

Prerequisites

Vonage API Account

To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.

Register an RCS Agent With Google Business Messaging

You will need an RCS Agent linked to your Vonage account to send and receive RCS messages via the Vonage Messages API. To get the agent set up, contact Vonage Support or your account manager to get the ball rolling. Once the agent is added to your account, you should see it listed under “External Accounts” in your dashboard.

This is an image of the developer dashboard. “External Accounts” is highlighted in the left-hand navigation bar. The bottom shows the RBM linked to the account.Developer Dashboard

Create a Vonage Application

The next step is to create a new Vonage Application. This will generate credentials and define specific settings for our RCS reminder app, such as webhook URLs. Follow these steps to create and configure your application:

  1. Click “Generate a new Application.”

  2. Give it a name.

  3. Click “Generate private and public key” (the private key will be downloaded to your computer; save that for later)

  4. Enable the “Messages” capability. 

    • The URL for the hooks will depend on what you get from ngrok. But the path should be /inbound and /status, respectively.

A screenshot of the “Create Application” form in the dashboard. The values for the form are: “RCS Test” for the name, and “https://<redacted>.ngrok.io/inbound” and “https://<redacted>.ngrok.io/status”A screenshot of the “Create Application” form in the developer dashboard

We now need to link our RCS Agent to the Vonage application.

Bootstrap the Project

Create a new directory for the project and initialize it with npm.

npm init -y

Then, we will install the packages we need.

npm install @vonage/jwt @vonage/auth @vonage/messages @vonage/server-sdk @vonage/jwt cron dotenv express jsonwebtoken uuid

(Note: Some of the vonage packages are included with @vonage/server-sdk, but it is always a good idea to be implicit with the packages required)

Here is a quick explanation of what each package provides:

  • @vonage/server-sdk,@vonage/messages, @vonage/auth, and @vonage/jwt will be used to send and receive messages

  • cron is a package used to execute code on a schedule 

  • dotenv dynamically loads .env files,

    which will be used to configure our app

  • express is used as a web server to process our messages 

  • jsonwebtoken will be used to validate JWT tokens 

  • uuid creates unique identifiers 

Copy over the private.key into our application folder, then create a .env file with the following:

VONAGE_APPLICATION_ID=<your application id>
VONAGE_PRIVATE_KEY_FILE=<full path to the private key>
VONAGE_BRAND_NAME=<id of your RBM account>
REMINDER_NUMBER=<your phone number or number you want to send the reminder to>

Create a file called server.js and bootstrap using this express boilerplate.

const Express = require('express');
require('dotenv').config();

const app = new Express();
const port = process.env.PORT || 3000;

// Catches async functions for express
const catchAsync = (fn) => (req, res, next) => {
  fn(req, res, next).catch(next);
};

app.use(Express.json());

// Hello World
app.all('/', catchAsync(async (req, res) => {
  console.log('Hello World', req.body);
  res.status(200).json({hello: 'World'});
}));

// Setup a 404 handler
app.all('*', (req, res) => {
  res.status(404).json({
	status: 404,
	title: 'Not Found',
  });
});

// Setup an error handler
app.use((err, req, res, next) => {
  res.status(500).json({
	status: 500,
	title: 'Internal Server Error',
	detail: err.message,
  });
});

Then update package.json to have a start script:

{
  // ...
  "scripts": {
    "start": "node server.js"
  }
}

Now you can run npm run start and ngrok, then navigate to the ngrok URL. You should see “Hello World” in JSON format. That means we have successfully bootstrapped Express.

How to Send Reminder Messages

Now, we will send out the reminder. We will schedule a function to run at a regular interval using the cron package. Cron expressions are outside the scope of this post, but if you want to play around with how often the reminder goes out, I suggest Crontab.guru to help build out the expression. 

How to Setup the Vonage Node SDK

Let's first set up the SDK to send messages. You will need to enter the application ID and the private key. Usually, you can pass the path to the private key, and the SDK will read it. However, we will need the private key in the next step, so we will read the file and store it in a variable instead.

// Add this to the top of the file
const { Auth } = require('@vonage/auth');
const { Vonage } = require('@vonage/server-sdk');
const { readFileSync } = require('fs');

// This can go anywhere else
const key = readFileSync(process.env.VONAGE_PRIVATE_KEY_FILE).toString();

const auth = new Auth({
  applicationId: process.env.VONAGE_APPLICATION_ID,
  privateKey: key,
});

const vonage = new Vonage(auth);

How to Send a Rich Card Reminder

We will generate our own JWT token, which can then be used for the inbound webhook to ensure we get messages from Vonage, but also because they have a built-in expiry. We want to give the user a set amount of time to respond. By using a JWT, we fulfill both requirements.

First, we will register for the cron job. We will run it every five minutes to check if the recipient has acknowledged that they had a glass of water.

// Add to top of the file
const { CronJob } = require('cron');

// Setup the cronjob
const job = new CronJob(
  // Run every 5 minutes
  '5 * * * * *',
  () => {
    sendReminder(reminderNumber);
  },
  null, // Data to pass into the cron function
  true, // Automatically start
  process.env.TZ || 'America/New_York', // The timezone
);

Next, we will write some functions to check whether we need to send a message. We store the reminder in an array with the token generated from sending the message. 

// Storage for sent reminders
const reminders = [];

// Check to see if we have sent a reminder 
const checkForReminders = (number) => {
  const search = reminders.find((reminder) => reminder.number === number);
  if (!search) {
	console.log('No reminder found');
	return false;
  }

  const { token } = search;
  console.log('Checking token', token);
  return validateToken(token);
};

// Send the reminder
const sendReminder = async (number) => {
  console.log('Checking for reminders');

  if (checkForReminders(number)) {
	console.log('Reminder already sent');
	return;
  }

  const token = await sendMessage(number, (job.nextDate() / 1000) + 10000);
  reminders.push([{number, token}]);
};

Now, on to sending the message. RCS messaging provides many different message types, including Rich Cards. These cards contain all the content the client needs to render the message. Within the RCS channel of the Vonage Messages API, Rich Cards are sent using the "custom" message type with a button to reply with“YES.” We build a token and send the message along. 

const sendMessage = async (number, exp) => {
  console.log('Sending message to', number);
  const token = tokenGenerate(
    auth.applicationId,
    auth.privateKey,
    {
      reminderId: uuid(),
      ...(exp ? {exp: exp} : {}),
    },
  );

  const message = new RCSCustom({
    to: number,
    from: process.env.VONAGE_BRAND_NAME,
    custom: {
  	contentMessage: {
    	  richCard: {
           standaloneCard: {
           cardOrientation: 'VERTICAL',
           cardContent: {
              title: 'Drink Water Reminder',
              description: 'Did you drink water today?',
              media: {
                height: 'SHORT',
                contentInfo: {
// Add a 
                  fileUrl: 'https://as1.ftcdn.net/jpg/02/22/48/50/220_F_222485075_uAeqmITGagEGdy9D4nWVou0a6dj6EuUz.jpg',
                },
              },
              suggestions: [
                {
                  reply: {
                    text: 'Yes',
                    postbackData: token,
                  },
                },
              ],
            },
          },
    	  },
       },
    },
  });

  console.log('Sending reminder', message);
  try {
	const res = await vonage.messages.send(message);
	console.log('Message sent', res);
	return token;
  } catch (err) {
	console.error('Error sending message');
	err.response.text().then((text) => {
  	console.log(text);
	});
  }
};

Now, when we run npm run start after about five minutes, our phone receives a message asking if we drank water.

Acknowledging the Message

So, how do we prevent the message from going out again? Well, that is where the Webhook comes in. When the user presses “Yes,” the client will respond to the RCS Agent, which is then forwarded to Vonage. We then call a webhook with the data attached so you can process it. The message will look like this:

{
   "channel": "rcs",
   "message_uuid": "aaaaaaaa-bbbb-4ccc-8ddd-0123456789ab",
   "to": "Vonage",
   "from": "<Number>",
   "timestamp": "2020-01-01T14:00:00.000Z",
   "context_status": "none",
   "message_type": "reply",
   "reply": {
  	"payload": "<The JWT Token>",
  	"text": "Yes"
   }
}

Notice that from and to have swapped since this came from the phone. Using this payload, we can find our reminder and remove it. (We are also validating the token signed by us, which can’t be easily forged).

app.all('/inbound', catchAsync(async (req, res) => {
  console.log('Inbound', req.body);

  const {channel, message_type, reply, from} = req.body;
  if (channel !== 'rcs') {
	console.log('Not RCS');
	res.status(200).json({ok: true});
	return;
  }

  if (message_type !== 'reply') {
	console.log('Not reply');
	res.status(200).json({ok: true});
	return;
  }

  const { id } = reply|| {};

  console.log('Id', id);

  if (!id) {
	console.log('No id');
	res.status(200).json({ok: true});
	return;
  }

  if (!validateToken(id)) {
	console.log('Invalid id');
	res.status(200).json({ok: true});
	return;
  }

  const index = reminders.findIndex((reminder) => reminder.number === from);
  reminders.splice(index, 1);
  console.log('Reminder removed', reminders);

  res.status(200).json({ok: true});
}));

And there you have it! Sending and receiving RCS messages is a breeze with the messages API. Now, you can build rich messaging applications with a few lines of code. It is best to keep these messages conversational rather than transactional. This way, you can improve your users' experience by guiding them instead of informing them.

Next Steps

RCS is set to enhance messaging experiences with advanced features and integration possibilities. With Vonage’s Messages API, you can easily incorporate RCS into your applications, bringing real-time messaging and rich interactive elements to your users. Starting in early fall 2024, Vonage will make enabling RCS through its platform simple. This opens up exciting opportunities to build engaging and feature-packed messaging solutions. To get started, check out the RCS Custom Messages Documentationfor more information on integrating other RCS message types with Vonage. You can check out this GitHub repository to see a full working version of this code.

Join the conversation on our Vonage Community Slack or send us a message on X, formerly known as Twitter.

Chuck ReevesSr JavaScript Developer Advocate

Long ago, in the dark ages before Google and StackOverflow, Chuck learned how to program. Those were the times when all you had to go on was the documentation or the source code itself. From humble beginnings as a Jr Full Stack developer, Chuck grew into the developer he is today, building out the tools that empower fellow developers to create amazing products. When he is not forging new tools, you can find him climbing up a mountain or on his bike.

Ready to start building?

Experience seamless connectivity, real-time messaging, and crystal-clear voice and video calls-all at your fingertips.

Subscribe to Our Developer Newsletter

Subscribe to our monthly newsletter to receive our latest updates on tutorials, releases, and events. No spam.