The essentials of building Voice Applications with Project Fonos

The purpose of this tutorial is to show the basics of Project Fonos. Here you will find how to create a Voice Application, add a Number, and then use that Number to originate a call. Please follow the guide in sequence, as each step builds on the last one.

GitHub logo fonoster / fonoster

🚀 An open-source alternative to Twilio.

Requirements

Before you start this guide, you will need the following:

  • A set of credentials from here 👈
  • An account for access to a SIP Service Provider (For US and Canada, we recommend voip.ms)
  • NodeJS 14+ (Use nvm if possible)
  • Fonos command-line tool (install with npm install -g @fonos/ctl)
  • Ngrok (install with npm install -g ngrok)

You can login to the server with:

fonos auth:login

And your output will be similar to:

Access your Fonos infrastructure
Press ^C at any time to quit.
? api endpoint api.fonoster.io
? access key id psanders
? access key token *************************...
? ready? Yes
Accessing endpoint api.fonoster.io... Done

Creating a basic Voice Application

A Voice Application is a server that takes control of the flow in a call. A Voice Application can use any combination of the following verbs:

  • Play - It takes an URL or file and streams the sound back to the calling party
  • Say - It takes a text, synthesizes the text into audio, and streams the result
  • Gather - It waits for DTMF events and returns back the result
  • Record - It records the voice of the calling party and saves the audio on the Storage sub-system
  • Mute - It tells the channel to stop sending media, thus effectively muting the channel
  • Unmute - It tells the channel to allow media flow

Perform the following steps to create a Voice Application.

First, create an empty NodeJS project with:

mkdir voiceapp
cd voiceapp
npm init # and follow the wizard

For me it looks like this:

...
package name: (voiceapp) 
version: (1.0.0) 
description: My voice app
entry point: (index.js) 
test command: 
git repository: 
keywords: 
author: Pedro Sanders
license: MIT 
About to write to /Users/yourusername/Projects/voiceapp/package.json:

{
  "name": "voiceapp",
  "version": "1.0.0",
  "description": "My voice app",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Pedro Sanders",
  "license": "MIT"
}


Is this OK? (yes) yes

Then, with your favorite IDE open and edit the file index.js with the following content:

const { VoiceServer } = require("@fonos/voice");
const voiceServer = new VoiceServer();

voiceServer.listen((req, res) => {
  console.log(req);
  res.play("sound:hello-world");
});

Next, install the Voice module with:

npm i --save @fonos/voice

Finally, launch the Voice Application with:

node index.js

Your output will look like this:

info: initializing voice server
info: starting voice server on @ 0.0.0.0, port=3000

Your app will live at http://127.0.0.1:3000. ⚠️ Be sure to leave the server up!

Using Ngrok to publish your Voice Application

Now that we have our Voice Application up and running, we need to make it available on the Internet—the fastest way to enable public access by using Ngrok. For example, with ngrok, you can publish a web server with a single command.

On a new console, run Ngrok with the following command:

ngrok http 3000

The output will look like this:

Leave this service running, and save the Forwarding URL for use in the next step.

Building a SIP Network

A SIP Network has all the building blocks needed to establish communication between two SIP endpoints(i.e., softphone, webphone, cellphone, the PSTN, etc.) We want to configure a Number and route the calls to our Voice Application for this guide.

Let's start by creating a SIP Service Provider.

Adding a SIP Service Provider

A SIP Service Provider is an organization that will terminate your calls to the phone network (or PSTN). You will need the username, password, and host you obtained from your SIP Service Provider for this section.

Create a new provider with:

fonos providers:create

The output will look similar to this:

This utility will help you create a new Provider
Press ^C at any time to quit.
? friendly name VOIPMS
? username 215706
? secret [hidden]
? host newyork1.voip.ms
? transport tcp
? expire 300
? ready? Yes
Creating provider YourServiceProvider... Done

Adding a SIP Number

A Number, often referred to as DID/DOD, refers to a number managed by your SIP Service provider.

If your Provider doesn't accept E164, you can append the --ignore-e164-validation

fonos numbers:create --ignore-e164-validation

Here is an example of the output:

This utility will help you create a new Number
Press ^C at any time to quit.
? number in E.164 format (e.g. +16471234567) 9842753574    
? service provider VOIPMS
? aor link (leave empty)
? webhook https://5a2d2ea5d84d.ngrok.io # Replace with the value you obtained from Ngrok
? ready? Yes
Creating number +17853178071... KyjgGEkasj

⚠️ Be sure to replace the information with what was given to you by your Provider.

Creating a SIP Domain

A SIP Domain is a space within the SIP Network where SIP entities live (usually SIP Agents). To create a SIP Domain, you can use the command-line tool or the SDK.

In this step, you need to select the Number you just created as your Egreess Number. Also, make sure to use an "unclaimed" uri or you will receive this error: "› Error: This Domain already exists."

Create a new Domain with:

fonos domains:create

Your output will look similar to this:

This utility will help you create a new Domain
Press ^C at any time to quit.
? friendly name Acme Corp
? domain uri (e.g acme.com) sip.acme.com
? egress number none
? egress rule .*
? ready? Yes
Creating domain Acme Corp... Jny9B_qaIh

⚠️ In the demo server, you don't need to own the Domain. Any URI is fair game!

Using the API to make a call

To make a call, you are going to need to install the SDK.

Install the SDK, from within the voiceapp, with:

npm i --save @fonos/sdk

Next, create the script call.js with the following code:

// This will load the SDK and reuse your Fonos credentials
const Fonos = require("@fonos/sdk");
const callManager = new Fonos.CallManager();

// Few notes:
//  1. Update the from to look exactly as the Number you added 
//  2. Use an active phone or mobile
//  3. Replace the webhook with the one from your Ngrok
callManager.call({
 from: "9842753574",
 to: "17853178070",
 webhook: "https://5a2d2ea5d84d.ngrok.io",
 ignoreE164Validation: true
})
.then(console.log)
.catch(console.error);

Finally, run your script with: node call.js

If everything goes well, you will start seeing the output in the console you are running your Voice Application. You will also receive a call that will stream a "Hello World," which further confirms that everything is behaving as it should.

Troubleshooting

1. Are you not receiving the call at all?

The first thing to check is that your SIP Service Provider configuration is correct. Next, double-check the username, password, and host. If your Provider has an Admin console, check if you can see the registration from Fonos.

Next, make sure the from matches the Number given to you by your Provider. Also, double-check the to has the correct prefix (for example, +1, etc.).

2. You receive the call but immediately hang up (did not hear a sound)

First, verify that Ngrok is still running. Next, compare Ngrok's URL with the webhook on your Number. They both need to match!

Then observe the console's output where your Voice Application is running, and see if there are any errors.

Giving feedback to Team PF

We want to provide you with the best possible experience. To do that, we need your valuable feedback. Because we know you are busy, we provide two ways to get quick feedback from you. From the command line, use the fonos bug command to start a GitHub issue. Or, you can use the fonos feedback command to complete a short survey (which takes less than 30 seconds).

GitHub logo fonoster / fonoster

🚀 An open-source alternative to Twilio.

20