Implementing Voicemail with Twilio Flex, TaskRouter, and WFO

With Twilio's suite of tools, it's easy for create a fully functioning voicemail box for Flex. This guide walks you through creating a special voicemail-specific TaskRouter queue with no agents, and a workflow to send tasks to this queue after they've waited for 30 seconds. With the help of some Twilio functions, we're able to create a customizable voicemail message, append the recording file's URL to the task, and cancel the task so as not to skew WFO reports with additional abandoned calls.

Here are the steps required for building a fully-functioning Flex Voicemail system:

  1. Create a Voicemail Queue in TaskRouter
  2. Create a Voicemail Workflow in TaskRouter
  3. Create a Task Router Event Stream Function
  4. Create a Voicemail Function
  5. Create a Voicemail Recording Handler Function

Create a Voicemail Queue in TaskRouter

  1. Access the TaskRouter page in Console.
  2. Click the default Flex Task Assignment workspace, or the appropriate workspace if you have modified this.
  3. Click TaskQueues.
  4. Click the red Plus sign + icon to add a new Queue.
  5. Name your TaskQueue Voicemail (or something similarly identifiable), and then change the QUEUE EXPRESSION to 1==2 to prevent workers from entering this queue. Click Save when finished.

Setup a Voicemail Workflow in TaskRouter

  1. Access the TaskRouter page in Console.
  2. Click the default Flex Task Assignment workspace, or the appropriate workspace if you have modified this.
  3. Click Workflows.
  4. Click the default Assign to Anyone workflow, or the appropriate workflow if you have modified this.
  5. Click Add a Filter.
  6. Name your Filter Voicemail (or something similarly identifiable), and then change the TIMEOUT to 30 seconds. Click Add a Step when finished.
  7. Click the QUEUE value, and then select the Voicemail queue you created in the previous section. Click Save when finished.

Create a Task Router Event Stream Function

  1. Access the Functions page in Console.
  2. Click the red Plus sign + icon to add a new Function.
  3. Select the "Blank" template, and then click Create.
  4. Add a FUNCTION NAME, PATH, and CODE, and then click Save.
    • FUNCTION NAME: Name your Function trEventStream (or something similarly identifiable).
    • PATH: Use the function name trEventStream (or something similarly identifiable).
    • CODE: Copy and paste the code below:
      exports.handler = function(context, event, callback) {
          // get configured twilio client
          const client = context.getTwilioClient();
          
          // setup an empty success response
          let response = new Twilio.Response();
          response.setStatusCode(204);
          
          // switch on the event type
          switch(event.EventType) {
              case 'task-queue.entered':
                  // ignore events that are not entering the Voicemail TaskQueue
                  if (event.TaskQueueName !== 'Voicemail') {
                      return callback(null, response);
                  }
          
                  let taskSid = event.TaskSid;
                  let taskAttributes = JSON.parse(event.TaskAttributes);
                  let callSid = taskAttributes.call_sid;
          
                  let url = `https://${context.DOMAIN_NAME}/voicemail?taskSid=${taskSid}`;
          
                  // redirect call to voicemail
                  client.calls(callSid).update({
                      method: 'POST',
                      url: encodeURI(url)
                  }).then(() => {
                      return callback(null, response)
                  }).catch(err => {
                      response.setStatusCode(500);
                      return callback(err, response)
                  });
              break;
              default:
                  callback(null, response);
              break;
          }
      };

Now we need to tell TaskRouter to push its events to our function we just setup. First, you'll need to copy the full trEventStream function path. This can be found on the function page, and should be in the following format:

https://color-noun-1234.twil.io/trEventStream
  1. Access the TaskRouter page in Console.
  2. Click the default Flex Task Assignment workspace, or the appropriate workspace if you have modified this.
  3. Click Settings.
  4. Enter the Task Router Event Stream Function URL you just copied into the EVENT CALLBACK URL field, and then select ALL EVENTS. Click Save when finished.

Create a Voicemail Function

  1. Access the Functions page in Console.
  2. Click the red Plus sign + icon to add a new Function.
  3. Select the "Blank" template, and then click Create.
  4. Add a FUNCTION NAME, PATH, and CODE, and then click Save.
    • FUNCTION NAME: Name your Function "voicemail" (or something similarly identifiable).
    • PATH: The path must be "voicemail", as the Event Stream Function calls for this.
    • CODE: Copy and paste the code below:
      exports.handler = function(context, event, callback) {
          
        let taskSid = event.taskSid;
        let actionUrl = `https://${context.DOMAIN_NAME}/voicemail-complete?taskSid=${taskSid}`;
          
        let twiml = new Twilio.twiml.VoiceResponse();
        twiml.say("Sorry, no one is available to take your call. Please leave a message at the beep. When you're done, press pound or just hang-up.");
        twiml.record({
          action: encodeURI(actionUrl),
          finishOnKey: '#',
          playBeep: true,
          transcribe: true
        });
        callback(null, twiml);
      };

Create a Voicemail Recording Handler Function

  1. Access the Functions page in Console.
  2. Click the red Plus sign + icon to add a new Function.
  3. Select the "Blank" template, and then click Create.
  4. Add a FUNCTION NAME, PATH, and CODE, and then click Save.
    • FUNCTION NAME: Name your Function "Voicemail complete" (or something similarly identifiable).
    • PATH: Use the function name voicemail-complete (or something similarly identifiable).
    • CODE: Copy and paste the code below:
      exports.handler = function(context, event, callback) {
        const client = context.getTwilioClient(),
              taskSid = event.taskSid,
              segmentLink = event.RecordingUrl;
      let twiml = new Twilio.twiml.VoiceResponse(); twiml.say("Thank you for your message. Good bye."); callback(null, twiml); // retrieve the task this voicemail recording corresponds to client.taskrouter.workspaces(context.TR_WORKSPACE_SID) .tasks(taskSid) .fetch() .then(task => { // parse the task attributes - lets append some WFO specific attributes // this will let WFO know this task was a voicemail and not an abandoned call let taskAttributes = JSON.parse(task.attributes); taskAttributes.conversations = taskAttributes.conversations || {}; taskAttributes.conversations = Object.assign(taskAttributes.conversations, { segment_link: segmentLink, abandoned: "Follow-Up", abandoned_phase: "Voicemail" }); // update the task attributes with the WFO specific information client.taskrouter.workspaces(context.TR_WORKSPACE_SID) .tasks(taskSid) .update({ attributes: JSON.stringify(taskAttributes) }).then(() => { // end return callback(null, null); }); }) };
Have more questions? Submit a request
Powered by Zendesk