Connect a Caller to Another Party
You're ready for this how-to guide if you've got the following:
A FreeClimb account
A registered application
A configured FreeClimb Number
Your tools and language installed
Trial accounts: A verified number
Node.js
In this tutorial we will demonstrate how to use a conference to connect an inbound call with an outbound call to another party, such as a call center agent. Once the initial caller hangs up, or if the other party does not answer the call, we will tell FreeClimb to terminate the conference.
When our application receives an inbound call, we first need to create a conference so that we can add participants when needed.
Create your package.json file and save in the root directory of your project:
{
"name": "node-another-party-how-to-guide",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"@freeclimb/sdk": "^3.8.0",
"body-parser": "^1.20.3",
"dotenv": "^16.4.5",
"express": "^4.21.1"
}
}
Install the package by running the following in the command line/terminal:
yarn install
Example code:
require('dotenv').config()
const express = require('express')
const bodyParser = require('body-parser')
const freeclimbSDK = require('@freeclimb/sdk')
const { AddToConference, CreateConference, PerclScript, Say, OutDial, IfMachine, CallStatus, ConferenceStatus, UpdateConferenceRequest } = freeclimbSDK
const app = express()
app.use(bodyParser.json())
// Where your app is hosted ex. www.myapp.com
const host = process.env.HOST
const port = process.env.PORT || 80
const hostUrl = `${host}:${port}`
// your freeclimb API key (available in the Dashboard) - be sure to set up environment variables to store these values
const accountId = process.env.ACCOUNT_ID
const apiKey = process.env.API_KEY
const configuration = freeclimbSDK.createConfiguration({ accountId, apiKey })
const freeclimb = new freeclimbSDK.DefaultApi(configuration)
app.post('/incomingCall', (req, res) => {
const conference = new CreateConference({
actionUrl: `${hostUrl}/conferenceCreated`
})
const percl = new PerclScript({ commands: [conference] }).build()
res.status(200).json(percl)
})
Once the conference is created, the callback specified by the conference’s actionUrl
is called. Inside this callback, we look-up the phone number of the agent we wish to connect the caller to (based on time of day, area code, etc) and return a PerCL script containing an OutDial
command. Further details regarding the OutDial
command can be found in the PerCL Overview on Outdial.
This example code uses a function
lookupAgentPhoneNumber
. To run this code, you'll need to implement the function to return a phone number.
app.post('/conferenceCreated', (req, res) => {
const createConferenceResponse = req.body
const conferenceId = createConferenceResponse.conferenceId
const say = new Say({ text: 'Please wait while we attempt to connect you to an agent.' })
// implementation of lookupAgentPhoneNumber() is left up to the developer
const agentPhoneNumber = lookupAgentPhoneNumber()
// Make OutDial request once conference has been created
const outDial = new OutDial({
destination: agentPhoneNumber,
callingNumber: createConferenceResponse.from,
actionUrl: `${hostUrl}/outboundCallMade/${conferenceId}`,
callConnectUrl: `${hostUrl}/callConnected/${conferenceId}`,
// Hangup if we get a voicemail machine
ifMachine: IfMachine.HANGUP
})
const percl = new PerclScript({ commands: [outDial] }).build()
res.status(200).json(percl)
})
When FreeClimb executes the OutDial
command, the actionUrl
callback associated with the command will be called. Inside this callback, we add the initial caller to the conference and set the leaveConferenceUrl
for this participant, allowing us to know when the caller has hung up.
app.post('/outboundCallMade/:conferenceId', (req, res) => {
const outboundCallResponse = req.body
const conferenceId = req.params.conferenceId
// Add initial caller to conference
const addToConference = new AddToConference({
conferenceId,
callId: outboundCallResponse.callId,
// set the leaveConferenceUrl for the inbound caller, so that we can terminate the conference when they hang up
leaveConferenceUrl: `${hostUrl}/leftConference`
})
const percl = new PerclScript({ commands: [addToConference] }).build()
res.status(200).json(percl)
})
When the OutDial
call is connected, the callConnectUrl
associated with the command will be called. Inside this callback, we check the DialCallStatus
to see if someone answered the call. If someone did answer the call, we add them to the conference, otherwise we terminate the conference using the FreeClimb API. Note that we don’t use the TerminateConference
PerCL command since all PerCL is ignored if the call is not answered.
app.post('/callConnected/:conferenceId', (req, res) => {
const callConnectedResponse = req.body
const conferenceId = req.params.conferenceId
if (callConnectedResponse.callStatus != CallStatus.IN_PROGRESS) {
// Terminate conference if agent does not answer the call. Can't use PerCL command since PerCL is ignored if the call was not answered.
terminateConference(conferenceId)
return res.status(200).json([])
}
const addToConference = new AddToConference({
conferenceId,
callId: callConnectedResponse.callId
})
const percl = new PerclScript({ commands: [addToConference] }).build()
res.status(200).json(percl)
})
When the initial caller hangs up, or otherwise leaves the conference, the leaveConferenceUrl
callback is called. Inside this callback, we terminate the conference using the FreeClimb API. Note that we don’t use the TerminateConference
PerCL command since all PerCL is ignored if this callback was triggered by the caller hanging up.
app.post('/leftConference', (req, res) => {
// Call terminateConference when the initial caller hangsups
const leftConferenceResponse = req.body
const conferenceId = leftConferenceResponse.conferenceId
terminateConference(conferenceId)
res.status(200).json([])
})
To terminate the conference, we use the FreeClimb API to make a POST request to the specified conference and update its status to Terminated
.
function terminateConference(conferenceId) {
// Create the ConferenceUpdateOptions and set the status to terminated
const options = new UpdateConferenceRequest({
status: ConferenceStatus.TERMINATED
})
freeclimb.updateAConference(conferenceId, options).catch(err => {/* Handle Errors */ })
}
Handle status updates:
// Specify this route with 'Status Callback URL' in App Config
app.post('/status', (req, res) => {
// handle status changes
res.status(200)
})
Start the server:
app.listen(port, () => {
console.log(`Starting server on port ${port}`)
})
Java
In this tutorial we will demonstrate how to use a conference to connect an inbound call with an outbound call to another party, such as a call center agent. Once the initial caller hangs up, or if the other party does not answer the call, we will tell FreeClimb to terminate the conference.
When our application receives an inbound call, we first need to create a conference so that we can add participants when needed.
Create your build.gradle file and save it to the root directory in your project:
/*
* This file was generated by the Gradle 'init' task.
*
* This is a general purpose Gradle build.
* Learn how to create Gradle builds at https://guides.gradle.org/creating-new-gradle-builds
*/
script {
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
//Add the dependency
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:2.1.6.RELEASE"
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
bootJar {
baseName = 'gs-spring-boot'
version = '0.1.0'
}
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies {
compile "org.springframework.boot:spring-boot-starter-web"
testCompile "junit:junit"
compile 'com.github.FreeClimbAPI:FreeClimb-Java-SDK:3.0.0'
}
sourceSets {
main {
java {
srcDirs = ['src'] // changed line
}
}
}
Build the file by running the following in your terminal/command line:
gradle build
Example code:
// Imports used
package main.java.connect_caller_to_party;
import org.springframework.web.bind.annotation.RestController;
import com.vailsys.freeclimb.percl.*;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import com.vailsys.freeclimb.api.FreeClimbException;
import com.vailsys.freeclimb.webhooks.conference.ConferenceCreateActionCallback;
import com.vailsys.freeclimb.webhooks.percl.OutDialActionCallback;
import com.vailsys.freeclimb.webhooks.StatusCallback;
import com.vailsys.freeclimb.api.call.CallStatus;
import com.vailsys.freeclimb.webhooks.conference.LeaveConferenceUrlCallback;
import com.vailsys.freeclimb.api.FreeClimbClient;
import com.vailsys.freeclimb.api.conference.ConferenceUpdateOptions;
import com.vailsys.freeclimb.api.conference.ConferenceStatus;
@RestController
public class ConnectCallerToPartyController {
private String baseUrl = System.getenv("HOST");
// To properly communicate with FreeClimb's API, set your FreeClimb app's
// VoiceURL endpoint to '{yourApplicationURL}/InboundCall' for this example
// Your FreeClimb app can be configured in the FreeClimb Dashboard
@RequestMapping(value = {
"/InboundCall" }, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<?> inboundCall() {
// Create an empty PerCL script container
PerCLScript script = new PerCLScript();
// Create a conference once an inbound call has been received
script.add(new CreateConference(baseUrl + "/ConferenceCreated"));
// Convert PerCL container to JSON and append to response
return new ResponseEntity<>(script.toJson(), HttpStatus.OK);
}
Once the conference is created, the callback specified by the conference’s actionUrl
is called. Inside this callback, we lookup the phone number of the agent we wish to connect the caller to (based on time of day, area code, etc) and return a PerCL script containing an OutDial
command. Further details regarding the OutDial
command can be found in the PerCL Overview on Outdial.
This example code uses a function
lookupAgentPhoneNumber
. To run this code, you'll need to implement the function to return a phone number.
/*
* NOTE: Testing this tutorial requires 2 phone numbers, referenced as agentPhoneNumber and CALLER_PHONE
*
* 1. SET agentPhoneNumber WITH A PHONE NUMBER (IN E.164 FORMAT) CAPABLE OF ACCEPTING PHONE CALLS OR CREATE lookupAgentPhoneNumber()
* FUNCTION agentPhoneNumber
* 2. RUN PROJECT WITH COMMAND:
* `gradle build && java -Dserver.port=0080 -jar build/libs/gs-spring-boot-0.1.0.jar`
* 3. USING CALLER_PHONE, CALL FreeClimb NUMBER ASSOCIATED WITH THE ACCOUNT (CONFIGURED IN FreeClimb DASHBOARD)
* 4. EXPECT agentPhoneNumber TO RECEIVE CALL FROM FreeClimb NUMBER ASSOCIATED WITH THE ACCOUNT:
* 5. EXPECT agentPhoneNumber and CALLER_PHONE SHOULD BE IN A CONFERENCE CALL
*/
@RequestMapping(value = {
"/ConferenceCreated" }, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<?> conferenceCreated(@RequestBody String str) {
PerCLScript script = new PerCLScript();
ConferenceCreateActionCallback conferenceCreateActionCallback;
try {
conferenceCreateActionCallback = ConferenceCreateActionCallback.createFromJson(str);
String conferenceId = conferenceCreateActionCallback.getConferenceId();
script.add(new Say("Please wait while we attempt to connect you to an agent."));
// Make OutDial request once conference has been created
String agentPhoneNumber = ""; // lookupAgentPhoneNumber(); // implementation of
// lookupAgentPhoneNumber() is left up to the developer
OutDial outDial = new OutDial(agentPhoneNumber, conferenceCreateActionCallback.getFrom(),
baseUrl + "/OutboundCallMade" + "/" + conferenceId, baseUrl + "/OutboundCallConnected" + "/" + conferenceId);
outDial.setIfMachine(OutDialIfMachine.HANGUP);
script.add(outDial);
} catch (FreeClimbException pe) {
System.out.print(pe);
}
return new ResponseEntity<>(script.toJson(), HttpStatus.OK);
}
When FreeClimb executes the OutDial
command, the actionUrl
callback associated with the command will be called. Inside this callback, we add the initial caller to the conference and set the leaveConferenceUrl
for this participant, allowing us to know when the caller has hung up.
@RequestMapping(value = {
"/OutboundCallMade/{conferenceId}" }, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<?> outboundCallMade(@PathVariable String conferenceId, @RequestBody String str) {
PerCLScript script = new PerCLScript();
OutDialActionCallback outDialActionCallback;
try {
// Convert JSON into a call status callback object
outDialActionCallback = OutDialActionCallback.createFromJson(str);
// Add initial caller to conference
AddToConference addToConference = new AddToConference(conferenceId, outDialActionCallback.getCallId());
// set the leaveConferenceUrl for the inbound caller, so that we can terminate
// the conference when they hang up
addToConference.setLeaveConferenceUrl(baseUrl + "/LeftConference");
script.add(addToConference);
} catch (FreeClimbException pe) {
System.out.print(pe);
}
return new ResponseEntity<>(script.toJson(), HttpStatus.OK);
}
When the OutDial
call is connected, the callConnectUrl
associated with the command will be called. Inside this callback, we check the DialCallStatus
to see if someone answered the call. If someone did answer the call, we add them to the conference, otherwise we terminate the conference using a FreeClimbClient
. Note that we don’t use the TerminateConference
PerCL command since all PerCL is ignored if the call is not answered.
@RequestMapping(value = {
"/OutboundCallConnected/{conferenceId}" }, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<?> outboundCallConnected(@PathVariable String conferenceId, @RequestBody String str) {
PerCLScript script = new PerCLScript();
StatusCallback statusCallback;
try {
// Convert JSON into a call status callback object
statusCallback = StatusCallback.fromJson(str);
// Terminate conference if agent does not answer the call. Can't use PerCL
// command since PerCL is ignored if the call was not answered.
if (statusCallback.getCallStatus() != CallStatus.IN_PROGRESS) {
terminateConference(conferenceId);
return new ResponseEntity<>(script.toJson(), HttpStatus.OK);
}
script.add(new AddToConference(conferenceId, statusCallback.getCallId()));
} catch (FreeClimbException pe) {
System.out.print(pe);
}
return new ResponseEntity<>(script.toJson(), HttpStatus.OK);
}
When the initial caller hangs up, or otherwise leaves the conference, the leaveConferenceUrl callback is called. Inside this callback, we terminate the conference using a FreeClimbClient
. Note that we don’t use the TerminateConference PerCL command since all PerCL is ignored if this callback was triggered by the caller hanging up.
@RequestMapping(value = {
"/LeftConference" }, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<?> leftConference(@RequestBody String str) {
LeaveConferenceUrlCallback leaveConferenceUrlCallback;
try {
// Convert JSON into a leave conference callback object
leaveConferenceUrlCallback = LeaveConferenceUrlCallback.createFromJson(str);
// Terminate the conference when the initial caller hangs up. Can't use PerCL
// command since PerCL is ignored if the caller hangs up.
terminateConference(leaveConferenceUrlCallback.getConferenceId());
} catch (FreeClimbException pe) {
System.out.print(pe);
}
return new ResponseEntity<>("", HttpStatus.OK);
}
To terminate the conference, we use a FreeClimbClient
to make a POST request to the specified conference and update its status to TERMINATED
.
private static void terminateConference(String conferenceId) throws FreeClimbException {
String accountId = System.getenv("ACCOUNT_ID");
String apiKey = System.getenv("API_KEY");
FreeClimbClient client = new FreeClimbClient(accountId, apiKey);
// Create the ConferenceUpdateOptions and set the status to terminated
ConferenceUpdateOptions conferenceUpdateOptions = new ConferenceUpdateOptions();
conferenceUpdateOptions.setStatus(ConferenceStatus.TERMINATED);
client.conferences.update(conferenceId, conferenceUpdateOptions);
}
}
C#
In this tutorial we will demonstrate how to use a conference to connect an inbound call with an outbound call to another party, such as a call center agent. Once the initial caller hangs up, or if the other party does not answer the call, we will tell FreeClimb to terminate the conference.
All the methods in this class were created in a ASP.NET Core MVC application in one controller, FreeClimbController
.
When our application receives an inbound call, we first need to create a conference so that we can add participants when needed.
Imported used (specified in the controller class):
using com.freeclimb.api;
using com.freeclimb.api.conference;
using com.freeclimb.percl;
using com.freeclimb.webhooks.call;
using com.freeclimb.webhooks.conference;
using Microsoft.AspNetCore.Mvc;
Example code:
[HttpPost ("InboundCall")]
public ActionResult InboundCall (CallStatusCallback callStatus) {
PerCLScript script = new PerCLScript ();
var conferenceActionUrl = AppUrl + "/voice/ConferenceCreated";
CreateConference cc = new CreateConference (conferenceActionUrl);
script.Add (cc);
return Content (script.toJson (), "application/json");
}
Once the conference is created, the callback specified by the conference’s actionUrl
is called. Inside this callback, we lookup the phone number of the agent we wish to connect the caller to (based on time of day, area code, etc) and return a PerCL script containing an OutDial
command. Further details regarding the OutDial
command can be found in the PerCL Overview on Outdial.
This example code uses a function
lookupAgentPhoneNumber
. To run this code, you'll need to implement the function to return a phone number.
[HttpPost ("ConferenceCreated")]
public ActionResult ConferenceCreated (ConferenceStatusCallback request) {
// Make OutDial request once conference has been created
PerCLScript script = new PerCLScript ();
Say say = new Say ();
say.setText ("Please wait while we attempt to connect you to an agent.");
script.Add (say);
string confId = request.getConferenceId;
// implementation of lookupAgentPhoneNumber() is left up to the developer
string agentPhoneNumber = lookupAgentPhoneNumber ();
// Make OutDial request once conference has been created
var outdialActionUrl = AppUrl + $"/voice/OutboundCallMade?conferenceId={confId}";
var outdialConnectedUrl = AppUrl + $"/voice/OutboundCallConnected?conferenceId={confId}";
OutDial outDial = new OutDial (agentPhoneNumber, outdialConnectedUrl);
outDial.setCallingNumber (request.getFrom);
outDial.setActionUrl (outdialActionUrl);
outDial.setIfMachine (com.freeclimb.EIfMachine.Hangup);
script.Add (outDial);
return Content (script.toJson (), "application/json");
}
When FreeClimb executes the OutDial
command, the actionUrl
callback associated with the command will be called. Inside this callback, we add the initial caller to the conference and set the leaveConferenceUrl
for this participant, allowing us to know when the caller has hung up.
Note that the ‘context’ of this callback is the original call, and not the new call we created with the OutDial
. So the getCallId
property of the request message sent in the callback is the call ID of the original call.
Example code:
[HttpPost ("OutboundCallMade")]
public ActionResult OutboundCallMade ([FromQuery (Name = "conferenceId")] string conferenceId, OutDialActionCallback request) {
PerCLScript script = new PerCLScript ();
// note the context of the request is the original call, not the newly created via OutDial
AddToConference addToConference = new AddToConference (conferenceId, request.getCallId);
var leaveConferenceUrl = AppUrl + "/voice/LeftConference";
// set the leaveConferenceUrl for the inbound caller, so that we can terminate the conference when they hang up
addToConference.setLeaveConferenceUrl (leaveConferenceUrl);
script.Add (addToConference);
return Content (script.toJson (), "application/json");
}
When the OutDial
call is connected, the callConnectUrl
associated with the command will be called. Inside this callback, we check the DialCallStatus
to see if someone answered the call. If someone did answer the call, we add them to the conference, otherwise we terminate the conference using a FreeClimbClient. Note that we don’t use the TerminateConference
PerCL command since all PerCL is ignored if the call is not answered.
Note that the ‘context’ of this callback is the new call we created with the OutDial
. So the getCallId
property of the request message sent in the callback is the call ID of the agent.
Example code:
[HttpPost ("OutboundCallConnected")]
public ActionResult OutboundCallConnected ([FromQuery (Name = "conferenceId")] string conferenceId, CallStatusCallback request) {
PerCLScript script = new PerCLScript ();
if (request.getDialCallStatus != com.freeclimb.ECallStatus.InProgress) {
terminateConference (conferenceId);
return Content (script.toJson (), "application/json");
}
// note context of this callback is the new call (agent). Add them to conference
script.Add (new AddToConference (conferenceId, request.getCallId));
return Content (script.toJson (), "application/json");
}
When the initial caller hangs up, or otherwise leaves the conference, the leaveConferenceUrl
callback is called. Inside this callback, we terminate the conference using a FreeClimbClient
. Note that we don’t use the TerminateConference
PerCL command since all PerCL is ignored if this callback was triggered by the caller hanging up.
Example code:
[HttpPost ("LeftConference")]
public ActionResult LeftConference (LeaveConferenceUrlCallback request) {
PerCLScript script = new PerCLScript ();
// just terminate conference sonce one party left
terminateConference (request.getConferenceId);
return Content (script.toJson (), "application/json");
}
To terminate the conference, we use a FreeClimbClient to make a POST request to the specified conference and update its status to TERMINATED
.
Example code:
private void terminateConference (string conferenceId) {
// your credentials information filled in here
string acctId = getAcctId ();
string apiKey = getApiKey ();
FreeClimbClient client = new FreeClimbClient (acctId, apiKey);
// terminating a conference is done by changing the status to Terminated
ConferenceOptions options = new ConferenceOptions ();
options.setStatus (com.freeclimb.EConferenceStatus.Terminated);
client.getConferencesRequester.update (conferenceId, options);
}
Updated about 1 month ago