Voximplant. Blog

Step-by-step call center tutorial part 6

IVR and operator skills

In a simple scenario all incoming user calls are evenly distributed among all available operators. But as a number of users grows, it became more convenient to direct specific requests to specific operators who can handle them better. This raises a number of questions: how to figure out user question and how to define which operator can answer that questions.

First question is addressed by an IVR, “Interactive voice response” system, which asks a caller questions and interpret answers either as keypresses or via voice recognition. Such system can be written via JavaScript by using say and handleTones methods. Or you can use the IVR framework that is built into VoxEngine and allows to construct IVR menus from ready-made blocks.

Second question is addressed by an “operator skill” concept. You can define a number of skills. Each skill is assigned to one or more operators and to one or more queues. After request is enqueued via enqueueACDRequest, it will reach only operators that have all the required skills. For the operator to be reached, following conditions must be met:

  1. Set of skills associated with the queue must be a subset of skills associated with the operator.
  2. Both queue and operator must be associated with same application.

The call queue itself works like a filter: it allows to enqueue requests only to operators who have all the skills that are set for the specific queue. Entire setup works in two steps: ask user questions, interpret answers and queue request for operators with appropriate skills. For demonstration purpose, we will implement a simple IVR that will ask “If you have a warranty question, press 1, otherwise press 2”. Selecting “1” from a phone keyboard will forward user to the operator with “warranty” skill. Selecting “2” will forward to any available operator. In order to use IVR module include it into your scenario CommonJS-style:


IVR module works by creating a number of linked states and calling enter method on root state, passing it call object. At this point you should have a skill named “general” associated with queue “main” and one operator. Create a second queue “warranty”, a second skill “warranty” and a second operator. Associate “general” skill with both “main” and “warranty” queues, while “warranty” skill only with “warranty” queue. That way, requests via “main” queue will reach any operators with “general” skill (“operator who can answer non-specific questions”), and requests via “warranty” queue will reach only operators that have both “general” and “warranty” skills (“operator can answer non-specific questions and questions about warranty). After queues are set, associate “general” skill with both operators and “warranty” skill only with second operator.  Complete call center code that will reach different operators based on user choice is listed here:

  1. require(Modules.ACD);
  2. require(Modules.IVR);
  4. VoxEngine.addEventListener(AppEvents.CallAlerting, function(e) {
  5. var inc = e.call;
  6. var incId = e.callerid;
  7. var request = null;
  8. var out = null;
  9. inc.answer();
  10. inc.addEventListener(CallEvents.Connected, function() {
  11. ivr();
  12. });
  13. inc.addEventListener(CallEvents.Disconnected, function() {
  14. // If operator already connected, this also hangs up operator's call.
  15. // Also cancels all events like ACDEvents.Waiting
  16. request.cancel();
  17. // Some logic can be performed instead of session termination
  18. VoxEngine.terminate();
  19. });
  21. function ivr() {
  22. var stateWarranty = new IVRState("warranty", {
  23. type: 'noinput',
  24. prompt: {say: "Your warranty issue will be handled by first available expert"},
  25. }, function() {
  26. enqueue('warranty');
  27. });
  29. var stateGeneral = new IVRState("general", {
  30. type: 'noinput',
  31. prompt: {say: "Your issue will be handled by first available expert"},
  32. }, function() {
  33. enqueue('main');
  34. });
  36. var mainState = new IVRState("main", {
  37. type: 'select',
  38. prompt: {say: "If you have a warranty question, press 1, otherwise press 2"},
  39. nextStates: {
  40. '1': stateWarranty,
  41. '2': stateGeneral,
  42. },
  43. });
  45. mainState.enter(inc);
  46. }
  48. function enqueue(queue) {
  49. var isLoop = true;
  50. inc.startPlayback("http://cdn.voximplant.com/bb_remix.mp3", isLoop);
  51. request = VoxEngine.enqueueACDRequest(queue, incId);
  52. // This event only fired if request was queued without errors.
  53. request.addEventListener(ACDEvents.Queued, function(e) {
  54. request.getStatus();
  55. });
  56. request.addEventListener(ACDEvents.Waiting, function(e) {
  57. inc.say("Queue position is " + e.position);
  58. inc.addEventListener(CallEvents.PlaybackFinished, function(e) {
  59. // Note that say() changes audio source, so we need to change it back,
  60. // but only if operator is not yet connected.
  61. if (!out) {
  62. inc.startPlayback("http://cdn.voximplant.com/bb_remix.mp3", isLoop);
  63. }
  64. });
  65. setTimeout(function() { request.getStatus(); }, 30000);
  66. });
  67. request.addEventListener(ACDEvents.OperatorReached, function (e) {
  68. out = e.operatorCall;
  69. VoxEngine.sendMediaBetween(inc, out);
  70. out.addEventListener(CallEvents.Disconnected, function() {
  71. inc.hangup();
  72. // Some logic can be performed instead of session termination
  73. VoxEngine.terminate();
  74. });
  75. });
  76. }
  77. });

Try to login both operators, call phone number and select “2”. Request will be queued into “main” queue and will reach any operator since “general” skill is associated with “main” queue and both operators. Now call phone number and select “1”. Request will be queued into “warranty” queue and will reach only second operator since only “warranty” skill is associated with “warranty” queue and that skill is also associated only with second operator.

Tip: If you check “Settings” –> “Skills” you’ll see 1 operator in “warranty” and 2 operators in “main” queue.


Tagged in