Click to Call via Web Browser
Help your customers and call centers quickly reach out via web browser or app
In this how-to guide, we will guide you through implementing a click to call application using FreeClimb. Click to call applications are great for customers who want to quickly chat with a support team and, conversely, for businesses and call centers that want to reach out to their customers in a more personal and efficient manner. This system will perform the following actions:
- Get phone numbers from the user using a webpage
- Create an outgoing call using the FreeClimb API
- Create a conference using PerCL
- Connect an agenct and caller in a conference using PerCL
You're ready for this how-to guide if you have the following:
A FreeClimb account
A registered application with a named alias
A configured FreeClimb number
Trial accounts: A verified number
Your language and tools:
Step 1: App set-up
Make your server locally accessible
The fastest way to start testing your FreeClimb application is to temporarily make your local server publicly accessible through a tunneling service. We'll use ngrok to do this.
Once you have downloaded ngrok and unzipped the file to install it, open your terminal and navigate to the directory where you've unzipped ngrok. Use the following command to start a HTTP tunnel on port 3000:
ngrok http 3000
Once you run ngrok using the above command, you should receive a response with a public URL that looks something like this:
ngrok by @inconshreveable
Tunnel Status online
Version 2.0/2.0
Web Interface http://127.0.0.1:4040
Forwarding http://92832de0.ngrok.io -> localhost:3000
Forwarding https://92832de0.ngrok.io -> localhost:3000
Connnections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
The forwarding URLs provided point to your local server. Save the URLs and move on to the next step. Make sure to keep ngrok open as your continue to build and run your app.
Configure your application's endpoints
Now that you've got a public URL, you're ready to configure your application's endpoints. We'll be configuring the voiceUrl using your ngrok URL and the route reference /incomingCall
.
Go to the Apps page in your dashboard. You should see your registered FreeClimb app.
Click Edit Config to enter the ngrok forwarding URL into your registered app's voiceUrl field and add the route /incomingCall
to the end of it. When you're done, the App Config should look something like this:
Save your updated App Config and proceed with the next steps.
Get dependencies
To start, create a new directory for the project. Within this directory create a file named package.json
and add the following content:
{
"name": "Node_Click_To_Call_How_To_Guide",
"version": "1.0.0",
"description": "Basic click to call how-to guide",
"main": "index.js",
"license": "MIT",
"dependencies": {
"@freeclimb/sdk": "^3.8.0",
"body-parser": "^1.19.0",
"dotenv": "^8.2.0",
"dotenv-safe": "^8.2.0",
"express": "^4.17.1",
"express-handlebars": "^5.2.0"
},
"devDependencies": {
"husky": "^4.3.8",
"jest": "^29.7.0",
"prettier": "^2.2.1",
"prettier-standard": "^16.4.1",
"supertest": "^6.1.3"
},
"scripts": {
"test": "jest",
"test:cov": "jest --coverage",
"start": "DEBUG=express:* node .",
"format": "prettier-standard --format",
"lint": "prettier-standard --format --check"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,jsx,ts,tsx,json,css,scss,md}": [
"prettier-standard --staged",
"git add"
]
},
"packageManager": "[email protected]+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447"
}
These dependencies can be installed from the command line by using the following command:
yarn start
Set environment variables
In order to authenticate your requests with the FreeClimb platform, we need to include your API credentials (account ID and API key) in every request. Your account ID and API key can be found in the FreeClimb dashboard.
Why put API credentials in environment variables?
If you were to push your code to a public repository, those credentials would be public and an attacker could steal your account. To prevent this, we create environment variables with your API credentials and import them into your code using dotenv-safe.
This sample app uses dotenv-safe to read in your credentials, and anything else you save, as environment variables. As required by dotenv-safe, before creating our .env
file we must create a .env.example
file that outlines the environment variables that will need to be present in the actual .env
file.
You must include both a
.env.example
file and a.env
file in order for dotenv-safe to work properly.
To do so, create a file in your project's directory named .env.example
and add the following content:
ACCOUNT_ID=
API_KEY=
APP_ID=
FC_NUMBER=
PORT=
HOST=
Once you've created your .env.example
file, create a .env
file in the root directory of your repo. Make sure to add the file to your .gitignore
file. Add your account ID and API key to your .env
file.
We'll also include your configured app ID, FreeClimb number, port, and host.
If you haven't already, make sure to assign a registered application you configured with your ngrok URL to the configured FreeClimb number you will be using for this sample application. Use this app ID and FreeClimb number as your APP_ID
variable and FC_NUMBER
variable respectively.
The port number will allow you to specify the port on which the app will run. For example, if you were to enter PORT=3000
to view the app, you would direct your browser to http://localhost:3000
.
If you've set up ngrok to expose a specific port from your local machine, use the public facing link provided by ngrok as your HOST
variable.
Your .env
file should look as follows:
ACCOUNT_ID="YOUR-ACCOUNT-ID"
API_KEY="YOUR-API-KEY"
APP_ID="YOUR-APP-ID"
FC_NUMBER="+15555550010" // make sure your FreeClimb number is in E.164 format
PORT="YOUR-PORT-NUMBER"
HOST="YOUR-HOST"
Save your files and continue to the next step.
Step 2: Create and share a FreeClimb SDK instance
Throughout this application, we will be using a FreeClimb SDK object to generate our PerCL responses. Rather than re-create this object in every file that needs it, we will instread create one instance and pass it using require
where needed.
To do this, create a new file in your project directory called freeclimb.js
and add the following:
const freeclimbSDK = require('@freeclimb/sdk')
const accountId = process.env.ACCOUNT_ID
const apiKey = process.env.API_KEY
const configuration = freeclimbSDK.createConfiguration({ accountId, apiKey })
const freeclimb = new freeclimbSDK.DefaultApi(configuration)
module.exports = freeclimb
Step 3: Create the express server
For this application, we'll create a server to contain our application logic and render our web forms. As we move on, we'll add more functionality to this file.
To start, create a new file in your project directory called index.js
and add the following:
require('dotenv-safe').config()
const express = require('express')
const bodyParser = require('body-parser')
const handlebars = require('express-handlebars')
// uncomment the lines below when you are ready to add in FreeClimb API functions in Step 7
// const freeclimb = require('./freeclimb')
// const calls = require('./calls')
// const conferences = require('./conferences')
const app = express()
app.engine('handlebars', handlebars({ defaultLayout: 'main' }))
app.set('view engine', 'handlebars')
app.use(bodyParser.urlencoded({ extended: true }))
app.use(bodyParser.json())
const port = process.env.PORT || 3000
const host = process.env.HOST
const fcNumber = process.env.FC_NUMBER
app.get('/', (req, res) => {
res.status(200).send('hello')
})
if (process.env.NODE_ENV !== 'test') {
app.listen(port, () => {
console.log(`Starting server on port ${port}`)
})
}
module.exports = { app }
You can confirm your Express server works properly by running the following console command to start your application:
yarn start
Now open a browser and head to localhost://{your-port-from-.env}
. You should see the text "hello world."
Congrats you've set up an Express server with Node!
Step 4: Set up your webpages & templates
To separate the application logic in the code from our webpages we use express-handelbars. This allows us to remove any complicated Javascript from our HTML forms. To setup the templates we'll be using in this app, create a directory named "views" within your project. Then, inside the "views" directory create another directory named "layouts."
Create a file called main.handlebars
inside the "layouts" directory with the following content. This is a basic layout that will contain all the pages for this application:
<!doctype html>
<html>
<head>
<title>Freeclimb Click To Call Sample</title>
</head>
<body>
<h1>FreeClimb Click To Call Sample</h1>
{{{body}}}
</body>
</html>
Get a user's phone number
We will be using a 2FA process to obtain the user's phone number. To do this create an HTML template with the following content in your "views" directory named inputPhone.handlebars
. This template contains a form with two inputs and one button for form submission as well as a block that can be used to conditionally render error messages (denoted by the {{#if error }}
). We'll use this if we need to re-render the form in the event the user enters an invalid phone number.
On submission of this form, a POST
request will be issued to the /send-verification
endpoint containing the user-entered phone number (shown in the form tag as method="post" action="/send-verification"
):
<p>Please enter your phone number and the phone number of the desired agent in E.164 format (e.g. +1234567891)</p>
<form method="post" action="/startCall">
<input type="tel" name="caller" placeholder="Your number" />
<br />
<input type="tel" name="agent" placeholder="Agent Number" />
<br />
<input type="submit" value="Submit" />
</form>
{{#if error}}
<p>{{error}}</p>
{{/if}}
Success message
We'll also create a template to render a success message to the user in the event the outbound call successfully connects. This form will also contain a button to bring the user back to the starting page if they wish to make another call. To do this, create another HTML template in your views
directory named callMade.handlebars
and add the following:
<p>call made successfully</p>
<form method="post" action="/reset">
<input type="submit" value="Make Another Call" />
</form>
Step 5: Initiate a call
For our click to call application, we will be using the FreeClimb API's Make a Call functionality to initiate the calling process. To do this, create a new file in your project directory called calls.js
and add the following:
const freeclimb = require('./freeclimb')
const { IfMachine } = require('@freeclimb/sdk')
exports.createCall = async (to, from, callback) => {
const applicationId = process.env.APP_ID
await freeclimb.makeACall({
to,
from,
applicationId,
callConnectUrl: callback,
ifMachine: IfMachine.HANGUP
})
}
Step 6: Handle hang up
Another important function of our click to call application is updating the status of our call in the event that either caller hangs up. We will do this by updating the Conference that both calls are part of. To do this, create a new file in your project directory called conferences.js
and add the following:
const freeclimb = require('./freeclimb')
const { ConferenceStatus } = require('@freeclimb/sdk')
exports.terminate = async conferenceId => {
const conference = await freeclimb.getAConference(conferenceId)
const status = conference.status
if (status !== 'terminated') {
await freeclimb.updateAConference(conferenceId, { status: ConferenceStatus.TERMINATED })
}
}
Step 7: Render webpage templates
In order to make our original index.js
file fully functional, we will add various routes to render webpages as well as handle the call and its users.
Import files
Before defining any endpoints we must import the files we use to interact with the FreeClimb API and call. Do this by uncommenting the following files in the header of your index.js
files:
// uncomment the lines below when you are ready to add in FreeClimb API functions
const freeclimb = require('./freeclimb')
const calls = require('./calls')
const conferences = require('./conferences')
Render webpages
The /
endpoint will serve as our entryway into the application and will render our first template to allow the user to enter phone numbers (inputPhone.handlebars
). First update the endpoint we defined for GET
requests for the /
route in Step 3. Instead of res.status(200).send('hello')
, it should now be res.status(200).render('inputPhone')
. See below:
app.get('/', (req, res) => {
res.status(200).render('inputPhone') //no need to include .handlebars
})
Next we'll create the endpoint hit by the 'Make Another Call' button in callMade.handlebars
. This route will simply re-render the inputPhone.handlebars
form to its initial state. To do this, add the following to your index.js
file:
app.post('/reset', (req, res) => {
res.status(200).render('inputPhone') //no need to include .handlebars
})
Step 8: Start the call and create conference
FreeClimb connects calls by initiating an outbound call, creating a conference, and adding each callee (in this case, the agent and the user) to that conference.
To begin, we need to handle incoming requests to /startCall
from the inputPhone.handlebars
form submission. /startCall
does this by using the FreeClimb API to make an outbound call to the agent phone number entered by the user.
Add the following code to index.js
:
app.post('/startCall', async (req, res) => {
const caller = req.body.caller
const agent = req.body.agent
try {
await calls.createCall(agent, fcNumber, `${host}/agentPickup/${caller}`)
res.status(200).render('callMade')
} catch (err) {
console.error(err)
res.status(500).render('inputPhone', {
error:
'Your call could not be made. Please ensure you have correctly entered both phone numbers.'
})
}
})
Once the phone call has been initiated by the user via the inputPhone
form and the agent outbound call has been initiated via /startCall
, we'll need to create a conference in order to connect the agent and user.
How does FreeClimb connect two calls?
FreeClimb uses the
OutDial
andCreateConference
PerCL commands to connect two or more callers.Check out these resources to learn more about how FreeClimb connect calls:
- How to Connect a Caller to Another Party
OutDial
PerCL commandCreateConference
PerCL command
First, we'll add an endpoint that returns the PerCL commands for creating a new conference. To do this, add the following to index.js
:
app.post('/agentPickup/:caller', async (req, res) => {
const caller = req.params.caller
res.status(200).json(
new PerclScript({
commands: [new CreateConference({ actionUrl: `${host}/conferenceCreated/${caller}` })]
}).build()
)
})
Next we'll create the endpoint hit by the actionUrl
of the createConference
command used in /agentPickup/:caller
. This will initiate an outbound call to the user.
To do this, add the following code to index.js
:
app.post('/conferenceCreated/:caller', async (req, res) => {
res.status(200).json(
new PerclScript({
commands: [
new OutDial({
callingNumber: process.env.FC_NUMBER,
destination: req.params.caller,
actionUrl: `${host}/userCalled/${req.body.conferenceId}`,
callConnectUrl: `${host}/userConnected/${req.body.conferenceId}`,
ifMachine: IfMachine.HANGUP
})
]
}).build()
)
})
Step 9: Connect agent and user calls
Now that we've created a conference and made outbound calls to both the agent and user, we need to add each into the conference we created in order to connect the two call legs.
To do this, we'll first create the endpoint hit by the actionUrl
of the OutDial
PerCL command from/conferenceCreated/:caller
. This will:
- add the agent into the conference we've already created
- set the endpoint for when the agent and caller disconnect from the call
Add the following to index.js
:
app.post('/userCalled/:conferenceId', (req, res) => {
res.status(200).json(
new PerclScript({
commands: [
new Say({ text: 'please wait while we attempt to add your client to the call' }),
new AddToConference({
conferenceId: req.params.conferenceId,
leaveConferenceUrl: `${host}/leftConference`
})
]
}).build()
)
})
Next, we'll create the endpoint hit by the callConnectedUrl
of the OutDial
PerCL command we used to call the user in /conferenceCreated/:caller
. This will:
- add the user into the conference
- set the endpoint for when the agent and caller disconnect from the call
Add the following to index.js
:
app.post('/userConnected/:conferenceId', async (req, res) => {
const conferenceId = req.params.conferenceId
if (req.body.dialCallStatus != CallStatus.IN_PROGRESS) {
await conferences.terminate(conferenceId)
res.status(500).json([])
} else {
res.status(200).json(
new PerclScript({
commands: [
new AddToConference({
conferenceId,
leaveConferenceUrl: `${host}/leftConference`
})
]
}).build()
)
}
})
Lastly, we'll create the endpoint hit by the leftConferenceUrl
we set for both our user and agent when we placed them into the conference. This will invoke the FreeClimb API to terminate the conference. To do this, add the following to index.js
:
app.post('/leftConference', async (req, res) => {
const conferenceId = req.body.conferenceId
await conferences.terminate(conferenceId)
res.status(200).json([])
})
Step 10: Run your app
To see your new click to call app in action, run the following command at the command line:
yarn start
Once you do this, direct your browser to localhost://{your-port-from-.env}
. From there you should be able to enter phone numbers and make a call.
If you are trying to run this sample app while using a trial account, make sure that the number you use to test click to call is a verified number.
Congrats! You can now build your own custom click to call application. 🥳🥳
Updated about 1 month ago