Connect a Caller to Another Party

👍

You're ready for this tutorial 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-tutorial",
  "version": "1.0.0",
  "dependencies": {
    "@freeclimb/sdk": "^1.0.0",
    "body-parser": "^1.19.0",
    "dotenv": "^8.1.0",
    "express": "^4.17.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 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 || 3000
// your FreeClimb API credentials (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 freeclimb = freeclimbSDK(accountId, apiKey)
app.post('/incomingCall', (req, res) => {
  const conference = freeclimb.percl.createConference(`${host}/conferenceCreated`)
  const percl = freeclimb.percl.build(conference)
  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 = freeclimb.percl.say('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 options = {
    // Hangup if we get a voicemail machine
    ifMachine: freeclimb.enums.ifMachine.hangup
  }
  const outDial = freeclimb.percl.outDial(agentPhoneNumber, createConferenceResponse.from, `${host}/outboundCallMade/${conferenceId}`, `${host}/callConnected/${conferenceId}`, options)
  const percl = freeclimb.percl.build(say, outDial)
  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
  // set the leaveConferenceUrl for the inbound caller, so that we can terminate the conference when they hang up
  const options = {
    leaveConferenceUrl: `${host}/leftConference`
  }
  // Add initial caller to conference
  const addToConference = freeclimb.percl.addToConference(conferenceId, outboundCallResponse.callId, options)
  const percl = freeclimb.percl.build(addToConference)
  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.dialCallStatus != freeclimb.enums.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 = freeclimb.percl.addToConference(conferenceId, callConnectedResponse.callId)
  const percl = freeclimb.percl.build(addToConference)
  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 = {
    status: freeclimb.enums.conferenceStatus.TERMINATED
  }
  freeclimb.api.conferences.update(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);
}