SIGN UP

Call queue with callback function

ACD Callback

Good customer service is one of the critical components of the successful business these days. Many interactions with customers happen over the phone when they making calls to company's call center and, since it's rather expensive to keep a big number of agents in the call center, customers have to spend their time in a queue listening to some music and waiting for the answer. Fortunately, there is a solution for this problem - queue with callback function. IVR tells customer that he can press some key (for example, # or *) to hangup the call, but stay in the queue. After customer's position in the queue was reached, system connects agent with the customer by calling customer's number. It's rather expensive module in case of Enterprise call center solutions, but with VoxImplant any developer can implement this type of queue in a few minutes. Please notice that you need to read our "Using ACD module for call queuing" post before reading this tutorial, since we will rely on some terms, code and entities described in the post.

First of all, sessions that use ACD module can exists without active calls during 2 hours. Let's modify our scenario from the "Using ACD module for call queuing" post to enable callback function:

// Enable ACD module
require(Modules.ACD);

let request;
let originalCall;
let callerid;
let statusInterval;
let callback = false;

VoxEngine.addEventListener(AppEvents.CallAlerting, handleInboundCall);

// Handle inbound call
function handleInboundCall(e) {
  originalCall = e.call;
  callerid = e.callerid;
  // Add event listeners
  originalCall.addEventListener(CallEvents.Connected, handleCallConnected);
  originalCall.addEventListener(CallEvents.PlaybackFinished, handlePlaybackFinished);
  originalCall.addEventListener(CallEvents.Failed, cleanup);
  originalCall.addEventListener(CallEvents.Disconnected, cleanup);
  // Answer call
  originalCall.answer();
}

// Terminate call and session
function cleanup(e) {
  if (request) {
    // Remove call from queue
    request.cancel();
    request = null;
  }
  // terminate session
  VoxEngine.terminate();
}

// Play music after TTS finish
function handlePlaybackFinished(e) {
  e.call.startPlayback('http://cdn.voximplant.com/toto.mp3');
}

// Get suffix for the number
function ordinal_suffix_of(i) {
  const j = i % 10,
    k = i % 100;
  if (j === 1 && k != 11)
    return i + 'st';
  else if (j === 2 && k != 12)
    return i + 'nd';
  else if (j === 3 && k != 13)
    return i + 'rd';
  return i + 'th';
}

// Handle callback request
function handleToneReceived(e) {
  if (e.tone === '#') {
    callback = true;
    originalCall.hangup(); // <--  despite there is no calls in the session it will stay alive
  }
}

// Call connected
function handleCallConnected(e) {
  // Enable key input processing
  originalCall.handleTones(true);
  originalCall.addEventListener(CallEvents.ToneReceived, handleToneReceived);
  // Put the call into the queue 'MainQueue'
  request = VoxEngine.enqueueACDRequest('MainQueue', callerid);

  // Get call status in queue after it was put in the queue
  request.addEventListener(ACDEvents.Queued, (acdevent) => {
    request.getStatus();
  });

  // Notify caller about his position in the queue
  request.addEventListener(ACDEvents.Waiting, (acdevent) => {
    const minutesLeft = acdevent.ewt + 1;
    let minutesWord = ' minute.';
    if (minutesLeft > 1) {
      minutesWord = ' minutes.';
    }
    originalCall.say(`You are ${ordinal_suffix_of(acdevent.position)}` +
      ` in a queue. Represetative will answer you in ${(acdevent.ewt + 1)} ${minutesWord} You can `+
      `press pound and we will call you back as soon as there is a free operator available.`, 
      Language.US_ENGLISH_FEMALE);
  });

  request.addEventListener(ACDEvents.Error, (acdevent) => {});

  // Connect caller with operator
  request.addEventListener(ACDEvents.OperatorReached, (acdevent) => {
    if (callback) {
      acdevent.operatorCall.say('Please wait while we connecting you with a customer.', 
        Language.US_ENGLISH_FEMALE);
      acdevent.operatorCall.addEventListener(CallEvents.Disconnected, VoxEngine.terminate);
      acdevent.operatorCall.addEventListener(CallEvents.PlaybackFinished, (callevent) => {
        // Calling customer
        originalCall = VoxEngine.callUser(callerid);
        originalCall.addEventListener(CallEvents.Connected, (callevent) => {
          VoxEngine.sendMediaBetween(acdevent.operatorCall, originalCall);
          clearInterval(statusInterval);
        });
        originalCall.addEventListener(CallEvents.Failed, cleanup);
        originalCall.addEventListener(CallEvents.Disconnected, cleanup);
      });
    } else {
      VoxEngine.sendMediaBetween(acdevent.operatorCall, originalCall);
      acdevent.operatorCall.addEventListener(CallEvents.Disconnected, VoxEngine.terminate);
      clearInterval(statusInterval);
    }
  });

  request.addEventListener(ACDEvents.Error, (acdevent) => {
    Logger.write(`ACD Error ${acdevent.error}`);
  });

  // No operators are available
  request.addEventListener(ACDEvents.Offline, (acdevent) => {
    originalCall.say('All operators are currently offline, please try to call again later.',
      Language.US_ENGLISH_FEMALE);
    originalCall.addEventListener(CallEvents.PlaybackFinished, (e) => {
      VoxEngine.terminate();
    });
  });

  // Get current call status in a queue every 30 seconds
  statusInterval = setInterval(request.getStatus, 30000);
}

This scenario processes inbound calls, but sometimes developers would like to put call in a queue from a callback form. It can be done using StartScenarios HTTP API request and additional code in the scenario:

let displayName;
const callback = true;
// Handle StartScenarios request
VoxEngine.addEventListener(AppEvents.Started, function (e) {
  let data = VoxEngine.customData();
  // Let's assume customer's phone and name are passed in script_custom_data param in phone:name form
  data = data.split(':');
  callerid = data[0];
  displayName = data[1];
  Logger.write(`Put ${displayName} with number ${callerid} in a queue`);
  // Put request in queue with name 'MainQueue'
  request = VoxEngine.enqueueACDRequest('MainQueue', callerid);
  // ... and the same code like in previous example
});

It's also possible to make requests to the session using media_session_access_url and get queue position updates to display it to the customer.

Add your comment

Please, enter valid email

Get your free developer account or talk with our sales team to learn more about Voximplant solutions
SIGN UP
Contact sales

Please complete this field.

Please complete this field.

Please complete this field.

Choose the solution

Please complete this field.

Please complete this field.