Set up Toggle Agent availability in Twilio

To make the agent available or unavailable, we need to use the Device object in the Twilio SDK. The Device object represents a softphone that communicates with Twilio to help inbound and outbound audio connections.

Overview

Here's an overview of the sequence of operations performed once an agent marks themselves as available by clicking the Toggle Agent Availability button in the media toolbar application:

graphic showing the toggle agent availability.

  1. The agent clicks the Toggle Agent Availability button on the media toolbar.
  2. The media toolbar application fires a REST API call to fetch the ID and toked required for authentication.
  3. The Twilio service authenticates the request and returns the ID and token.
  4. The media toolbar application uses this token to initialize the Device object provided by Twilio SDK.
  5. Once the device object is registered in Twilio, it fires a registered event.
  6. When the registered event is received in your media toolbar application, the invoke agentStateEvent UEF operation to make the agent state available.
  7. After the fusion application identifies this action, the agent state is made as Available in the Fusion application also.

Update makeAgentAvailable method in VendorHandler to call the supplier API to make the agent available

  1. Create a method in the vendorHandler class (src/ts/cti/vendor/vendorHandler.ts) with the following code:
    private async getIdAndToken(): Promise<any> {
        const headers: Headers = (new Headers()) as Headers;
        const url: string =  'https://twilio-node-voice-stream.com/token'; // Replace this url with the url of the deployed node app
        headers.set('Accept', 'application/json');
        const request: Request = new Request(url, {
            method: 'GET',
            headers: headers
        }) as Request;
        const idAndToken: Response = await fetch(request);
        this.idAndToken = await idAndToken.json();
        return this.idAndToken;
    }
  2. Add the following import statement:
    import { Call, Device } from '@twilio/voice-sdk';
    import { ICtiVendorHandler } from './ICtiVendorHandler';
    import { IntegrationEventsHandler } from '../integrationEventsHandler';
  3. Create two class properties in Twilio, device and idAndToken as shown:
    export class VendorHandler implements ICtiVendorHandler {
        private twilio: any;
        private device: Device | null;
        private integrationEventsHandler: IntegrationEventsHandler;
        public idAndToken: any;
     
        constructor(integrationEventsHandler: IntegrationEventsHandler) {
            this.twilio = (window as any).Twilio;
            this.device = null;
            this.idAndToken = null;
            this.integrationEventsHandler = integrationEventsHandler;
        }
    // ...
    }
  4. Instantiate a device with DeviceOptions:
    const idAndToken = await this.getIdAndToken(); // get the id token
    this.device = new this.twilio.Device(idAndToken.token, {
        logLevel: 1,
        codecPreferences: ["opus", "pcmu"],
        enableRingingState: true
    });
  5. To make the agent available call the register on the device instance as shown:
    this.device.register();
  6. Use the registered event and error event to get the result for registration event as shown:
    this.device.on("registered", () => {
        // Do your logic when registration is completed.
    });
    this.device.on("error", (deviceError) => {
        // Do your logic when the registration fails
    });
  7. Update the makeAgentAvailable method in vendorHandler as shown:
    public async makeAgentAvailable(): Promise<void> {
        await this.getIdAndToken();
        this.device = 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.register();
        }
        return new Promise((res: Function, rej: Function) => {
            resolve = res;
            reject = rej;
        });
    }
  8. Update the makeAgentUnavailable method in VendorHandler as shown:
    public async makeAgentUnavailable(): Promise<void> {
        let resolve: Function;
        let reject: Function;
        if (this.device) {
            this.device.on("unregistered", () => {
                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;
        });
    }
  9. View the complete code:
    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;
        public idAndToken: any;
     
        constructor() {
            this.twilio = (window as any).Twilio;
            this.device = null;
            this.idAndToken = null;
            this.integrationEventsHandler = integrationEventsHandler;
        }
     
        public async makeAgentAvailable(): Promise<void> {
            const idAndToken = await this.getIdAndToken();
            this.device = new Device(idAndToken.token, {
                logLevel: 1,
                codecPreferences: [Call.Codec.Opus, Call.Codec.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.register();
            }
            return new Promise((res: Function, rej: Function) => {
                resolve = res;
                reject = rej;
            });
        }
        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> {
            throw new Error('Method not implemented.');
        }
        public async acceptCall(): Promise<void> {
            throw new Error('Method not implemented.');
        }
        public async rejectCall(): Promise<void> {
            throw new Error('Method not implemented.');
        }
        public async hangupCall(): Promise<void> {
            throw new Error('Method not implemented.');
        }
     
        private async getIdAndToken(): Promise<any> {
            const headers: Headers = (new Headers()) as Headers;
            headers.set('Accept', 'application/json');
            const request: Request = new Request('https://twilio-node-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

Sign in to your Fusion application and open the media toolbar. Click the Agent Availability button from your media toolbar application. You'll see that the button color changes to and the phone icon status in the Fusion application is changed to Available.