In this tutorial we will show how to automate the routing of calls from customers to your support agents. In this example customers would select a product, then be connected to a specialist for that product. If no one is available our customer's number will be saved so that our agent can call them back.
In order to instruct TaskRouter to handle the Tasks, we need to configure a Workspace. We can do this in the TaskRouter Console or programmatically using the TaskRouter REST API.
In this Ruby on Rails application, this step will be executed in the initialization phase every time you run the app.
A Workspace is the container element for any TaskRouter application. The elements are:
We'll use a TaskRouterClient
provided in the twilio-ruby gem to create and configure the workspace.
lib/workspace_config.rb
1class WorkspaceConfig2WORKSPACE_NAME = 'Rails Workspace'.freeze3WORKFLOW_NAME = 'Sales'.freeze4WORKFLOW_TIMEOUT = ENV['WORKFLOW_TIMEOUT'].freeze5QUEUE_TIMEOUT = ENV['QUEUE_TIMEOUT'].freeze6ASSIGNMENT_CALLBACK_URL = ENV['ASSIGNMENT_CALLBACK_URL'].freeze7EVENT_CALLBACK_URL = ENV['EVENT_CALLBACK_URL'].freeze8BOB_NUMBER = ENV['BOB_NUMBER'].freeze9ALICE_NUMBER = ENV['ALICE_NUMBER'].freeze1011def self.setup12puts 'Configuring workspace, please wait ...'13new.setup14puts 'Workspace ready!'15end1617def initialize18@account_sid = ENV['TWILIO_ACCOUNT_SID']19@auth_token = ENV['TWILIO_AUTH_TOKEN']20@client = taskrouter_client21end2223def setup24@workspace_sid = create_workspace25@client = taskrouter_client26WorkspaceInfo.instance.workers = create_workers27workflow_sid = create_workflow.sid28WorkspaceInfo.instance.workflow_sid = workflow_sid29idle_activity_sid = activity_by_name('Idle').sid30WorkspaceInfo.instance.post_work_activity_sid = idle_activity_sid31WorkspaceInfo.instance.idle_activity_sid = idle_activity_sid32WorkspaceInfo.instance.offline_activity_sid = activity_by_name('Offline').sid33WorkspaceInfo.instance.workspace_sid = @workspace_sid34end3536private3738attr_reader :client, :account_sid, :auth_token3940def taskrouter_client41client_instance = Twilio::REST::Client.new(42account_sid,43auth_token44)4546client_instance.taskrouter.v147end4849def create_workspace50workspace = client.workspaces.list(friendly_name: WORKSPACE_NAME).first51workspace.delete unless workspace.nil?5253workspace = client.workspaces.create(54friendly_name: WORKSPACE_NAME,55event_callback_url: EVENT_CALLBACK_URL56)5758workspace.sid59end6061def create_workers62bob_attributes = "{\"products\": [\"ProgrammableSMS\"], \"contact_uri\": \"#{BOB_NUMBER}\"}"63alice_attributes = "{\"products\": [\"ProgrammableVoice\"], \"contact_uri\": \"#{ALICE_NUMBER}\"}"6465bob = create_worker('Bob', bob_attributes)66alice = create_worker('Alice', alice_attributes)6768{69BOB_NUMBER => { sid: bob.sid, name: 'Bob' },70ALICE_NUMBER => { sid: alice.sid, name: 'Alice' }71}72end7374def create_worker(name, attributes)75client.workspaces(@workspace_sid).workers.create(76friendly_name: name,77attributes: attributes,78activity_sid: activity_by_name('Idle').sid79)80end8182def activity_by_name(name)83client.workspaces(@workspace_sid).activities.list(friendly_name: name).first84end8586def create_task_queues87reservation_activity_sid = activity_by_name('Reserved').sid88assignment_activity_sid = activity_by_name('Busy').sid8990voice_queue = create_task_queue('Voice', reservation_activity_sid,91assignment_activity_sid,92"products HAS 'ProgrammableVoice'")9394sms_queue = create_task_queue('SMS', reservation_activity_sid,95assignment_activity_sid,96"products HAS 'ProgrammableSMS'")9798all_queue = create_task_queue('All', reservation_activity_sid,99assignment_activity_sid, '1==1')100101{ voice: voice_queue, sms: sms_queue, all: all_queue }102end103104def create_task_queue(name, reservation_sid, assignment_sid, target_workers)105client.workspaces(@workspace_sid).task_queues.create(106friendly_name: name,107reservation_activity_sid: reservation_sid,108assignment_activity_sid: assignment_sid,109target_workers: target_workers110)111end112113def create_workflow114queues = create_task_queues115config = workflow_config(queues)116117client.workspaces(@workspace_sid).workflows.create(118configuration: config.to_json,119friendly_name: WORKFLOW_NAME,120assignment_callback_url: ASSIGNMENT_CALLBACK_URL,121fallback_assignment_callback_url: ASSIGNMENT_CALLBACK_URL,122task_reservation_timeout: WORKFLOW_TIMEOUT123)124end125126def workspace_sid127@workspace_sid || 'no_workspace_yet'128end129130def workflow_config(queues)131default_target = default_rule_target(queues[:all].sid)132133{134task_routing: {135filters: [136{137expression: 'selected_product=="ProgrammableVoice"',138targets: [139rule_target(queues[:voice].sid),140default_target141]142},143{144expression: 'selected_product=="ProgrammableSMS"',145targets: [146rule_target(queues[:sms].sid),147default_target148]149}150],151default_filter: default_target152}153}154end155156def rule_target(sid)157{ queue: sid, priority: 5, timeout: QUEUE_TIMEOUT }158end159160def default_rule_target(sid)161{162queue: sid,163priority: 1,164timeout: QUEUE_TIMEOUT,165expression: '1==1'166}167end168end
Now let's look in more detail at all the steps, starting with the creation of the workspace itself.
Before creating a workspace, we need to delete any others with the same friendly_name
as the one we are trying to create. In order to create a workspace we need to provide a friendly_name
and a callback_url
where a requests will be made every time an event is triggered in our workspace.
lib/workspace_config.rb
1class WorkspaceConfig2WORKSPACE_NAME = 'Rails Workspace'.freeze3WORKFLOW_NAME = 'Sales'.freeze4WORKFLOW_TIMEOUT = ENV['WORKFLOW_TIMEOUT'].freeze5QUEUE_TIMEOUT = ENV['QUEUE_TIMEOUT'].freeze6ASSIGNMENT_CALLBACK_URL = ENV['ASSIGNMENT_CALLBACK_URL'].freeze7EVENT_CALLBACK_URL = ENV['EVENT_CALLBACK_URL'].freeze8BOB_NUMBER = ENV['BOB_NUMBER'].freeze9ALICE_NUMBER = ENV['ALICE_NUMBER'].freeze1011def self.setup12puts 'Configuring workspace, please wait ...'13new.setup14puts 'Workspace ready!'15end1617def initialize18@account_sid = ENV['TWILIO_ACCOUNT_SID']19@auth_token = ENV['TWILIO_AUTH_TOKEN']20@client = taskrouter_client21end2223def setup24@workspace_sid = create_workspace25@client = taskrouter_client26WorkspaceInfo.instance.workers = create_workers27workflow_sid = create_workflow.sid28WorkspaceInfo.instance.workflow_sid = workflow_sid29idle_activity_sid = activity_by_name('Idle').sid30WorkspaceInfo.instance.post_work_activity_sid = idle_activity_sid31WorkspaceInfo.instance.idle_activity_sid = idle_activity_sid32WorkspaceInfo.instance.offline_activity_sid = activity_by_name('Offline').sid33WorkspaceInfo.instance.workspace_sid = @workspace_sid34end3536private3738attr_reader :client, :account_sid, :auth_token3940def taskrouter_client41client_instance = Twilio::REST::Client.new(42account_sid,43auth_token44)4546client_instance.taskrouter.v147end4849def create_workspace50workspace = client.workspaces.list(friendly_name: WORKSPACE_NAME).first51workspace.delete unless workspace.nil?5253workspace = client.workspaces.create(54friendly_name: WORKSPACE_NAME,55event_callback_url: EVENT_CALLBACK_URL56)5758workspace.sid59end6061def create_workers62bob_attributes = "{\"products\": [\"ProgrammableSMS\"], \"contact_uri\": \"#{BOB_NUMBER}\"}"63alice_attributes = "{\"products\": [\"ProgrammableVoice\"], \"contact_uri\": \"#{ALICE_NUMBER}\"}"6465bob = create_worker('Bob', bob_attributes)66alice = create_worker('Alice', alice_attributes)6768{69BOB_NUMBER => { sid: bob.sid, name: 'Bob' },70ALICE_NUMBER => { sid: alice.sid, name: 'Alice' }71}72end7374def create_worker(name, attributes)75client.workspaces(@workspace_sid).workers.create(76friendly_name: name,77attributes: attributes,78activity_sid: activity_by_name('Idle').sid79)80end8182def activity_by_name(name)83client.workspaces(@workspace_sid).activities.list(friendly_name: name).first84end8586def create_task_queues87reservation_activity_sid = activity_by_name('Reserved').sid88assignment_activity_sid = activity_by_name('Busy').sid8990voice_queue = create_task_queue('Voice', reservation_activity_sid,91assignment_activity_sid,92"products HAS 'ProgrammableVoice'")9394sms_queue = create_task_queue('SMS', reservation_activity_sid,95assignment_activity_sid,96"products HAS 'ProgrammableSMS'")9798all_queue = create_task_queue('All', reservation_activity_sid,99assignment_activity_sid, '1==1')100101{ voice: voice_queue, sms: sms_queue, all: all_queue }102end103104def create_task_queue(name, reservation_sid, assignment_sid, target_workers)105client.workspaces(@workspace_sid).task_queues.create(106friendly_name: name,107reservation_activity_sid: reservation_sid,108assignment_activity_sid: assignment_sid,109target_workers: target_workers110)111end112113def create_workflow114queues = create_task_queues115config = workflow_config(queues)116117client.workspaces(@workspace_sid).workflows.create(118configuration: config.to_json,119friendly_name: WORKFLOW_NAME,120assignment_callback_url: ASSIGNMENT_CALLBACK_URL,121fallback_assignment_callback_url: ASSIGNMENT_CALLBACK_URL,122task_reservation_timeout: WORKFLOW_TIMEOUT123)124end125126def workspace_sid127@workspace_sid || 'no_workspace_yet'128end129130def workflow_config(queues)131default_target = default_rule_target(queues[:all].sid)132133{134task_routing: {135filters: [136{137expression: 'selected_product=="ProgrammableVoice"',138targets: [139rule_target(queues[:voice].sid),140default_target141]142},143{144expression: 'selected_product=="ProgrammableSMS"',145targets: [146rule_target(queues[:sms].sid),147default_target148]149}150],151default_filter: default_target152}153}154end155156def rule_target(sid)157{ queue: sid, priority: 5, timeout: QUEUE_TIMEOUT }158end159160def default_rule_target(sid)161{162queue: sid,163priority: 1,164timeout: QUEUE_TIMEOUT,165expression: '1==1'166}167end168end
We have a brand new workspace, now we need workers. Let's create them on the next step.
We'll create two workers: Bob and Alice. They each have two attributes: contact_uri
a phone number and products
, a list of products each worker is specialized in. We also need to specify an activity_sid
and a name for each worker. The selected activity will define the status of the worker.
A set of default activities is created with your workspace. We use the Idle
activity to make a worker available for incoming calls.
lib/workspace_config.rb
1class WorkspaceConfig2WORKSPACE_NAME = 'Rails Workspace'.freeze3WORKFLOW_NAME = 'Sales'.freeze4WORKFLOW_TIMEOUT = ENV['WORKFLOW_TIMEOUT'].freeze5QUEUE_TIMEOUT = ENV['QUEUE_TIMEOUT'].freeze6ASSIGNMENT_CALLBACK_URL = ENV['ASSIGNMENT_CALLBACK_URL'].freeze7EVENT_CALLBACK_URL = ENV['EVENT_CALLBACK_URL'].freeze8BOB_NUMBER = ENV['BOB_NUMBER'].freeze9ALICE_NUMBER = ENV['ALICE_NUMBER'].freeze1011def self.setup12puts 'Configuring workspace, please wait ...'13new.setup14puts 'Workspace ready!'15end1617def initialize18@account_sid = ENV['TWILIO_ACCOUNT_SID']19@auth_token = ENV['TWILIO_AUTH_TOKEN']20@client = taskrouter_client21end2223def setup24@workspace_sid = create_workspace25@client = taskrouter_client26WorkspaceInfo.instance.workers = create_workers27workflow_sid = create_workflow.sid28WorkspaceInfo.instance.workflow_sid = workflow_sid29idle_activity_sid = activity_by_name('Idle').sid30WorkspaceInfo.instance.post_work_activity_sid = idle_activity_sid31WorkspaceInfo.instance.idle_activity_sid = idle_activity_sid32WorkspaceInfo.instance.offline_activity_sid = activity_by_name('Offline').sid33WorkspaceInfo.instance.workspace_sid = @workspace_sid34end3536private3738attr_reader :client, :account_sid, :auth_token3940def taskrouter_client41client_instance = Twilio::REST::Client.new(42account_sid,43auth_token44)4546client_instance.taskrouter.v147end4849def create_workspace50workspace = client.workspaces.list(friendly_name: WORKSPACE_NAME).first51workspace.delete unless workspace.nil?5253workspace = client.workspaces.create(54friendly_name: WORKSPACE_NAME,55event_callback_url: EVENT_CALLBACK_URL56)5758workspace.sid59end6061def create_workers62bob_attributes = "{\"products\": [\"ProgrammableSMS\"], \"contact_uri\": \"#{BOB_NUMBER}\"}"63alice_attributes = "{\"products\": [\"ProgrammableVoice\"], \"contact_uri\": \"#{ALICE_NUMBER}\"}"6465bob = create_worker('Bob', bob_attributes)66alice = create_worker('Alice', alice_attributes)6768{69BOB_NUMBER => { sid: bob.sid, name: 'Bob' },70ALICE_NUMBER => { sid: alice.sid, name: 'Alice' }71}72end7374def create_worker(name, attributes)75client.workspaces(@workspace_sid).workers.create(76friendly_name: name,77attributes: attributes,78activity_sid: activity_by_name('Idle').sid79)80end8182def activity_by_name(name)83client.workspaces(@workspace_sid).activities.list(friendly_name: name).first84end8586def create_task_queues87reservation_activity_sid = activity_by_name('Reserved').sid88assignment_activity_sid = activity_by_name('Busy').sid8990voice_queue = create_task_queue('Voice', reservation_activity_sid,91assignment_activity_sid,92"products HAS 'ProgrammableVoice'")9394sms_queue = create_task_queue('SMS', reservation_activity_sid,95assignment_activity_sid,96"products HAS 'ProgrammableSMS'")9798all_queue = create_task_queue('All', reservation_activity_sid,99assignment_activity_sid, '1==1')100101{ voice: voice_queue, sms: sms_queue, all: all_queue }102end103104def create_task_queue(name, reservation_sid, assignment_sid, target_workers)105client.workspaces(@workspace_sid).task_queues.create(106friendly_name: name,107reservation_activity_sid: reservation_sid,108assignment_activity_sid: assignment_sid,109target_workers: target_workers110)111end112113def create_workflow114queues = create_task_queues115config = workflow_config(queues)116117client.workspaces(@workspace_sid).workflows.create(118configuration: config.to_json,119friendly_name: WORKFLOW_NAME,120assignment_callback_url: ASSIGNMENT_CALLBACK_URL,121fallback_assignment_callback_url: ASSIGNMENT_CALLBACK_URL,122task_reservation_timeout: WORKFLOW_TIMEOUT123)124end125126def workspace_sid127@workspace_sid || 'no_workspace_yet'128end129130def workflow_config(queues)131default_target = default_rule_target(queues[:all].sid)132133{134task_routing: {135filters: [136{137expression: 'selected_product=="ProgrammableVoice"',138targets: [139rule_target(queues[:voice].sid),140default_target141]142},143{144expression: 'selected_product=="ProgrammableSMS"',145targets: [146rule_target(queues[:sms].sid),147default_target148]149}150],151default_filter: default_target152}153}154end155156def rule_target(sid)157{ queue: sid, priority: 5, timeout: QUEUE_TIMEOUT }158end159160def default_rule_target(sid)161{162queue: sid,163priority: 1,164timeout: QUEUE_TIMEOUT,165expression: '1==1'166}167end168end
After creating our workers, let's set up the Task Queues.
Next, we set up the Task Queues. Each with a friendly_name
and a targetWorkers
, which is an expression to match Workers. Our Task Queues are:
SMS
- Will target Workers specialized in Programmable SMS, such as Bob, using the expression "products HAS \"ProgrammableSMS\""
.Voice
- Will do the same for Programmable Voice Workers, such as Alice, using the expression "products HAS \"ProgrammableVoice\""
.All
- This queue targets all users and can be used when there are no specialist around for the chosen product. We can use the "1==1"
expression here.lib/workspace_config.rb
1class WorkspaceConfig2WORKSPACE_NAME = 'Rails Workspace'.freeze3WORKFLOW_NAME = 'Sales'.freeze4WORKFLOW_TIMEOUT = ENV['WORKFLOW_TIMEOUT'].freeze5QUEUE_TIMEOUT = ENV['QUEUE_TIMEOUT'].freeze6ASSIGNMENT_CALLBACK_URL = ENV['ASSIGNMENT_CALLBACK_URL'].freeze7EVENT_CALLBACK_URL = ENV['EVENT_CALLBACK_URL'].freeze8BOB_NUMBER = ENV['BOB_NUMBER'].freeze9ALICE_NUMBER = ENV['ALICE_NUMBER'].freeze1011def self.setup12puts 'Configuring workspace, please wait ...'13new.setup14puts 'Workspace ready!'15end1617def initialize18@account_sid = ENV['TWILIO_ACCOUNT_SID']19@auth_token = ENV['TWILIO_AUTH_TOKEN']20@client = taskrouter_client21end2223def setup24@workspace_sid = create_workspace25@client = taskrouter_client26WorkspaceInfo.instance.workers = create_workers27workflow_sid = create_workflow.sid28WorkspaceInfo.instance.workflow_sid = workflow_sid29idle_activity_sid = activity_by_name('Idle').sid30WorkspaceInfo.instance.post_work_activity_sid = idle_activity_sid31WorkspaceInfo.instance.idle_activity_sid = idle_activity_sid32WorkspaceInfo.instance.offline_activity_sid = activity_by_name('Offline').sid33WorkspaceInfo.instance.workspace_sid = @workspace_sid34end3536private3738attr_reader :client, :account_sid, :auth_token3940def taskrouter_client41client_instance = Twilio::REST::Client.new(42account_sid,43auth_token44)4546client_instance.taskrouter.v147end4849def create_workspace50workspace = client.workspaces.list(friendly_name: WORKSPACE_NAME).first51workspace.delete unless workspace.nil?5253workspace = client.workspaces.create(54friendly_name: WORKSPACE_NAME,55event_callback_url: EVENT_CALLBACK_URL56)5758workspace.sid59end6061def create_workers62bob_attributes = "{\"products\": [\"ProgrammableSMS\"], \"contact_uri\": \"#{BOB_NUMBER}\"}"63alice_attributes = "{\"products\": [\"ProgrammableVoice\"], \"contact_uri\": \"#{ALICE_NUMBER}\"}"6465bob = create_worker('Bob', bob_attributes)66alice = create_worker('Alice', alice_attributes)6768{69BOB_NUMBER => { sid: bob.sid, name: 'Bob' },70ALICE_NUMBER => { sid: alice.sid, name: 'Alice' }71}72end7374def create_worker(name, attributes)75client.workspaces(@workspace_sid).workers.create(76friendly_name: name,77attributes: attributes,78activity_sid: activity_by_name('Idle').sid79)80end8182def activity_by_name(name)83client.workspaces(@workspace_sid).activities.list(friendly_name: name).first84end8586def create_task_queues87reservation_activity_sid = activity_by_name('Reserved').sid88assignment_activity_sid = activity_by_name('Busy').sid8990voice_queue = create_task_queue('Voice', reservation_activity_sid,91assignment_activity_sid,92"products HAS 'ProgrammableVoice'")9394sms_queue = create_task_queue('SMS', reservation_activity_sid,95assignment_activity_sid,96"products HAS 'ProgrammableSMS'")9798all_queue = create_task_queue('All', reservation_activity_sid,99assignment_activity_sid, '1==1')100101{ voice: voice_queue, sms: sms_queue, all: all_queue }102end103104def create_task_queue(name, reservation_sid, assignment_sid, target_workers)105client.workspaces(@workspace_sid).task_queues.create(106friendly_name: name,107reservation_activity_sid: reservation_sid,108assignment_activity_sid: assignment_sid,109target_workers: target_workers110)111end112113def create_workflow114queues = create_task_queues115config = workflow_config(queues)116117client.workspaces(@workspace_sid).workflows.create(118configuration: config.to_json,119friendly_name: WORKFLOW_NAME,120assignment_callback_url: ASSIGNMENT_CALLBACK_URL,121fallback_assignment_callback_url: ASSIGNMENT_CALLBACK_URL,122task_reservation_timeout: WORKFLOW_TIMEOUT123)124end125126def workspace_sid127@workspace_sid || 'no_workspace_yet'128end129130def workflow_config(queues)131default_target = default_rule_target(queues[:all].sid)132133{134task_routing: {135filters: [136{137expression: 'selected_product=="ProgrammableVoice"',138targets: [139rule_target(queues[:voice].sid),140default_target141]142},143{144expression: 'selected_product=="ProgrammableSMS"',145targets: [146rule_target(queues[:sms].sid),147default_target148]149}150],151default_filter: default_target152}153}154end155156def rule_target(sid)157{ queue: sid, priority: 5, timeout: QUEUE_TIMEOUT }158end159160def default_rule_target(sid)161{162queue: sid,163priority: 1,164timeout: QUEUE_TIMEOUT,165expression: '1==1'166}167end168end
We have a Workspace, Workers and Task Queues... what's left? A Workflow. Let's see how to create one next!
Finally, we create the Workflow using the following parameters:
friendly_name
as the name of a Workflow.
assignment_callback_url
and fallback_assignment_callback_url
as the public URL where a request will be made when this Workflow assigns a Task to a Worker. We will learn how to implement it on the next steps.
task_reservation_timeout
as the maximum time we want to wait until a Worker is available for handling a Task.
configuration
which is a set of rules for placing Tasks into Task Queues. The routing configuration will take a Task's attribute and match this with Task Queues. This application's Workflow rules are defined as:
"selected_product==\ "ProgrammableSMS\""
expression for SMS
Task Queue. This expression will match any Task with ProgrammableSMS
as the selected_product
attribute."selected_product==\ "ProgrammableVoice\""
expression for Voice
Task Queue.lib/workspace_config.rb
1class WorkspaceConfig2WORKSPACE_NAME = 'Rails Workspace'.freeze3WORKFLOW_NAME = 'Sales'.freeze4WORKFLOW_TIMEOUT = ENV['WORKFLOW_TIMEOUT'].freeze5QUEUE_TIMEOUT = ENV['QUEUE_TIMEOUT'].freeze6ASSIGNMENT_CALLBACK_URL = ENV['ASSIGNMENT_CALLBACK_URL'].freeze7EVENT_CALLBACK_URL = ENV['EVENT_CALLBACK_URL'].freeze8BOB_NUMBER = ENV['BOB_NUMBER'].freeze9ALICE_NUMBER = ENV['ALICE_NUMBER'].freeze1011def self.setup12puts 'Configuring workspace, please wait ...'13new.setup14puts 'Workspace ready!'15end1617def initialize18@account_sid = ENV['TWILIO_ACCOUNT_SID']19@auth_token = ENV['TWILIO_AUTH_TOKEN']20@client = taskrouter_client21end2223def setup24@workspace_sid = create_workspace25@client = taskrouter_client26WorkspaceInfo.instance.workers = create_workers27workflow_sid = create_workflow.sid28WorkspaceInfo.instance.workflow_sid = workflow_sid29idle_activity_sid = activity_by_name('Idle').sid30WorkspaceInfo.instance.post_work_activity_sid = idle_activity_sid31WorkspaceInfo.instance.idle_activity_sid = idle_activity_sid32WorkspaceInfo.instance.offline_activity_sid = activity_by_name('Offline').sid33WorkspaceInfo.instance.workspace_sid = @workspace_sid34end3536private3738attr_reader :client, :account_sid, :auth_token3940def taskrouter_client41client_instance = Twilio::REST::Client.new(42account_sid,43auth_token44)4546client_instance.taskrouter.v147end4849def create_workspace50workspace = client.workspaces.list(friendly_name: WORKSPACE_NAME).first51workspace.delete unless workspace.nil?5253workspace = client.workspaces.create(54friendly_name: WORKSPACE_NAME,55event_callback_url: EVENT_CALLBACK_URL56)5758workspace.sid59end6061def create_workers62bob_attributes = "{\"products\": [\"ProgrammableSMS\"], \"contact_uri\": \"#{BOB_NUMBER}\"}"63alice_attributes = "{\"products\": [\"ProgrammableVoice\"], \"contact_uri\": \"#{ALICE_NUMBER}\"}"6465bob = create_worker('Bob', bob_attributes)66alice = create_worker('Alice', alice_attributes)6768{69BOB_NUMBER => { sid: bob.sid, name: 'Bob' },70ALICE_NUMBER => { sid: alice.sid, name: 'Alice' }71}72end7374def create_worker(name, attributes)75client.workspaces(@workspace_sid).workers.create(76friendly_name: name,77attributes: attributes,78activity_sid: activity_by_name('Idle').sid79)80end8182def activity_by_name(name)83client.workspaces(@workspace_sid).activities.list(friendly_name: name).first84end8586def create_task_queues87reservation_activity_sid = activity_by_name('Reserved').sid88assignment_activity_sid = activity_by_name('Busy').sid8990voice_queue = create_task_queue('Voice', reservation_activity_sid,91assignment_activity_sid,92"products HAS 'ProgrammableVoice'")9394sms_queue = create_task_queue('SMS', reservation_activity_sid,95assignment_activity_sid,96"products HAS 'ProgrammableSMS'")9798all_queue = create_task_queue('All', reservation_activity_sid,99assignment_activity_sid, '1==1')100101{ voice: voice_queue, sms: sms_queue, all: all_queue }102end103104def create_task_queue(name, reservation_sid, assignment_sid, target_workers)105client.workspaces(@workspace_sid).task_queues.create(106friendly_name: name,107reservation_activity_sid: reservation_sid,108assignment_activity_sid: assignment_sid,109target_workers: target_workers110)111end112113def create_workflow114queues = create_task_queues115config = workflow_config(queues)116117client.workspaces(@workspace_sid).workflows.create(118configuration: config.to_json,119friendly_name: WORKFLOW_NAME,120assignment_callback_url: ASSIGNMENT_CALLBACK_URL,121fallback_assignment_callback_url: ASSIGNMENT_CALLBACK_URL,122task_reservation_timeout: WORKFLOW_TIMEOUT123)124end125126def workspace_sid127@workspace_sid || 'no_workspace_yet'128end129130def workflow_config(queues)131default_target = default_rule_target(queues[:all].sid)132133{134task_routing: {135filters: [136{137expression: 'selected_product=="ProgrammableVoice"',138targets: [139rule_target(queues[:voice].sid),140default_target141]142},143{144expression: 'selected_product=="ProgrammableSMS"',145targets: [146rule_target(queues[:sms].sid),147default_target148]149}150],151default_filter: default_target152}153}154end155156def rule_target(sid)157{ queue: sid, priority: 5, timeout: QUEUE_TIMEOUT }158end159160def default_rule_target(sid)161{162queue: sid,163priority: 1,164timeout: QUEUE_TIMEOUT,165expression: '1==1'166}167end168end
Our workspace is completely setup. Now it's time to see how we use it to route calls.
Right after receiving a call, Twilio will send a request to the URL specified on the number's configuration.
The endpoint will then process the request and generate a TwiML response. We'll use the Say verb to give the user product alternatives, and a key they can press in order to select one. The Gather verb allows us to capture the user's key press.
lib/twiml_generator.rb
1module TwimlGenerator2def self.generate_gather_product(callback_url)3response = Twilio::TwiML::VoiceResponse.new4gather = Twilio::TwiML::Gather.new(num_digits: 1,5action: callback_url,6method: 'POST')7gather.say 'Welcome to the Twilio support line!'8gather.say 'To get specialized help with programmable voice press 1, '\9'or press 2 for programmable SMS'1011response.append(gather)12response.to_s13end1415def self.generate_task_enqueue(selected_product)16enqueue = Twilio::TwiML::Enqueue.new(nil, workflow_sid: WorkspaceInfo.instance.workflow_sid)17enqueue.task "{\"selected_product\": \"#{selected_product}\"}"1819response = Twilio::TwiML::VoiceResponse.new20response.append(enqueue)21response.to_s22end2324def self.generate_confirm_message(status)25response = Twilio::TwiML::MessagingResponse.new26response.message(body: "Your status has changed to #{status}")27response.to_s28end29end
We just asked the caller to choose a product, next we will use their choice to create the appropriate Task.
This is the endpoint set as the action
URL on the Gather
verb on the previous step. A request is made to this endpoint when the user presses a key during the call. This request has a Digits
parameter that holds the pressed keys. A Task
will be created based on the pressed digit with the selected_product
as an attribute. The Workflow will take this Task's attributes and match them with the configured expressions in order to find a Task Queue for this Task, so an appropriate available Worker can be assigned to handle it.
We use the Enqueue
verb with a WorkflowSid
attribute to integrate with TaskRouter. Then the voice call will be put on hold while TaskRouter tries to find an available Worker to handle this Task.
lib/twiml_generator.rb
1module TwimlGenerator2def self.generate_gather_product(callback_url)3response = Twilio::TwiML::VoiceResponse.new4gather = Twilio::TwiML::Gather.new(num_digits: 1,5action: callback_url,6method: 'POST')7gather.say 'Welcome to the Twilio support line!'8gather.say 'To get specialized help with programmable voice press 1, '\9'or press 2 for programmable SMS'1011response.append(gather)12response.to_s13end1415def self.generate_task_enqueue(selected_product)16enqueue = Twilio::TwiML::Enqueue.new(nil, workflow_sid: WorkspaceInfo.instance.workflow_sid)17enqueue.task "{\"selected_product\": \"#{selected_product}\"}"1819response = Twilio::TwiML::VoiceResponse.new20response.append(enqueue)21response.to_s22end2324def self.generate_confirm_message(status)25response = Twilio::TwiML::MessagingResponse.new26response.message(body: "Your status has changed to #{status}")27response.to_s28end29end
After sending a Task to Twilio, let's see how we tell TaskRouter which Worker to use to execute that task.
When TaskRouter selects a Worker, it does the following:
POST
request is made to the Workflow's AssignmentCallbackURL, which was configured using the WorkspaceConfig class when the application is initialized. This request includes the full details of the Task, the selected Worker, and the Reservation.Handling this Assignment Callback is a key component of building a TaskRouter application as we can instruct how the Worker will handle a Task. We could send a text, email, push notifications or make a call.
Since we created this Task during a voice call with an Enqueue
verb, let's instruct TaskRouter to dequeue the call and dial a Worker. If we do not specify a to
parameter with a phone number, TaskRouter will pick the Worker's contact_uri
attribute.
We also send a post_work_activity_sid
which will tell TaskRouter which Activity to assign this worker after the call ends.
app/controllers/callback_controller.rb
1class CallbackController < ApplicationController2skip_before_filter :verify_authenticity_token34def assignment5instruction = {6instruction: 'dequeue',7post_work_activity_sid: WorkspaceInfo.instance.post_work_activity_sid8}910render json: instruction11end1213def events14event_type = params[:EventType]1516if ['workflow.timeout', 'task.canceled'].include?(event_type)17task_attributes = JSON.parse(params[:TaskAttributes])1819MissedCall.create(20selected_product: task_attributes['selected_product'],21phone_number: task_attributes['from']22)2324redirect_to_voicemail(task_attributes['call_sid']) if event_type == 'workflow.timeout'25elsif event_type == 'worker.activity.update' &&26params[:WorkerActivityName] == 'Offline'2728worker_attributes = JSON.parse(params[:WorkerAttributes])29notify_offline_status(worker_attributes['contact_uri'])30end3132render nothing: true33end3435private3637def redirect_to_voicemail(call_sid)38email = ENV['MISSED_CALLS_EMAIL_ADDRESS']39message = 'Sorry, All agents are busy. Please leave a message. We\'ll call you as soon as possible'40url_message = { Message: message }.to_query41redirect_url =42"http://twimlets.com/voicemail?Email=#{email}&#{url_message}"4344client.calls(call_sid).update(url: redirect_url)45end4647def notify_offline_status(phone_number)48message = 'Your status has changed to Offline. Reply with '\49'"On" to get back Online'50client.messages.create(51to: phone_number,52from: ENV['TWILIO_NUMBER'],53body: message54)55end5657def client58Twilio::REST::Client.new(ENV['TWILIO_ACCOUNT_SID'], ENV['TWILIO_AUTH_TOKEN'])59end60end
Now that our Tasks are routed properly, let's deal with missed calls in the next step.
This endpoint will be called after each TaskRouter Event is triggered. In our application, we are trying to collect missed calls, so we would like to handle the workflow.timeout
event. This event is triggered when the Task waits more than the limit set on Workflow Configuration-- or rather when no worker is available.
Here we use TwilioRestClient to route this call to a Voicemail Twimlet. Twimlets are tiny web applications for voice. This one will generate a TwiML
response using Say
verb and record a message using Record
verb. The recorded message will then be transcribed and sent to the email address configured.
Note that we are also listening for task.canceled
. This is triggered when the customer hangs up before being assigned to an agent, therefore canceling the task. Capturing this event allows us to collect the information from the customers that hang up before the Workflow times out.
app/controllers/callback_controller.rb
1class CallbackController < ApplicationController2skip_before_filter :verify_authenticity_token34def assignment5instruction = {6instruction: 'dequeue',7post_work_activity_sid: WorkspaceInfo.instance.post_work_activity_sid8}910render json: instruction11end1213def events14event_type = params[:EventType]1516if ['workflow.timeout', 'task.canceled'].include?(event_type)17task_attributes = JSON.parse(params[:TaskAttributes])1819MissedCall.create(20selected_product: task_attributes['selected_product'],21phone_number: task_attributes['from']22)2324redirect_to_voicemail(task_attributes['call_sid']) if event_type == 'workflow.timeout'25elsif event_type == 'worker.activity.update' &&26params[:WorkerActivityName] == 'Offline'2728worker_attributes = JSON.parse(params[:WorkerAttributes])29notify_offline_status(worker_attributes['contact_uri'])30end3132render nothing: true33end3435private3637def redirect_to_voicemail(call_sid)38email = ENV['MISSED_CALLS_EMAIL_ADDRESS']39message = 'Sorry, All agents are busy. Please leave a message. We\'ll call you as soon as possible'40url_message = { Message: message }.to_query41redirect_url =42"http://twimlets.com/voicemail?Email=#{email}&#{url_message}"4344client.calls(call_sid).update(url: redirect_url)45end4647def notify_offline_status(phone_number)48message = 'Your status has changed to Offline. Reply with '\49'"On" to get back Online'50client.messages.create(51to: phone_number,52from: ENV['TWILIO_NUMBER'],53body: message54)55end5657def client58Twilio::REST::Client.new(ENV['TWILIO_ACCOUNT_SID'], ENV['TWILIO_AUTH_TOKEN'])59end60end
See how to allow Workers change their availability status
We have created this endpoint, so a worker can send an SMS message to the support line with the command "On" or "Off" to change their availability status.
This is important as a worker's activity will change to Offline
when they miss a call. When this happens, they receive an SMS letting them know that their activity has changed, and that they can reply with the On
command to make themselves available for incoming calls again.
app/controllers/message_controller.rb
1class MessageController < ApplicationController2skip_before_filter :verify_authenticity_token34def incoming5command = params['Body'].downcase6from_number = params['From']78if command == 'off'9status = 'Offline'10activity_sid = WorkspaceInfo.instance.offline_activity_sid11else12status = 'Idle'13activity_sid = WorkspaceInfo.instance.idle_activity_sid14end1516worker_sid = WorkspaceInfo.instance.workers[from_number][:sid]17client18.workspaces(WorkspaceInfo.instance.workspace_sid)19.workers(worker_sid)20.fetch21.update(activity_sid: activity_sid)2223render xml: TwimlGenerator.generate_confirm_message(status)24end2526private2728def client29client_instance = Twilio::REST::Client.new(30ENV['TWILIO_ACCOUNT_SID'],31ENV['TWILIO_AUTH_TOKEN']32)3334client_instance.taskrouter.v135end36end
Congratulations! You finished this tutorial. As you can see, using Twilio's TaskRouter is quite simple.
If you're a Ruby developer working with Twilio, you might also enjoy these tutorials:
Voice JavaScript SDK Quickstart
Learn how to use Twilio JavaScript Voice SDK to make browser-to-phone and browser-to-browser calls with ease.
Learn how to implement ETA Notifications using Ruby on Rails and Twilio.