Make an outbound call with Twilio

The following flow diagram shows the sequence of operations performed once an agent starts an outbound call from the Fusion application:

A graphic showing the outbound call scenario in Twilio.

  1. When the agent starts an outbound call from the Fusion application by clicking on the phone number, the onOutgoingEvent event is fired.
  2. The onOutgoingEvent event listener in your media toolbar application receives this event and starts the outbound call by calling the Twilio device.connect API by passing the phone number to be dialed.
  3. Twilio starts the outbound call and returns the Call object.
  4. The newCommEvent action is called from your media toolbar application.
  5. The Fusion application shows the dialing panel once the newCommEvent action is received

To make an outbound call using Twilio , we need to call the connect method of the Twilio Device object. The code looks like this:

const device = new Device(token);
 
let call = await device.connect({
  params: {
    To: '+15551234567'
  }
});

This example shows an try to create a new connection with the Twilio application that you associated with the Access Token used when instantiating the Device instance. This method returns a Promise with the Call object. You can track this Call object to monitor or change the active call. You can listen to user actions by listening to the to the accept, disconnect and cancel events on the Call object.

You can update the makeOutboundCall method in VendorHandler class for this as shown in the following example:

public async makeOutboundCall(phoneNumber: string, eventId: string): Promise<void> {
        const params = {
            To: phoneNumber,
        };
        if (this.device) {
            this.call = await this.device.connect({ params });
            const callId = this.call.parameters.CallSid
            this.call.on("accept", () => { this.integrationEventsHandler.outboundCallAcceptedHandler(callId) });
            this.call.on("disconnect", () => { this.integrationEventsHandler.callHangupHandler(callId) });
            this.call.on("cancel", () => { this.integrationEventsHandler.callRejectedHandler(callId) });
        }
    }

Complete code

Here's the complete code of the vendorHandler.ts file for starting an outbound call:

import { Call, Device } from '@twilio/voice-sdk';
import { ICtiVendorHandler } from './ICtiVendorHandler';
import { IntegrationEventsHandler } from '../integrationEventsHandler';
 
export class VendorHandler implements ICtiVendorHandler {
    private twilio: any;
    private device: Device | null;
    private integrationEventsHandler: IntegrationEventsHandler;
    private call: Call | null;
    public idAndToken: any;
 
    constructor(integrationEventsHandler: IntegrationEventsHandler) {
        this.twilio = (window as any).Twilio;
        this.device = null;
        this.idAndToken = null;
        this.integrationEventsHandler = integrationEventsHandler;
        this.call = null;
    }
 
    public async makeAgentAvailable(): Promise<void> {
        this.idAndToken = await this.getIdAndToken();
        this.device = new this.twilio.Device(this.idAndToken.token, {
            logLevel: 1,
            codecPreferences: ["opus", "pcmu"],
            enableRingingState: true
        });
        let resolve: Function;
        let reject: Function;
        if (this.device) {
            this.device.on("registered", () => {
                console.log("Registration completed ...")
                resolve();
            });
            this.device.on("error", (deviceError) => {
                console.error("Registration Failed ...", deviceError);
                reject();
            });
 
            this.device.on("incoming", this.incomingCallCallback);
 
            this.device.register();
        }
        return new Promise((res: Function, rej: Function) => {
            resolve = res;
            reject = rej;
        });
    }
 
    public incomingCallCallback = (call: Call) => {
        this.integrationEventsHandler.incomingCallHandler(call.parameters.From, call.parameters.CallSid);
        this.call = call;
    }
    public async makeAgentUnavailable(): Promise<void> {
        let resolve: Function;
        let reject: Function;
        if (this.device) {
            this.device.on("unregister", () => {
                console.log("Successfully UnRegistered ...")
                resolve();
            });
            this.device.on("error", (deviceError) => {
                console.error("Failed to unregister ...", deviceError);
                reject();
            });
            this.device.unregister();
        }
        return new Promise((res: Function, rej: Function) => {
            resolve = res;
            reject = rej;
        });
    }
    public async makeOutboundCall(phoneNumber: string, eventId: string): Promise<void> {
        const params = {
            To: phoneNumber,
        };
        if (this.device) {
            this.call = await this.device.connect({ params });
            const callId = this.call.parameters.CallSid
            this.call.on("accept", () => { this.integrationEventsHandler.outboundCallAcceptedHandler(callId) });
            this.call.on("disconnect", () => { this.integrationEventsHandler.callHangupHandler(callId) });
            this.call.on("cancel", () => { this.integrationEventsHandler.callRejectedHandler(callId) });
        }
    }
    public async acceptCall(): Promise<void> {
        if (this.call) {
            this.call.accept();
        }
    }
    public async rejectCall(): Promise<void> {
        if (this.call) {
            this.call.reject();
        }
    }
    public async hangupCall(): Promise<void> {
        if (this.call) {
            this.call.disconnect();
        }
    }
 
    private async getIdAndToken(): Promise<any> {
        const headers: Headers = (new Headers()) as Headers;
        headers.set('Accept', 'application/json');
        const request: Request = new Request('https://twilio-voice-stream.com/token', {
            method: 'GET',
            headers: headers
        }) as Request;
        const idAndToken: Response = await fetch(request);
        this.idAndToken = await idAndToken.json();
        return this.idAndToken;
    }
}

Verify your progress

After finishing these steps, use OJET server to start you application and sign in to your Fusion application. Open the media toolbar and make your agent available for calls by clicking on the Agent Availability button. To start an outbound call, open the contact from your Fusion application and click the phone number, which starts the outbound call.