Create complex IVRs for your CRM

Nathalie
24.11.2021 0 7:22 min

What is sipgate.io?

sipgate.io is a collection of APIs that enables sipgate’s customers to create flexible integrations for their individual needs.
Among other things, it provides interfaces for sending and receiving text messages or faxes, monitoring call history, and initiating and manipulating calls. In this tutorial we will explore the Push-API from sipgate.io to accept a call and start an IVR (Interactive Voice Response) process. The callers can then convey information using DTMF tones. DTMF tones are the tones you hear when you type on the keypad of your phone.

In this tutorial

The script in this project sets up a simple webserver that runs on your local machine. When someone tries to reach your sipgate number, this webserver answers the call and plays the IVR process. Our IVR system consists of three phases:

  1. Welcome phase: an audio file greets callers and asks them for their customer number.
  2. Request phase: after callers enter the number via their keypad, the system asks if they want to check their balance (key number 1) or talk to the customer service (key number 3).
  3. Final phase: depending on the callers decision, the system will finally play an audio file again.

Prerequisites: You have node.js and NPM installed on your computer.

First Steps

Setting up the project

We use Node.js to use the official sipgate.io node library. First, we create a new Node.js project that contains a index.ts file where the script will be written.

npm init -y
mkdir src
touch src/index.ts

We need some more dependencies, which we install with this command:

npm i sipgateio dotenv ts-node
npm i -D typescript
  • sipgateio: A library we developed that allows us to build a server and design responses to webhooks.
  • dotenv: This can be used to read the variables stored in the .env file.
  • ts-node &typescript: This helps us to be able to run typescript files (typescript is installed as dev dependency).

Further configurations

In addition, two more files are added:

touch .env tsconfig.json

The .env-file contains the server address (which we will add later) and the port to listen on:

SIPGATE_WEBHOOK_SERVER_ADDRESS=
SIPGATE_WEBHOOK_SERVER_PORT=8080
  

The file tsconfig.json specifies the root files and compiler options required to compile a Typescript project:

{
    "compilerOptions": {
        "target": "es2017",
        "module": "commonjs",
        "lib": ["dom", "es6", "es2017", "esnext.asynciterable"],
        "skipLibCheck": true,
        "sourceMap": true,
        "outDir": "./dist",
        "moduleResolution": "node",
        "removeComments": true,
        "noImplicitAny": true,
        "strictNullChecks": true,
        "strictFunctionTypes": true,
        "noImplicitThis": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "noImplicitReturns": true,
        "noFallthroughCasesInSwitch": true,
        "allowSyntheticDefaultImports": true,
        "esModuleInterop": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "resolveJsonModule": true,
        "baseUrl": "."
    },
    "exclude": ["node_modules"],
    "include": ["./src/**/*.ts"]
}
  

Lastly, we need to make changes to the automatically generated package.json. Add these lines for the execution of the project:

"main": "src/index.ts",
"scripts": {
    "start": "ts-node ."
}
    

Structure of the script

First, we need to import the dependencies we installed at the beginning so we can use them for our script:

import * as dotenv from "dotenv";
import { createWebhookModule, WebhookResponse } from "sipgateio";
    

Next, we set the environment variables as defined in the .env file. If they are not set, errors are thrown and the process terminates.

dotenv.config();

if (!process.env.SIPGATE_WEBHOOK_SERVER_ADDRESS) {
    console.error(
        "ERROR: You need to set a server address to receive webhook events!\n",
    );
    process.exit();
}
    
if (!process.env.SIPGATE_WEBHOOK_SERVER_PORT) {
    console.error(
        "ERROR: You need to set a server port to receive webhook events!\n",
    );
    process.exit();
}
    
const SERVER_ADDRESS = process.env.SIPGATE_WEBHOOK_SERVER_ADDRESS;
const PORT = process.env.SIPGATE_WEBHOOK_SERVER_PORT;
    

In the next step we define the DTMF input length for the different IVR steps. The constants indicate which DTMF input length is expected. For example, an 8-digit customer number. The enumeration „CallStage“ defines the IVR steps.

const MAX_WELCOME_DTMF_INPUT_LENGTH = 8;
const MAX_REQUEST_DTMF_INPUT_LENGTH = 1;

enum CallStage {
    WELCOMESTAGE,
    REQUESTSTAGE,
    ENDSTAGE,
}
    

We now start to build our server. The createServer function returns a Promise for a WebhookServer, which can then be used to register callback functions for various call events:

createWebhookModule()
.createServer({
    port: PORT,
    serverAddress: SERVER_ADDRESS,
})
.then((webhookServer) => { /* TODO */ });
    

The following content is added in the curly brackets under /* TODO */.

First, we map each call (callId) to its associated IVR step (CallStage).

In this project, our server needs to respond to newCall and onData events. To do this, we define a function to be called when a new call is received. We use the WebhookResponse.gatherDTMF function to access webhooks with a matching XML file to answer. The XML file contains the following attributes:

  • announcement: Here you can define the wav file that will be played to the callers.
  • timeout: After the announcement, a timer is started which specifies the maximum time in which DTMF tones are received. As soon as new DTMF tones are received (when the callers make key entries), the timer is restarted.
  • maxDigits: The maximum length of the input (DTMF tones) is defined here.
const stage = new Map <string, CallStage>();
webhookServer.onNewCall((newCallEvent) => {
    stage.set(newCallEvent.callId, CallStage.WELCOMESTAGE);
    console.log(`New call from ${newCallEvent.from} to ${newCallEvent.to}`);
    return WebhookResponse.gatherDTMF({
        maxDigits: MAX_WELCOME_DTMF_INPUT_LENGTH,
        timeout: 5000,
        announcement:
        "https://github.com/sipgate-io/io-labs-complex-ivr/blob/main/static/welcome.wav?raw=true",
    });
});
    

Via the onData event we receive the DTMF tones and the ID of the callers. This allows us to check whether the callers have made the correct entry. If so, the next phase asks if the caller:s want to view their credit or talk to customer support. If the input or the CallStage was incorrect, the call is hung up.

webhookServer.onData((dataEvent) => {
    const selection = dataEvent.dtmf;
    const callerId = dataEvent.callId;
    if (
        stage.get(callerId) === CallStage.WELCOMESTAGE &&
        selection.length === MAX_WELCOME_DTMF_INPUT_LENGTH
    ) {
        console.log(`The caller provided a valid id: ${selection} `);
        stage.set(callerId, CallStage.REQUESTSTAGE);
        return WebhookResponse.gatherDTMF({
        maxDigits: MAX_REQUEST_DTMF_INPUT_LENGTH,
        timeout: 5000,
        announcement:
            "https://github.com/sipgate-io/io-labs-complex-ivr/blob/main/static/request.wav?raw=true",
        });
    }

    //TODO
    
    return WebhookResponse.hangUpCall();
});
      

The last //TODO thing that needs to be added is the check which option the callers have chosen (key input 1 or 3). Accordingly, audio files will be played again, but this time no input is expected and after playing an announcement the call is ended.

if (
  stage.get(callerId) === CallStage.REQUESTSTAGE &&
  selection.length === MAX_REQUEST_DTMF_INPUT_LENGTH
) {
stage.set(callerId, CallStage.ENDSTAGE);
switch (selection) {
    case "1":
    console.log("Output 1");
    return WebhookResponse.gatherDTMF({
        maxDigits: 1,
        timeout: 0,
        announcement:
        "https://github.com/sipgate-io/io-labs-complex-ivr/blob/main/static/credit.wav?raw=true",
    });
    case "3":
    console.log("Output 2");
    return WebhookResponse.gatherDTMF({
        maxDigits: 1,
        timeout: 0,
        announcement:
        "https://github.com/sipgate-io/io-labs-complex-ivr/blob/main/static/customerservice.wav?raw=true",
    });
    default:
    return WebhookResponse.hangUpCall();
  }
}
});
      

Project execution

Great, now the script is up! To run everything you need to follow these steps:

  1. Start your local host with ssh -R 80:localhost:8080 nokey@localhost.run. You will see an output, copy from it the last URL.
  2. Add the URL in your .env-file next to SIPGATE_WEBHOOK_SERVER_ADDRESS
  3. Go to your sipgate account and set both the incoming and outgoing webhook URL to the URL from the previous steps.
  4. Now run npm start in the terminal of the root package of the project to start the server.

Now you can call your sipgate account number to start the IVR process. When the call is successfully established, your terminal will log some information.

Conclusion

If you’ve made it this far, congratulations! In this tutorial you have learned how to automatically answer a call and directly start an IVR process. It can be easily extended with personal IVR steps and individual audio files.

You can find the complete project in our GitHub-Repository.

If you want to learn more about the possibilities of our sipgate.io library, take a look at our other tutorials, e.g. on how to create insightful live statistics.

Keine Kommentare


Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert