In this tutorial, we will be creating a Node.js application to have a basic understanding of how to make outgoing calls and handle incoming calls programmatically. We will be leveraging the Vonage Voice API and the Node.js Server SDK for Vonage APIs to do the heavy-lifting for us.
All of the code covered in this tutorial is available on the companion repository on GitHub in case you want to take a look at the code directly.
sudiptog81 / calls-with-js
Make and Receive Calls with JavaScript and the Vonage Voice API
Make and Receive Calls with JS and Vonage APIs
Quick Start
Clone and Install Dependencies
git clone https://github.com/sudiptog81/calls-with-js.git
cd calls-with-js
yarn
Override Environment Variables
Create a file named .env
as per the template given in .env.example
. Get the values for Vonage specific variables from the Vonage Dashboard.
Start an HTTP Tunnel with ngrok
Assuming PORT
has a value of 5000
in .env
.
ngrok http 5000
Override WebHook URLs in Vonage Dashboard
- Answer URL:
<ngrok-tunnel-url>/answer
- Event URL:
<ngrok-tunnel-url>/event
Run the Application
yarn dev
Call a Friend
Replace <number>
with your friend's number and <message>
with a custom message.
curl http://localhost:5000/call?to=<number>&msg=<message>
Ask Them to Call Back
Note: They will be rick-rolled!
License
The MIT Open-Source License.
Requirements
Make sure you have a recent version of the Node.js JavaScript runtime, a package manager for Node.js - npm
or yarn
- installed, and ngrok
for creating a public endpoint.
$ node -v
v15.11.0
$ npm -v
7.6.3
$ yarn -v
1.22.10
$ ngrok -v
ngrok version 2.3.35
We will also need a code editor such as Visual Studio Code. Apart from these, we will need a Vonage API account. If you donβt have one already, you can sign up today and start building with free credit.
Initializing Awesomeness
Create a new project directory (I'll call it js-calls
in this tutorial) and navigate to that directory in a terminal window.
mkdir js-calls
cd js-calls
Once that is done, use npm
or yarn
to initialise a new Node.js project.
yarn init -y # or npm init -y
The above command will accept all the defaults, so if you wish to override some of the values, you might prefer using the following instead:
yarn init # or npm init
We will add a few dependencies before we head on to the next section. express
is a web framework that we'll use to spin up a few HTTP endpoints, morgan
will be a request logger for the said endpoints, dotenv
will manage the environment variables for us and @vonage/server-sdk
will allow us to interact with the Vonage APIs.
nodemon
will be a development dependency that allows us to focus on developing the application without stopping it and running it again, essentially being a live reload solution that restarts the code whenever there is a change in the source code.
yarn add express morgan dotenv @vonage/server-sdk
yarn add -D nodemon
At this point, I like to add a couple of scripts in the package.json
file. I'd add a start
and a dev
script for this.
{
...
"scripts": {
"start": "node .",
"dev": "nodemon ."
},
...
}
Taking Care of Chores
In order to work with the Vonage Voice API, we need to create a Vonage Application and provision a virtual phone number. To do this, go to the Vonage dashboard and click on Your Applications
in the sidebar. Continue to Create a new application
.
Give your application a unique name to identify it on the dashboard. Generate a new public-private key pair and save the private key in the project directory.
Scroll down and click the button that says Generate new application
. Yay! You just created a Vonage application. You'll probably be redirected to the Application Overview page. If you scroll up, you will come across the Application ID
. There are some additional configurations that we'll come across as we get this going.
Next up, we have to reserve a phone number and link it to this newly created application. Go to the Numbers
section on the sidebar and navigate to Buy Numbers
. You'll be greeted with a form asking you for the country and features you want the number to be equipped with.
I'll go ahead with selecting United States
for this one. For the features, I'll tick only the VOICE
feauture, select Mobile
as the type and click on Search
. We are greeted with a list of numbers available. If you recall, signing up for a new account gave us a few credits, we may use it as well for reserving a number. Let's buy the first one in the list. It might open a popup asking you to confirm your choice and let you know of any limitations. We will not be using the SMS offering in this tutorial.
Once you purchase a number, head back to Your Applications
and click on the name of the application that you created for this tutorial. Scroll down and link the number you purchased, with this application.
Once all this is done, go to the Dashboard home by clicking on the Vonage icon and also take note of the API key and the API secret. Do not share the API secret with anyone!
At this point, you are aware of few key pieces of information that we will need soon - the Vonage number you purchased, the Vonage application ID, the API key and the API secret. Let's get to the code now.
Create a file named .env
and populate the values for environment variables with this information. Populate PORT
with the port number you want the application to listen on, I'll use 5000
. TO_NUMBER
will be a valid phone number in the E.164 format, without any leading +
, 00
or any other access code, that we define as the default number to call to.
When working with Vonage APIs, all phone numbers should be in this format, that is, the country code digits followed by the subscriber number.
VONAGE_NUMBER
, VONAGE_API_KEY
, VONAGE_API_SECRET
and VONAGE_APPLICATION_ID
can be obtained from the Vonage dashboard. Recall that you generated a public-private key pair while creating a Vonage application. Provide the absolute path to that file with the correct file path separator for your operating system and assign that path to VONAGE_PRIVATE_KEY_PATH
.
TO_NUMBER=
VONAGE_NUMBER=
VONAGE_API_KEY=
VONAGE_API_SECRET=
VONAGE_APPLICATION_ID=
VONAGE_PRIVATE_KEY_PATH=
PORT=
Making a Call
Create an index.js
file in the project directory, require and configure the dotenv
module early in the application and import the dependencies needed in this tutorial - they will be the Vonage Server SDK, Express.js and the Morgan middleware.
require('dotenv').config();
const Vonage = require('@vonage/server-sdk');
const express = require('express');
const morgan = require('morgan');
Next, instantiate an Express.js application and an instance of the Vonage
class. The constructor accepts an object with the following required keys - apiKey
, apiSecret
, applicationId
and privateKey
- all of which can now be accessed through the process.env
object.
const app = express();
const vonage = new Vonage({
apiKey: process.env.VONAGE_API_KEY,
apiSecret: process.env.VONAGE_API_SECRET,
applicationId: process.env.VONAGE_APPLICATION_ID,
privateKey: process.env.VONAGE_PRIVATE_KEY_PATH
});
Configure Express.js to parse JSON in the request body of POST
requests and to use the Morgan logger.
app.use(morgan('tiny'));
app.use(express.json());
In order to create a call with the Vonage SDK for Node.js, you need to call the vonage.calls.create()
method. This method takes two required arguments - the first one is a JavaScript object and the second one is a callback that is triggered after the SDK attempts to create the call. An example invocation can be as follows, assuming vonage
is an instance of Vonage
from @vonage/server-sdk
:
vonage.calls.create({
to: [{
type: 'phone',
number: process.env.TO_NUMBER
}],
from: {
type: 'phone',
number: process.env.VONAGE_NUMBER,
},
ncco: [{
action: 'talk',
text: 'This call was made from JavaScript.',
language: 'en-IN',
style: '4'
}]
}, (err, resp) => {
if (err)
console.error(err);
if (resp)
console.log(resp);
});
Here, the to
property accepts an array of JS objects which have the following fields - type
, number
, and optionally dtmfAnswer
. The from
property accepts a JS object that has the type
and number
fields. The ncco
property accepts a Nexmo Call Control Object that defines the flow of a call made using the Vonage Voice API. The Voice API Reference is also a great resource in know more about the form in which the API expects the requests to be.
Using a Nexmo Call Control Object, you can stream audio files into a call, connect different phones, send synthesized speech generated by a TTS module and what not. Do take a break and look at the documentation on NCCO!
Even if you try to run this application at this stage, assuming you have the environment variables set up correctly, you'll receive an error telling you that Voice Capabilities are not enabled in the Vonage application that we create using the dashboard. Let's backtrack a bit and revisit the application on the Vonage Dashboard.
Click on the Edit
button on the overview page of your application and flip the switch for Voice under the Capabilities section. You'll notice that there are a few text fields that need to be filled before we can turn this capability on - the first two are mandatory. These are expected to be webhook endpoints to which the Vonage APIs make HTTP requests to. If you observe carefully, these can be GET
or even POST
endpoints.
Let us go back to our code and define these endpoints. For the Event Webhook, we just need to return a status code 200
for all incoming requests. I like to use POST
for my endpoints when possible.
For the Event URL, we will use the /events
route and simply log the request body to the console and reply with a 200
status code.
app.post('/event', (req, res) => {
console.log(req.body);
res.status(200).send('');
});
We will take a look at how to handle incoming calls in the next section. At this point, we can assume that we shall use the /answer
route for the Answer URL webhook endpoint.
Configure the Express.js instance to listen on a specified port.
app.listen(process.env.PORT, () => console.log(`Running on port ${process.env.PORT}`));
At this point, we have a basic structure for the endpoints, however, we need a publicly accessible URL for these endpoints. We will use ngrok
to create a tunnel to our application running on localhost
. Execute the following command on another terminal window. Take note of the URLs that are displayed on the terminal.
ngrok http 5000 # replace this with the correct PORT
The web interface for ngrok
allows us to inspect and replay the HTTP requests being received by the tunnel. Requests sent to the two URLs displayed below that will be forwarded to our application. We now have predefined routes and a publicly accessible endpoint for our application. We can now fill in the details for enabling the Voice capability. Save the application on the dashboard once the details are filled in.
At this point, the index.js
file should look like this:
require('dotenv').config();
const Vonage = require('@vonage/server-sdk');
const express = require('express');
const morgan = require('morgan');
const app = express();
const vonage = new Vonage({
apiKey: process.env.VONAGE_API_KEY,
apiSecret: process.env.VONAGE_API_SECRET,
applicationId: process.env.VONAGE_APPLICATION_ID,
privateKey: process.env.VONAGE_PRIVATE_KEY_PATH
});
app.use(morgan('tiny'));
app.use(express.json());
vonage.calls.create({
to: [{
type: 'phone',
number: process.env.TO_NUMBER
}],
from: {
type: 'phone',
number: process.env.VONAGE_NUMBER,
},
ncco: [{
action: 'talk',
text: 'This call was made from JavaScript.',
language: 'en-IN',
style: '4'
}]
}, (err, resp) => {
if (err)
console.error(err);
if (resp)
console.log(resp);
});
app.post('/event', (req, res) => {
console.log(req.body);
res.status(200).send('');
});
app.listen(process.env.PORT, () => console.log(`Running on port ${process.env.PORT}`));
If you try running this application now, by executing yarn dev
, you should notice a call being made to the number as defined in TO_NUMBER
, and a few requests being received on the ngrok
web interface.
You should also see a request like this being sent to /events
:
Here is a recording of the call you should expect:
We can now clean this up by creating a /call
route responsible for making a call to a number and instructing the API to speak a message provided in the request. Replace the earlier invocation to vonage.calls.create()
with the following:
app.get('/call', (req, res) => {
vonage.calls.create({
to: [{
type: 'phone',
number: req.query.to || process.env.TO_NUMBER
}],
from: {
type: 'phone',
number: process.env.VONAGE_NUMBER,
},
ncco: [{
action: 'talk',
text: req.query.msg || 'This call was made from JavaScript.',
language: 'en-IN',
style: '4'
}]
}, (err, resp) => {
if (err)
console.error(err);
if (resp)
console.log(resp);
});
res.json('ok');
});
Run the application now and make the following cURL request after replacing the number.
$ curl "http://localhost:5000/call?to=<phone-number>&msg=You%20just%20got%20rickrolled\!"
"ok"
You should expect the messages and the call to go somewhat like the one given below.
Hooray! You can make a few calls now! In the next section, we go over how to handle an inbound call.
Receiving a Call
For handling an incoming call, the webhook endpoint for the Answer URL must respond with an NCCO as application/json
. We can access the calling number using req.body.from
. This might be a good point to refer back to the documentation for call control objects.
In the previous section, we assumed that we are going to use the /answer
route for handling inbound calls. We define a handler for that in this section. Here, I am responding to a POST
request with a NCCO object, as JSON, that will be responsible for greeting the caller and speaking out the phone number from which the call is being made from, and also play an audio stream into the call.
app.post('/answer', (req, res) => {
const number = req.body.from.split('').join(' ');
const ncco = [
{
action: 'talk',
text: 'Thank you for calling from ' + number,
language: 'en-IN',
style: '4'
},
{
action: 'stream',
streamUrl: ['https://www.albinoblacksheep.com/audio/mp3/RickRollMarioPaint.mp3']
}
];
res.json(ncco);
});
Make a call to your Vonage number and keep your fingers crossed! You might hear something like this:
You can also accept user input using DTMF Tones and Speech Recognition, which we can go over in some other tutorial. This shows that we can achieve a lot leveraging Vonage Voice APIs!
Taking a Step Back
Congratulations! We went through setting up the environment, creating a Node.js application, building a few endpoints, and the great part being able to make phone calls and answer them using code that you can reuse in a lot of use cases!
Learn more about the Vonage APIs on their Developer Education Portal.
Top comments (0)