Send an SMS During a Call

🚧

This tutorial is for sending a message in the context of an active call using PerCL, the Performance Command Language. For simply sending a message, see Send an SMS Message in the API Reference.

👍

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

To send a message using PerCL you need to use the Sms PerCl command. In this example, our voice application will answer a call and send a text message. The status of the outgoing message is tracked using the notificationUrl property of the Sms command.

An incoming call is initiated and FreeClimb POSTs to the incomingCall endpoint to determine how to handle it. The PerCL script assembled in response will send a text message from the incoming number to the configured mobile number informing them that a call is coming in. As the message is processed, its various status states are sent to the notificationUrl, which the application logs.

Create your package.json file and save in the root directory of your project:

{
  "name": "node-send-sms-during-a-call-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:

// Imports and setup
require('dotenv').config()
const express = require('express')
const bodyParser = require('body-parser')
const app = express()
app.use(bodyParser.json())
const freeclimbSDK = require('@freeclimb/sdk')

const port = process.env.PORT || 80
const host = process.env.HOST
const accountId = process.env.ACCOUNT_ID
const apiKey = process.env.API_KEY
const freeclimb = freeclimbSDK(accountId, apiKey)
// Your account ID & api key can be found under API credentials on the FreeClimb Dashboard
app.post('/incomingCall', (req, res) => {
  const options = {
    notificationUrl: `${host}/notificationUrl`
  }
  // Create sms PerCL that sends sms to current caller using the number handling the request
  const smsCommand = freeclimb.percl.sms(req.body.to, req.body.from, 'Incoming Phone Call', options)
  const percl = freeclimb.percl.build(smsCommand)
  res.status(200).json(percl)
})

// Receive status updates of the sms
app.post('/notificationUrl', (req, res) => {
  console.log('Outbound Message Status Change: ', req.body)
})

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}`)
})

Java

To send a message using PerCL you need to use the Sms PerCl command. In this example, our voice application will answer a call and send a text message. The status of the outgoing message is tracked using the notificationUrl property of the Sms command.

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
 */

buildscript {
    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:

import org.springframework.web.bind.annotation.RestController;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
 
import com.vailsys.freeclimb.api.FreeClimbException;
import com.vailsys.freeclimb.api.message.Status;
import com.vailsys.freeclimb.percl.PerCLScript;
import com.vailsys.freeclimb.percl.Sms;
import com.vailsys.freeclimb.webhooks.call.VoiceCallback;
import com.vailsys.freeclimb.webhooks.message.MessageStatus;
//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 String inboundCall(@RequestBody String body) {
    VoiceCallback voiceCallback;
 
    try {
      // convert json string to object
      voiceCallback = VoiceCallback.createFromJson(body);
 
      PerCLScript script = new PerCLScript();
 
      // send an sms message to the caller
      Sms sms = new Sms(voiceCallback.getFrom(), fromNumber, "Hello from FreeClimb");
 
      // set notificationUrl for when the message changes status
      sms.setNotificationUrl(notificationUrl);
 
      script.add(sms);
 
      return script.toJson();
    } catch (FreeClimbException pe) {
      // handle errors
    }
 
    return "[]";
  }

When the status of the message changes, such as it was successfully sent or failed to send, the notificationUrl callback will be called with a MessageStatus object containing the status of the message, as well as additional information about the message. In the example below, we check the status of the message to see if it failed to send.

@RequestMapping(value = {
      "/notificationUrl" }, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
 
  public String notificationUrl(@RequestBody String body) {
    MessageStatus messageStatus;
 
    try {
      messageStatus = MessageStatus.createFromJson(body);
      Status status = messageStatus.getStatus();
      if (status == Status.FAILED || status == Status.REJECTED) {
        // message Failed to send
      }
    } catch (FreeClimbException e) {
      // handle errors
    }
    return "[]";
  }

C#

To send a message using PerCL you need to use the Sms PerCl command. In this example, our voice application will answer a call and send a text message. The status of the outgoing message is tracked using the notificationUrl property of the Sms command.

Our voice application will answer a call and ask the user for a phone number to send a text message to. It then sends the message to the entered number and tells the user if it was able to send the message. There are three methods below on the ASP.NET Core MVC controller class that are handling this voice application. The first, InboundCall, is what is called when a call is received for the application. The others are callbacks used by the main application.

Example code:

[HttpPost] // POST /voice
    public ActionResult voice (VoiceRequest freeClimbRequest) {
      // Create an empty PerCL script container
      PerCLScript script = new PerCLScript ();
      // Verify inbound call is in proper state
      if (freeClimbRequest.getCallStatus == ECallStatus.Ringing) {
        // Create PerCL say script with US English as the language
        Say say = new Say ();
        say.setLanguage (ELanguage.EnglishUS);
        // Set greeting prompt
        say.setText ("Hello");
        // Add PerCL say script to PerCL container
        script.Add (say);
        // Create PerCL pause script with a 100 millisecond pause
        Pause pause = new Pause (100);
        // Add PerCL pause script to PerCL container
        script.Add (pause);
        // Create PerCL getdigits script
        string getDigitsUrl = AppUrl + "PhoneNumDone";
        GetDigits getDigits = new GetDigits (getDigitsUrl);
        // Set the max and min number of expected digits to 1
        getDigits.setMaxDigits (10);
        getDigits.setMinDigits (10);
        // Set the DTMF buffer flush to false
        getDigits.setFlushBuffer (EBool.False);
        // Create PerCL say script with US English as the language
        say = new Say ();
        say.setLanguage (ELanguage.EnglishUS);
        // Set color selection menu prompt
        say.setText ("Please enter the 10 digits phone number to send the text message to.");
        // Add main selection menu prompt to the getdigits script
        getDigits.setPrompts (say);
        // Add PerCL getdigits script to PerCL container
        script.Add (getDigits);
      }
      // Convert PerCL container to JSON and append to response
      return Content (script.toJson (), "application/json");
    }

In the InboundCall method, we answer the call (see Accept an Incoming Call for more detail) and ask the user to enter a phone number using the GetDigits PerCL command (see Collect Digits for more detail).

In the PhoneNumDone method, we get the phone number the user entered from the information in the request message sent by FreeClimb and then create the Sms PerCL command, setting the to and from phone number fields, and setting a hard-coded text value. We also set the notificationUrl property, which is a URL that is called when the status of our message changes. This lets us track the status of the message. The notificationUrl handler would typically look for a failure or delivered status and carry out actions appropriate to your application. In this tutorial’s status callback, MessageStatusCallback, we log the status.

Example code:

[HttpPost ("GetDigits")] // /voice/GetDigits
    public ActionResult GetDigits (GetDigitsActionCallback getDigitsStatusCallback) {
      // Create an empty PerCL script container
      PerCLScript script = new PerCLScript ();
      // Verify the getdigits contains a single digit response
      if ((getDigitsStatusCallback.getDigits != null) && (getDigitsStatusCallback.getDigits.Length == 10)) {
        // create properly formatted phone num
        string phoneNum = "+1" + getDigitsStatusCallback.getDigits;
        // create and add PerCL sms script to PerCL container
        Sms sms = new Sms (FromPhoneNumber, phoneNum, "Hello from FreeClimb SMS");
        // add a notification URL so we can track status of the message
        sms.setNotificationUrl(AppUrl + "MessageStatusCallback");
        script.Add (sms);
        // Create PerCL say script with US English as the language
        Say say = new Say ();
        say.setLanguage (ELanguage.EnglishUS);
        // Set color selected prompt
        say.setText ("We'll send the text message now. Goodbye.");
        // Add PerCL say script to PerCL container
        script.Add (say);

        // Create PerCL hangup script and add to the container
        script.Add (new Hangup ());
      }
      // unexpected getdigit response
      else {
        // Create PerCL say script with US English as the language
        Say say = new Say ();
        say.setLanguage (ELanguage.EnglishUS);
        // Set error selection prompt
        say.setText ("There was an error retrieving your selection. Goodbye.");
        // Add PerCL say script to PerCL container
        script.Add (say);
        // Create PerCL hangup script and add to the container
        script.Add (new Hangup ());
      }
      // Convert PerCL container to JSON and append to response
      return Content (script.toJson (), "application/json");
    }

    public ActionResult MessageStatusCallback(MessageStatus status)
    {
      // note this returns no PerCL; it's just a status message informating us of a change in status of a message we sent
      // Read the entire FreeClimb request JSON context
      // just log for tutorial. your app may need to take action based on status change
      Console.WriteLine ("Message Status Callback. Message ID: " + status.getMessageId + ". Message Status: " + status.getStatus.ToString ());
      // just return OK
      return Ok();
    }