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 ASP.NET MVC application this step will be executed in the Application_Start
event 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-csharp helper library to create and configure the workspace.
TaskRouter.Web/App_Start/WorkspaceConfig.cs
1using System;2using System.Collections.Generic;3using System.Linq;4using System.Web.Helpers;5using TaskRouter.Web.Infrastructure;6using Twilio;7using Twilio.Rest.Taskrouter.V1;8using Twilio.Rest.Taskrouter.V1.Workspace;910namespace TaskRouter.Web11{12public class WorkspaceConfig13{14private readonly Config _config;1516private const string VoiceQueue = "VoiceQueue";17private const string SmsQueue = "SMSQueue";18private const string AllQueue = "AllQueue";1920public static void RegisterWorkspace()21{22new WorkspaceConfig().Register();23}2425public WorkspaceConfig():this(new Config())26{27}2829public WorkspaceConfig(Config config)30{31TwilioClient.Init(config.AccountSID, config.AuthToken);32_config = config;3334}3536public WorkspaceConfig(Type workspaceResource):this()37{38}3940public virtual ActivityResource GetActivityByFriendlyName(string workspaceSid, string friendlyName)41{42return ActivityResource.Read(workspaceSid, friendlyName).First();43}4445public virtual ActivityResource CreateActivityWithFriendlyName(string workspaceSid, string friendlyName)46{47return ActivityResource.Create(workspaceSid, friendlyName);48}4950public virtual WorkspaceResource GetWorkspaceByFriendlyName(string friendlyName)51{52return WorkspaceResource.Read(friendlyName).FirstOrDefault();53}5455public virtual WorkspaceResource CreateWorkspace(string friendlyName, Uri eventCallbackUrl)56{57return WorkspaceResource.Create(friendlyName, eventCallbackUrl);58}5960public virtual bool DeleteWorkspace(string workspaceSid)61{62return WorkspaceResource.Delete(workspaceSid);63}6465public virtual WorkerResource CreateWorker(string workspaceSid, string bob, string activitySid, string attributes)66{67return WorkerResource.Create(workspaceSid, bob, activitySid, attributes);68}6970public void Register()71{72var workspace = DeleteAndCreateWorkspace(73"Twilio Workspace", new Uri(new Uri(_config.HostUrl), "/callback/events").AbsoluteUri);74var workspaceSid = workspace.Sid;7576var assignmentActivity = GetActivityByFriendlyName(workspaceSid, "Unavailable");77var idleActivity = GetActivityByFriendlyName(workspaceSid, "Available");78var reservationActivity = CreateActivityWithFriendlyName(workspaceSid, "Reserved");79var offlineActivity = GetActivityByFriendlyName(workspaceSid, "Offline");8081var workers = CreateWorkers(workspaceSid, idleActivity);82var taskQueues = CreateTaskQueues(workspaceSid, assignmentActivity, reservationActivity);83var workflow = CreateWorkflow(workspaceSid, taskQueues);8485Singleton.Instance.WorkspaceSid = workspaceSid;86Singleton.Instance.WorkflowSid = workflow.Sid;87Singleton.Instance.Workers = workers;88Singleton.Instance.PostWorkActivitySid = idleActivity.Sid;89Singleton.Instance.IdleActivitySid = idleActivity.Sid;90Singleton.Instance.OfflineActivitySid = offlineActivity.Sid;91}9293public virtual WorkspaceResource DeleteAndCreateWorkspace(string friendlyName, string eventCallbackUrl) {94var workspace = GetWorkspaceByFriendlyName(friendlyName);95if (workspace != null)96{97DeleteWorkspace(workspace.Sid);98}99100return CreateWorkspace(friendlyName, new Uri(eventCallbackUrl));101}102103private IDictionary<string, string> CreateWorkers(string workspaceSid, ActivityResource activity)104{105var attributesForBob = new106{107products = new List<object>()108{109"ProgrammableSMS"110},111contact_uri = _config.AgentForProgrammableSMS112};113114var bobWorker = CreateWorker(workspaceSid, "Bob", activity.Sid, Json.Encode(attributesForBob));115116var attributesForAlice = new117{118products = new List<object>()119{120"ProgrammableVoice"121},122contact_uri = _config.AgentForProgrammableVoice123};124125var alice = CreateWorker(workspaceSid, "Alice", activity.Sid, Json.Encode(attributesForAlice));126127return new Dictionary<string, string>128{129{ _config.AgentForProgrammableSMS, bobWorker.Sid },130{ _config.AgentForProgrammableVoice, alice.Sid },131};132}133134public virtual TaskQueueResource CreateTaskQueue(135string workspaceSid, string friendlyName,136string assignmentActivitySid, string reservationActivitySid, string targetWorkers)137{138var queue = TaskQueueResource.Create(139workspaceSid,140friendlyName: friendlyName,141assignmentActivitySid: assignmentActivitySid,142reservationActivitySid: reservationActivitySid143);144145TaskQueueResource.Update(146workspaceSid,147queue.Sid,148friendlyName,149targetWorkers,150assignmentActivitySid,151reservationActivitySid,1521);153154return queue;155}156157private IDictionary<string, TaskQueueResource> CreateTaskQueues(158string workspaceSid, ActivityResource assignmentActivity, ActivityResource reservationActivity)159{160161var voiceQueue = CreateTaskQueue(162workspaceSid, "Voice",163assignmentActivity.Sid, reservationActivity.Sid, "products HAS 'ProgrammableVoice'");164165var smsQueue = CreateTaskQueue(166workspaceSid, "SMS",167assignmentActivity.Sid, reservationActivity.Sid, "products HAS 'ProgrammableSMS'");168169var allQueue = CreateTaskQueue(170workspaceSid, "All",171assignmentActivity.Sid, reservationActivity.Sid, "1 == 1");172173return new Dictionary<string, TaskQueueResource> {174{ VoiceQueue, voiceQueue },175{ SmsQueue, smsQueue },176{ AllQueue, allQueue }177};178}179180public virtual WorkflowResource CreateWorkflow(string workspaceSid, IDictionary<string, TaskQueueResource> taskQueues)181{182var voiceQueue = taskQueues[VoiceQueue];183var smsQueue = taskQueues[SmsQueue];184var allQueue = taskQueues[AllQueue];185186var voiceFilter = new {187friendlyName = "Voice",188expression = "selected_product==\"ProgrammableVoice\"",189targets = new List<object>() {190new { queue = voiceQueue.Sid, Priority = "5", Timeout = "30" },191new { queue = allQueue.Sid, Expression = "1==1", Priority = "1", Timeout = "30" }192}193};194195var smsFilter = new {196friendlyName = "SMS",197expression = "selected_product==\"ProgrammableSMS\"",198targets = new List<object>() {199new { queue = smsQueue.Sid, Priority = "5", Timeout = "30" },200new { queue = allQueue.Sid, Expression = "1==1", Priority = "1", Timeout = "30" }201}202};203204var workflowConfiguration = new205{206task_routing = new207{208filters = new List<object>()209{210voiceFilter,211smsFilter212},213default_filter = new214{215queue = allQueue.Sid,216expression = "1==1",217priority = "1",218timeout = "30"219}220}221};222223// Call REST API224return WorkflowResource.Create(225workspaceSid,226"Tech Support",227Json.Encode(workflowConfiguration),228new Uri($"{_config.HostUrl}/callback/assignment"),229new Uri($"{_config.HostUrl}/callback/assignment"),23015);231}232}233}
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 friendlyName
as the one we are trying to create. In order to create a workspace we need to provide a friendlyName
and a eventCallbackUrl
where a requests will be made every time an event is triggered in our workspace.
TaskRouter.Web/App_Start/WorkspaceConfig.cs
1using System;2using System.Collections.Generic;3using System.Linq;4using System.Web.Helpers;5using TaskRouter.Web.Infrastructure;6using Twilio;7using Twilio.Rest.Taskrouter.V1;8using Twilio.Rest.Taskrouter.V1.Workspace;910namespace TaskRouter.Web11{12public class WorkspaceConfig13{14private readonly Config _config;1516private const string VoiceQueue = "VoiceQueue";17private const string SmsQueue = "SMSQueue";18private const string AllQueue = "AllQueue";1920public static void RegisterWorkspace()21{22new WorkspaceConfig().Register();23}2425public WorkspaceConfig():this(new Config())26{27}2829public WorkspaceConfig(Config config)30{31TwilioClient.Init(config.AccountSID, config.AuthToken);32_config = config;3334}3536public WorkspaceConfig(Type workspaceResource):this()37{38}3940public virtual ActivityResource GetActivityByFriendlyName(string workspaceSid, string friendlyName)41{42return ActivityResource.Read(workspaceSid, friendlyName).First();43}4445public virtual ActivityResource CreateActivityWithFriendlyName(string workspaceSid, string friendlyName)46{47return ActivityResource.Create(workspaceSid, friendlyName);48}4950public virtual WorkspaceResource GetWorkspaceByFriendlyName(string friendlyName)51{52return WorkspaceResource.Read(friendlyName).FirstOrDefault();53}5455public virtual WorkspaceResource CreateWorkspace(string friendlyName, Uri eventCallbackUrl)56{57return WorkspaceResource.Create(friendlyName, eventCallbackUrl);58}5960public virtual bool DeleteWorkspace(string workspaceSid)61{62return WorkspaceResource.Delete(workspaceSid);63}6465public virtual WorkerResource CreateWorker(string workspaceSid, string bob, string activitySid, string attributes)66{67return WorkerResource.Create(workspaceSid, bob, activitySid, attributes);68}6970public void Register()71{72var workspace = DeleteAndCreateWorkspace(73"Twilio Workspace", new Uri(new Uri(_config.HostUrl), "/callback/events").AbsoluteUri);74var workspaceSid = workspace.Sid;7576var assignmentActivity = GetActivityByFriendlyName(workspaceSid, "Unavailable");77var idleActivity = GetActivityByFriendlyName(workspaceSid, "Available");78var reservationActivity = CreateActivityWithFriendlyName(workspaceSid, "Reserved");79var offlineActivity = GetActivityByFriendlyName(workspaceSid, "Offline");8081var workers = CreateWorkers(workspaceSid, idleActivity);82var taskQueues = CreateTaskQueues(workspaceSid, assignmentActivity, reservationActivity);83var workflow = CreateWorkflow(workspaceSid, taskQueues);8485Singleton.Instance.WorkspaceSid = workspaceSid;86Singleton.Instance.WorkflowSid = workflow.Sid;87Singleton.Instance.Workers = workers;88Singleton.Instance.PostWorkActivitySid = idleActivity.Sid;89Singleton.Instance.IdleActivitySid = idleActivity.Sid;90Singleton.Instance.OfflineActivitySid = offlineActivity.Sid;91}9293public virtual WorkspaceResource DeleteAndCreateWorkspace(string friendlyName, string eventCallbackUrl) {94var workspace = GetWorkspaceByFriendlyName(friendlyName);95if (workspace != null)96{97DeleteWorkspace(workspace.Sid);98}99100return CreateWorkspace(friendlyName, new Uri(eventCallbackUrl));101}102103private IDictionary<string, string> CreateWorkers(string workspaceSid, ActivityResource activity)104{105var attributesForBob = new106{107products = new List<object>()108{109"ProgrammableSMS"110},111contact_uri = _config.AgentForProgrammableSMS112};113114var bobWorker = CreateWorker(workspaceSid, "Bob", activity.Sid, Json.Encode(attributesForBob));115116var attributesForAlice = new117{118products = new List<object>()119{120"ProgrammableVoice"121},122contact_uri = _config.AgentForProgrammableVoice123};124125var alice = CreateWorker(workspaceSid, "Alice", activity.Sid, Json.Encode(attributesForAlice));126127return new Dictionary<string, string>128{129{ _config.AgentForProgrammableSMS, bobWorker.Sid },130{ _config.AgentForProgrammableVoice, alice.Sid },131};132}133134public virtual TaskQueueResource CreateTaskQueue(135string workspaceSid, string friendlyName,136string assignmentActivitySid, string reservationActivitySid, string targetWorkers)137{138var queue = TaskQueueResource.Create(139workspaceSid,140friendlyName: friendlyName,141assignmentActivitySid: assignmentActivitySid,142reservationActivitySid: reservationActivitySid143);144145TaskQueueResource.Update(146workspaceSid,147queue.Sid,148friendlyName,149targetWorkers,150assignmentActivitySid,151reservationActivitySid,1521);153154return queue;155}156157private IDictionary<string, TaskQueueResource> CreateTaskQueues(158string workspaceSid, ActivityResource assignmentActivity, ActivityResource reservationActivity)159{160161var voiceQueue = CreateTaskQueue(162workspaceSid, "Voice",163assignmentActivity.Sid, reservationActivity.Sid, "products HAS 'ProgrammableVoice'");164165var smsQueue = CreateTaskQueue(166workspaceSid, "SMS",167assignmentActivity.Sid, reservationActivity.Sid, "products HAS 'ProgrammableSMS'");168169var allQueue = CreateTaskQueue(170workspaceSid, "All",171assignmentActivity.Sid, reservationActivity.Sid, "1 == 1");172173return new Dictionary<string, TaskQueueResource> {174{ VoiceQueue, voiceQueue },175{ SmsQueue, smsQueue },176{ AllQueue, allQueue }177};178}179180public virtual WorkflowResource CreateWorkflow(string workspaceSid, IDictionary<string, TaskQueueResource> taskQueues)181{182var voiceQueue = taskQueues[VoiceQueue];183var smsQueue = taskQueues[SmsQueue];184var allQueue = taskQueues[AllQueue];185186var voiceFilter = new {187friendlyName = "Voice",188expression = "selected_product==\"ProgrammableVoice\"",189targets = new List<object>() {190new { queue = voiceQueue.Sid, Priority = "5", Timeout = "30" },191new { queue = allQueue.Sid, Expression = "1==1", Priority = "1", Timeout = "30" }192}193};194195var smsFilter = new {196friendlyName = "SMS",197expression = "selected_product==\"ProgrammableSMS\"",198targets = new List<object>() {199new { queue = smsQueue.Sid, Priority = "5", Timeout = "30" },200new { queue = allQueue.Sid, Expression = "1==1", Priority = "1", Timeout = "30" }201}202};203204var workflowConfiguration = new205{206task_routing = new207{208filters = new List<object>()209{210voiceFilter,211smsFilter212},213default_filter = new214{215queue = allQueue.Sid,216expression = "1==1",217priority = "1",218timeout = "30"219}220}221};222223// Call REST API224return WorkflowResource.Create(225workspaceSid,226"Tech Support",227Json.Encode(workflowConfiguration),228new Uri($"{_config.HostUrl}/callback/assignment"),229new Uri($"{_config.HostUrl}/callback/assignment"),23015);231}232}233}
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.
TaskRouter.Web/App_Start/WorkspaceConfig.cs
1using System;2using System.Collections.Generic;3using System.Linq;4using System.Web.Helpers;5using TaskRouter.Web.Infrastructure;6using Twilio;7using Twilio.Rest.Taskrouter.V1;8using Twilio.Rest.Taskrouter.V1.Workspace;910namespace TaskRouter.Web11{12public class WorkspaceConfig13{14private readonly Config _config;1516private const string VoiceQueue = "VoiceQueue";17private const string SmsQueue = "SMSQueue";18private const string AllQueue = "AllQueue";1920public static void RegisterWorkspace()21{22new WorkspaceConfig().Register();23}2425public WorkspaceConfig():this(new Config())26{27}2829public WorkspaceConfig(Config config)30{31TwilioClient.Init(config.AccountSID, config.AuthToken);32_config = config;3334}3536public WorkspaceConfig(Type workspaceResource):this()37{38}3940public virtual ActivityResource GetActivityByFriendlyName(string workspaceSid, string friendlyName)41{42return ActivityResource.Read(workspaceSid, friendlyName).First();43}4445public virtual ActivityResource CreateActivityWithFriendlyName(string workspaceSid, string friendlyName)46{47return ActivityResource.Create(workspaceSid, friendlyName);48}4950public virtual WorkspaceResource GetWorkspaceByFriendlyName(string friendlyName)51{52return WorkspaceResource.Read(friendlyName).FirstOrDefault();53}5455public virtual WorkspaceResource CreateWorkspace(string friendlyName, Uri eventCallbackUrl)56{57return WorkspaceResource.Create(friendlyName, eventCallbackUrl);58}5960public virtual bool DeleteWorkspace(string workspaceSid)61{62return WorkspaceResource.Delete(workspaceSid);63}6465public virtual WorkerResource CreateWorker(string workspaceSid, string bob, string activitySid, string attributes)66{67return WorkerResource.Create(workspaceSid, bob, activitySid, attributes);68}6970public void Register()71{72var workspace = DeleteAndCreateWorkspace(73"Twilio Workspace", new Uri(new Uri(_config.HostUrl), "/callback/events").AbsoluteUri);74var workspaceSid = workspace.Sid;7576var assignmentActivity = GetActivityByFriendlyName(workspaceSid, "Unavailable");77var idleActivity = GetActivityByFriendlyName(workspaceSid, "Available");78var reservationActivity = CreateActivityWithFriendlyName(workspaceSid, "Reserved");79var offlineActivity = GetActivityByFriendlyName(workspaceSid, "Offline");8081var workers = CreateWorkers(workspaceSid, idleActivity);82var taskQueues = CreateTaskQueues(workspaceSid, assignmentActivity, reservationActivity);83var workflow = CreateWorkflow(workspaceSid, taskQueues);8485Singleton.Instance.WorkspaceSid = workspaceSid;86Singleton.Instance.WorkflowSid = workflow.Sid;87Singleton.Instance.Workers = workers;88Singleton.Instance.PostWorkActivitySid = idleActivity.Sid;89Singleton.Instance.IdleActivitySid = idleActivity.Sid;90Singleton.Instance.OfflineActivitySid = offlineActivity.Sid;91}9293public virtual WorkspaceResource DeleteAndCreateWorkspace(string friendlyName, string eventCallbackUrl) {94var workspace = GetWorkspaceByFriendlyName(friendlyName);95if (workspace != null)96{97DeleteWorkspace(workspace.Sid);98}99100return CreateWorkspace(friendlyName, new Uri(eventCallbackUrl));101}102103private IDictionary<string, string> CreateWorkers(string workspaceSid, ActivityResource activity)104{105var attributesForBob = new106{107products = new List<object>()108{109"ProgrammableSMS"110},111contact_uri = _config.AgentForProgrammableSMS112};113114var bobWorker = CreateWorker(workspaceSid, "Bob", activity.Sid, Json.Encode(attributesForBob));115116var attributesForAlice = new117{118products = new List<object>()119{120"ProgrammableVoice"121},122contact_uri = _config.AgentForProgrammableVoice123};124125var alice = CreateWorker(workspaceSid, "Alice", activity.Sid, Json.Encode(attributesForAlice));126127return new Dictionary<string, string>128{129{ _config.AgentForProgrammableSMS, bobWorker.Sid },130{ _config.AgentForProgrammableVoice, alice.Sid },131};132}133134public virtual TaskQueueResource CreateTaskQueue(135string workspaceSid, string friendlyName,136string assignmentActivitySid, string reservationActivitySid, string targetWorkers)137{138var queue = TaskQueueResource.Create(139workspaceSid,140friendlyName: friendlyName,141assignmentActivitySid: assignmentActivitySid,142reservationActivitySid: reservationActivitySid143);144145TaskQueueResource.Update(146workspaceSid,147queue.Sid,148friendlyName,149targetWorkers,150assignmentActivitySid,151reservationActivitySid,1521);153154return queue;155}156157private IDictionary<string, TaskQueueResource> CreateTaskQueues(158string workspaceSid, ActivityResource assignmentActivity, ActivityResource reservationActivity)159{160161var voiceQueue = CreateTaskQueue(162workspaceSid, "Voice",163assignmentActivity.Sid, reservationActivity.Sid, "products HAS 'ProgrammableVoice'");164165var smsQueue = CreateTaskQueue(166workspaceSid, "SMS",167assignmentActivity.Sid, reservationActivity.Sid, "products HAS 'ProgrammableSMS'");168169var allQueue = CreateTaskQueue(170workspaceSid, "All",171assignmentActivity.Sid, reservationActivity.Sid, "1 == 1");172173return new Dictionary<string, TaskQueueResource> {174{ VoiceQueue, voiceQueue },175{ SmsQueue, smsQueue },176{ AllQueue, allQueue }177};178}179180public virtual WorkflowResource CreateWorkflow(string workspaceSid, IDictionary<string, TaskQueueResource> taskQueues)181{182var voiceQueue = taskQueues[VoiceQueue];183var smsQueue = taskQueues[SmsQueue];184var allQueue = taskQueues[AllQueue];185186var voiceFilter = new {187friendlyName = "Voice",188expression = "selected_product==\"ProgrammableVoice\"",189targets = new List<object>() {190new { queue = voiceQueue.Sid, Priority = "5", Timeout = "30" },191new { queue = allQueue.Sid, Expression = "1==1", Priority = "1", Timeout = "30" }192}193};194195var smsFilter = new {196friendlyName = "SMS",197expression = "selected_product==\"ProgrammableSMS\"",198targets = new List<object>() {199new { queue = smsQueue.Sid, Priority = "5", Timeout = "30" },200new { queue = allQueue.Sid, Expression = "1==1", Priority = "1", Timeout = "30" }201}202};203204var workflowConfiguration = new205{206task_routing = new207{208filters = new List<object>()209{210voiceFilter,211smsFilter212},213default_filter = new214{215queue = allQueue.Sid,216expression = "1==1",217priority = "1",218timeout = "30"219}220}221};222223// Call REST API224return WorkflowResource.Create(225workspaceSid,226"Tech Support",227Json.Encode(workflowConfiguration),228new Uri($"{_config.HostUrl}/callback/assignment"),229new Uri($"{_config.HostUrl}/callback/assignment"),23015);231}232}233}
After creating our workers, let's set up the Task Queues.
Next, we set up the Task Queues. Each with a friendlyName
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\""
.
TaskRouter.Web/App_Start/WorkspaceConfig.cs
1using System;2using System.Collections.Generic;3using System.Linq;4using System.Web.Helpers;5using TaskRouter.Web.Infrastructure;6using Twilio;7using Twilio.Rest.Taskrouter.V1;8using Twilio.Rest.Taskrouter.V1.Workspace;910namespace TaskRouter.Web11{12public class WorkspaceConfig13{14private readonly Config _config;1516private const string VoiceQueue = "VoiceQueue";17private const string SmsQueue = "SMSQueue";18private const string AllQueue = "AllQueue";1920public static void RegisterWorkspace()21{22new WorkspaceConfig().Register();23}2425public WorkspaceConfig():this(new Config())26{27}2829public WorkspaceConfig(Config config)30{31TwilioClient.Init(config.AccountSID, config.AuthToken);32_config = config;3334}3536public WorkspaceConfig(Type workspaceResource):this()37{38}3940public virtual ActivityResource GetActivityByFriendlyName(string workspaceSid, string friendlyName)41{42return ActivityResource.Read(workspaceSid, friendlyName).First();43}4445public virtual ActivityResource CreateActivityWithFriendlyName(string workspaceSid, string friendlyName)46{47return ActivityResource.Create(workspaceSid, friendlyName);48}4950public virtual WorkspaceResource GetWorkspaceByFriendlyName(string friendlyName)51{52return WorkspaceResource.Read(friendlyName).FirstOrDefault();53}5455public virtual WorkspaceResource CreateWorkspace(string friendlyName, Uri eventCallbackUrl)56{57return WorkspaceResource.Create(friendlyName, eventCallbackUrl);58}5960public virtual bool DeleteWorkspace(string workspaceSid)61{62return WorkspaceResource.Delete(workspaceSid);63}6465public virtual WorkerResource CreateWorker(string workspaceSid, string bob, string activitySid, string attributes)66{67return WorkerResource.Create(workspaceSid, bob, activitySid, attributes);68}6970public void Register()71{72var workspace = DeleteAndCreateWorkspace(73"Twilio Workspace", new Uri(new Uri(_config.HostUrl), "/callback/events").AbsoluteUri);74var workspaceSid = workspace.Sid;7576var assignmentActivity = GetActivityByFriendlyName(workspaceSid, "Unavailable");77var idleActivity = GetActivityByFriendlyName(workspaceSid, "Available");78var reservationActivity = CreateActivityWithFriendlyName(workspaceSid, "Reserved");79var offlineActivity = GetActivityByFriendlyName(workspaceSid, "Offline");8081var workers = CreateWorkers(workspaceSid, idleActivity);82var taskQueues = CreateTaskQueues(workspaceSid, assignmentActivity, reservationActivity);83var workflow = CreateWorkflow(workspaceSid, taskQueues);8485Singleton.Instance.WorkspaceSid = workspaceSid;86Singleton.Instance.WorkflowSid = workflow.Sid;87Singleton.Instance.Workers = workers;88Singleton.Instance.PostWorkActivitySid = idleActivity.Sid;89Singleton.Instance.IdleActivitySid = idleActivity.Sid;90Singleton.Instance.OfflineActivitySid = offlineActivity.Sid;91}9293public virtual WorkspaceResource DeleteAndCreateWorkspace(string friendlyName, string eventCallbackUrl) {94var workspace = GetWorkspaceByFriendlyName(friendlyName);95if (workspace != null)96{97DeleteWorkspace(workspace.Sid);98}99100return CreateWorkspace(friendlyName, new Uri(eventCallbackUrl));101}102103private IDictionary<string, string> CreateWorkers(string workspaceSid, ActivityResource activity)104{105var attributesForBob = new106{107products = new List<object>()108{109"ProgrammableSMS"110},111contact_uri = _config.AgentForProgrammableSMS112};113114var bobWorker = CreateWorker(workspaceSid, "Bob", activity.Sid, Json.Encode(attributesForBob));115116var attributesForAlice = new117{118products = new List<object>()119{120"ProgrammableVoice"121},122contact_uri = _config.AgentForProgrammableVoice123};124125var alice = CreateWorker(workspaceSid, "Alice", activity.Sid, Json.Encode(attributesForAlice));126127return new Dictionary<string, string>128{129{ _config.AgentForProgrammableSMS, bobWorker.Sid },130{ _config.AgentForProgrammableVoice, alice.Sid },131};132}133134public virtual TaskQueueResource CreateTaskQueue(135string workspaceSid, string friendlyName,136string assignmentActivitySid, string reservationActivitySid, string targetWorkers)137{138var queue = TaskQueueResource.Create(139workspaceSid,140friendlyName: friendlyName,141assignmentActivitySid: assignmentActivitySid,142reservationActivitySid: reservationActivitySid143);144145TaskQueueResource.Update(146workspaceSid,147queue.Sid,148friendlyName,149targetWorkers,150assignmentActivitySid,151reservationActivitySid,1521);153154return queue;155}156157private IDictionary<string, TaskQueueResource> CreateTaskQueues(158string workspaceSid, ActivityResource assignmentActivity, ActivityResource reservationActivity)159{160161var voiceQueue = CreateTaskQueue(162workspaceSid, "Voice",163assignmentActivity.Sid, reservationActivity.Sid, "products HAS 'ProgrammableVoice'");164165var smsQueue = CreateTaskQueue(166workspaceSid, "SMS",167assignmentActivity.Sid, reservationActivity.Sid, "products HAS 'ProgrammableSMS'");168169var allQueue = CreateTaskQueue(170workspaceSid, "All",171assignmentActivity.Sid, reservationActivity.Sid, "1 == 1");172173return new Dictionary<string, TaskQueueResource> {174{ VoiceQueue, voiceQueue },175{ SmsQueue, smsQueue },176{ AllQueue, allQueue }177};178}179180public virtual WorkflowResource CreateWorkflow(string workspaceSid, IDictionary<string, TaskQueueResource> taskQueues)181{182var voiceQueue = taskQueues[VoiceQueue];183var smsQueue = taskQueues[SmsQueue];184var allQueue = taskQueues[AllQueue];185186var voiceFilter = new {187friendlyName = "Voice",188expression = "selected_product==\"ProgrammableVoice\"",189targets = new List<object>() {190new { queue = voiceQueue.Sid, Priority = "5", Timeout = "30" },191new { queue = allQueue.Sid, Expression = "1==1", Priority = "1", Timeout = "30" }192}193};194195var smsFilter = new {196friendlyName = "SMS",197expression = "selected_product==\"ProgrammableSMS\"",198targets = new List<object>() {199new { queue = smsQueue.Sid, Priority = "5", Timeout = "30" },200new { queue = allQueue.Sid, Expression = "1==1", Priority = "1", Timeout = "30" }201}202};203204var workflowConfiguration = new205{206task_routing = new207{208filters = new List<object>()209{210voiceFilter,211smsFilter212},213default_filter = new214{215queue = allQueue.Sid,216expression = "1==1",217priority = "1",218timeout = "30"219}220}221};222223// Call REST API224return WorkflowResource.Create(225workspaceSid,226"Tech Support",227Json.Encode(workflowConfiguration),228new Uri($"{_config.HostUrl}/callback/assignment"),229new Uri($"{_config.HostUrl}/callback/assignment"),23015);231}232}233}
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:
friendlyName
as the name of a Workflow.
assignmentCallbackUrl
and
fallbackAssignmentCallbackUrl
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.
Timeout
as the maximum time we want to wait until a Worker is available for handling a Task.
workflowConfiguration
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.
TaskRouter.Web/App_Start/WorkspaceConfig.cs
1using System;2using System.Collections.Generic;3using System.Linq;4using System.Web.Helpers;5using TaskRouter.Web.Infrastructure;6using Twilio;7using Twilio.Rest.Taskrouter.V1;8using Twilio.Rest.Taskrouter.V1.Workspace;910namespace TaskRouter.Web11{12public class WorkspaceConfig13{14private readonly Config _config;1516private const string VoiceQueue = "VoiceQueue";17private const string SmsQueue = "SMSQueue";18private const string AllQueue = "AllQueue";1920public static void RegisterWorkspace()21{22new WorkspaceConfig().Register();23}2425public WorkspaceConfig():this(new Config())26{27}2829public WorkspaceConfig(Config config)30{31TwilioClient.Init(config.AccountSID, config.AuthToken);32_config = config;3334}3536public WorkspaceConfig(Type workspaceResource):this()37{38}3940public virtual ActivityResource GetActivityByFriendlyName(string workspaceSid, string friendlyName)41{42return ActivityResource.Read(workspaceSid, friendlyName).First();43}4445public virtual ActivityResource CreateActivityWithFriendlyName(string workspaceSid, string friendlyName)46{47return ActivityResource.Create(workspaceSid, friendlyName);48}4950public virtual WorkspaceResource GetWorkspaceByFriendlyName(string friendlyName)51{52return WorkspaceResource.Read(friendlyName).FirstOrDefault();53}5455public virtual WorkspaceResource CreateWorkspace(string friendlyName, Uri eventCallbackUrl)56{57return WorkspaceResource.Create(friendlyName, eventCallbackUrl);58}5960public virtual bool DeleteWorkspace(string workspaceSid)61{62return WorkspaceResource.Delete(workspaceSid);63}6465public virtual WorkerResource CreateWorker(string workspaceSid, string bob, string activitySid, string attributes)66{67return WorkerResource.Create(workspaceSid, bob, activitySid, attributes);68}6970public void Register()71{72var workspace = DeleteAndCreateWorkspace(73"Twilio Workspace", new Uri(new Uri(_config.HostUrl), "/callback/events").AbsoluteUri);74var workspaceSid = workspace.Sid;7576var assignmentActivity = GetActivityByFriendlyName(workspaceSid, "Unavailable");77var idleActivity = GetActivityByFriendlyName(workspaceSid, "Available");78var reservationActivity = CreateActivityWithFriendlyName(workspaceSid, "Reserved");79var offlineActivity = GetActivityByFriendlyName(workspaceSid, "Offline");8081var workers = CreateWorkers(workspaceSid, idleActivity);82var taskQueues = CreateTaskQueues(workspaceSid, assignmentActivity, reservationActivity);83var workflow = CreateWorkflow(workspaceSid, taskQueues);8485Singleton.Instance.WorkspaceSid = workspaceSid;86Singleton.Instance.WorkflowSid = workflow.Sid;87Singleton.Instance.Workers = workers;88Singleton.Instance.PostWorkActivitySid = idleActivity.Sid;89Singleton.Instance.IdleActivitySid = idleActivity.Sid;90Singleton.Instance.OfflineActivitySid = offlineActivity.Sid;91}9293public virtual WorkspaceResource DeleteAndCreateWorkspace(string friendlyName, string eventCallbackUrl) {94var workspace = GetWorkspaceByFriendlyName(friendlyName);95if (workspace != null)96{97DeleteWorkspace(workspace.Sid);98}99100return CreateWorkspace(friendlyName, new Uri(eventCallbackUrl));101}102103private IDictionary<string, string> CreateWorkers(string workspaceSid, ActivityResource activity)104{105var attributesForBob = new106{107products = new List<object>()108{109"ProgrammableSMS"110},111contact_uri = _config.AgentForProgrammableSMS112};113114var bobWorker = CreateWorker(workspaceSid, "Bob", activity.Sid, Json.Encode(attributesForBob));115116var attributesForAlice = new117{118products = new List<object>()119{120"ProgrammableVoice"121},122contact_uri = _config.AgentForProgrammableVoice123};124125var alice = CreateWorker(workspaceSid, "Alice", activity.Sid, Json.Encode(attributesForAlice));126127return new Dictionary<string, string>128{129{ _config.AgentForProgrammableSMS, bobWorker.Sid },130{ _config.AgentForProgrammableVoice, alice.Sid },131};132}133134public virtual TaskQueueResource CreateTaskQueue(135string workspaceSid, string friendlyName,136string assignmentActivitySid, string reservationActivitySid, string targetWorkers)137{138var queue = TaskQueueResource.Create(139workspaceSid,140friendlyName: friendlyName,141assignmentActivitySid: assignmentActivitySid,142reservationActivitySid: reservationActivitySid143);144145TaskQueueResource.Update(146workspaceSid,147queue.Sid,148friendlyName,149targetWorkers,150assignmentActivitySid,151reservationActivitySid,1521);153154return queue;155}156157private IDictionary<string, TaskQueueResource> CreateTaskQueues(158string workspaceSid, ActivityResource assignmentActivity, ActivityResource reservationActivity)159{160161var voiceQueue = CreateTaskQueue(162workspaceSid, "Voice",163assignmentActivity.Sid, reservationActivity.Sid, "products HAS 'ProgrammableVoice'");164165var smsQueue = CreateTaskQueue(166workspaceSid, "SMS",167assignmentActivity.Sid, reservationActivity.Sid, "products HAS 'ProgrammableSMS'");168169var allQueue = CreateTaskQueue(170workspaceSid, "All",171assignmentActivity.Sid, reservationActivity.Sid, "1 == 1");172173return new Dictionary<string, TaskQueueResource> {174{ VoiceQueue, voiceQueue },175{ SmsQueue, smsQueue },176{ AllQueue, allQueue }177};178}179180public virtual WorkflowResource CreateWorkflow(string workspaceSid, IDictionary<string, TaskQueueResource> taskQueues)181{182var voiceQueue = taskQueues[VoiceQueue];183var smsQueue = taskQueues[SmsQueue];184var allQueue = taskQueues[AllQueue];185186var voiceFilter = new {187friendlyName = "Voice",188expression = "selected_product==\"ProgrammableVoice\"",189targets = new List<object>() {190new { queue = voiceQueue.Sid, Priority = "5", Timeout = "30" },191new { queue = allQueue.Sid, Expression = "1==1", Priority = "1", Timeout = "30" }192}193};194195var smsFilter = new {196friendlyName = "SMS",197expression = "selected_product==\"ProgrammableSMS\"",198targets = new List<object>() {199new { queue = smsQueue.Sid, Priority = "5", Timeout = "30" },200new { queue = allQueue.Sid, Expression = "1==1", Priority = "1", Timeout = "30" }201}202};203204var workflowConfiguration = new205{206task_routing = new207{208filters = new List<object>()209{210voiceFilter,211smsFilter212},213default_filter = new214{215queue = allQueue.Sid,216expression = "1==1",217priority = "1",218timeout = "30"219}220}221};222223// Call REST API224return WorkflowResource.Create(225workspaceSid,226"Tech Support",227Json.Encode(workflowConfiguration),228new Uri($"{_config.HostUrl}/callback/assignment"),229new Uri($"{_config.HostUrl}/callback/assignment"),23015);231}232}233}
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.
TaskRouter.Web/Controllers/CallController.cs
1using System.Web.Mvc;2using TaskRouter.Web.Infrastructure;3using TaskRouter.Web.Models;4using TaskRouter.Web.Services;5using Twilio.AspNet.Mvc;6using Twilio.TwiML;78namespace TaskRouter.Web.Controllers9{10public class CallController : TwilioController11{12private readonly IMissedCallsService _service;1314public CallController()15{16_service = new MissedCallsService(new TaskRouterDbContext());17}1819public CallController(IMissedCallsService service)20{21_service = service;22}2324[HttpPost]25public ActionResult Incoming()26{27var response = new VoiceResponse();28var gather = new Gather(numDigits: 1, action: "/call/enqueue", method: "POST");29gather.Say("For Programmable SMS, press one. For Voice, press any other key.");30response.Gather(gather);3132return TwiML(response);33}3435[HttpPost]36public ActionResult Enqueue(string digits)37{38var selectedProduct = digits == "1" ? "ProgrammableSMS" : "ProgrammableVoice";39var response = new VoiceResponse();4041response.Enqueue(42selectedProduct,43workflowSid: Singleton.Instance.WorkflowSid);4445return TwiML(response);46}474849}50}
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.
TaskRouter.Web/Controllers/CallController.cs
1using System.Web.Mvc;2using TaskRouter.Web.Infrastructure;3using TaskRouter.Web.Models;4using TaskRouter.Web.Services;5using Twilio.AspNet.Mvc;6using Twilio.TwiML;78namespace TaskRouter.Web.Controllers9{10public class CallController : TwilioController11{12private readonly IMissedCallsService _service;1314public CallController()15{16_service = new MissedCallsService(new TaskRouterDbContext());17}1819public CallController(IMissedCallsService service)20{21_service = service;22}2324[HttpPost]25public ActionResult Incoming()26{27var response = new VoiceResponse();28var gather = new Gather(numDigits: 1, action: "/call/enqueue", method: "POST");29gather.Say("For Programmable SMS, press one. For Voice, press any other key.");30response.Gather(gather);3132return TwiML(response);33}3435[HttpPost]36public ActionResult Enqueue(string digits)37{38var selectedProduct = digits == "1" ? "ProgrammableSMS" : "ProgrammableVoice";39var response = new VoiceResponse();4041response.Enqueue(42selectedProduct,43workflowSid: Singleton.Instance.WorkflowSid);4445return TwiML(response);46}474849}50}
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.
TaskRouter.Web/Controllers/CallbackController.cs
1using Newtonsoft.Json;2using System;3using System.Linq;4using System.Threading.Tasks;5using System.Web.Mvc;6using TaskRouter.Web.Infrastructure;7using TaskRouter.Web.Models;8using TaskRouter.Web.Services;9using Twilio;10using Twilio.AspNet.Mvc;11using Twilio.Rest.Api.V2010.Account;12using Twilio.Types;1314namespace TaskRouter.Web.Controllers15{16public class CallbackController : TwilioController17{18private readonly IMissedCallsService _service;1920public CallbackController()21{22_service = new MissedCallsService(new TaskRouterDbContext());2324if (Config.ENV != "test")25{26TwilioClient.Init(Config.AccountSID, Config.AuthToken);27}28}2930public CallbackController(IMissedCallsService service)31{32_service = service;33}3435[HttpPost]36public ActionResult Assignment()37{38var response = new39{40instruction = "dequeue",41post_work_activity_sid = Singleton.Instance.PostWorkActivitySid42};4344return new JsonResult() { Data = response };45}4647[HttpPost]48public async Task<ActionResult> Events(49string eventType, string taskAttributes, string workerSid, string workerActivityName, string workerAttributes)50{51if (IsEventTimeoutOrCanceled(eventType))52{53await CreateMissedCallAndRedirectToVoiceMail(taskAttributes);54}5556if (HasWorkerChangedToOffline(eventType, workerActivityName))57{58SendMessageToWorker(workerSid, workerAttributes);59}6061return new EmptyResult();62}636465private bool IsEventTimeoutOrCanceled(string eventType)66{67var desiredEvents = new string[] { "workflow.timeout", "task.canceled" };68return desiredEvents.Any(e => e == eventType);69}7071private bool HasWorkerChangedToOffline(string eventType, string workerActivityName)72{73return eventType == "worker.activity.update" && workerActivityName == "Offline";74}7576private async Task CreateMissedCallAndRedirectToVoiceMail(string taskAttributes)77{78dynamic attributes = JsonConvert.DeserializeObject(taskAttributes);79var missedCall = new MissedCall80{81PhoneNumber = attributes.from,82Product = attributes.selected_product,83CreatedAt = DateTime.Now84};8586await _service.CreateAsync(missedCall);87string voiceSid = attributes.call_sid;88VoiceMail(voiceSid);89}9091private void SendMessageToWorker(string workerSid, string workerAttributes)92{93const string message = "You went offline. To make yourself available reply with \"on\"";9495dynamic attributes = JsonConvert.DeserializeObject(workerAttributes);96string workerPhoneNumber = attributes.contact_uri;9798MessageResource.Create(99to: new PhoneNumber(Config.TwilioNumber),100from: new PhoneNumber(workerPhoneNumber),101body: message102);103}104105private void VoiceMail(string callSid)106{107var msg = "Sorry, All agents are busy. Please leave a message. We will call you as soon as possible";108var routeUrl = "http://twimlets.com/voicemail?Email=" + Config.VoiceMail + "&Message=" + Url.Encode(msg);109CallResource.Update(callSid, url: new Uri(routeUrl));110}111}112}
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.
TaskRouter.Web/Controllers/CallbackController.cs
1using Newtonsoft.Json;2using System;3using System.Linq;4using System.Threading.Tasks;5using System.Web.Mvc;6using TaskRouter.Web.Infrastructure;7using TaskRouter.Web.Models;8using TaskRouter.Web.Services;9using Twilio;10using Twilio.AspNet.Mvc;11using Twilio.Rest.Api.V2010.Account;12using Twilio.Types;1314namespace TaskRouter.Web.Controllers15{16public class CallbackController : TwilioController17{18private readonly IMissedCallsService _service;1920public CallbackController()21{22_service = new MissedCallsService(new TaskRouterDbContext());2324if (Config.ENV != "test")25{26TwilioClient.Init(Config.AccountSID, Config.AuthToken);27}28}2930public CallbackController(IMissedCallsService service)31{32_service = service;33}3435[HttpPost]36public ActionResult Assignment()37{38var response = new39{40instruction = "dequeue",41post_work_activity_sid = Singleton.Instance.PostWorkActivitySid42};4344return new JsonResult() { Data = response };45}4647[HttpPost]48public async Task<ActionResult> Events(49string eventType, string taskAttributes, string workerSid, string workerActivityName, string workerAttributes)50{51if (IsEventTimeoutOrCanceled(eventType))52{53await CreateMissedCallAndRedirectToVoiceMail(taskAttributes);54}5556if (HasWorkerChangedToOffline(eventType, workerActivityName))57{58SendMessageToWorker(workerSid, workerAttributes);59}6061return new EmptyResult();62}636465private bool IsEventTimeoutOrCanceled(string eventType)66{67var desiredEvents = new string[] { "workflow.timeout", "task.canceled" };68return desiredEvents.Any(e => e == eventType);69}7071private bool HasWorkerChangedToOffline(string eventType, string workerActivityName)72{73return eventType == "worker.activity.update" && workerActivityName == "Offline";74}7576private async Task CreateMissedCallAndRedirectToVoiceMail(string taskAttributes)77{78dynamic attributes = JsonConvert.DeserializeObject(taskAttributes);79var missedCall = new MissedCall80{81PhoneNumber = attributes.from,82Product = attributes.selected_product,83CreatedAt = DateTime.Now84};8586await _service.CreateAsync(missedCall);87string voiceSid = attributes.call_sid;88VoiceMail(voiceSid);89}9091private void SendMessageToWorker(string workerSid, string workerAttributes)92{93const string message = "You went offline. To make yourself available reply with \"on\"";9495dynamic attributes = JsonConvert.DeserializeObject(workerAttributes);96string workerPhoneNumber = attributes.contact_uri;9798MessageResource.Create(99to: new PhoneNumber(Config.TwilioNumber),100from: new PhoneNumber(workerPhoneNumber),101body: message102);103}104105private void VoiceMail(string callSid)106{107var msg = "Sorry, All agents are busy. Please leave a message. We will call you as soon as possible";108var routeUrl = "http://twimlets.com/voicemail?Email=" + Config.VoiceMail + "&Message=" + Url.Encode(msg);109CallResource.Update(callSid, url: new Uri(routeUrl));110}111}112}
Most of the features of our application are implemented. The last piece is allowing the Workers to change their availability status. Let's see how to do that next.
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.
TaskRouter.Web/Controllers/MessageController.cs
1using System;2using System.Web.Mvc;3using TaskRouter.Web.Infrastructure;4using Twilio;5using Twilio.AspNet.Mvc;6using Twilio.Rest.Taskrouter.V1.Workspace;7using Twilio.TwiML;89namespace TaskRouter.Web.Controllers10{11public class MessageController : TwilioController12{13private const string On = "on";14private const string Off = "off";1516public MessageController()17{18if (Config.ENV != "test")19{20TwilioClient.Init(Config.AccountSID, Config.AuthToken);21}22}2324public virtual WorkerResource FetchWorker(string workspaceSid, string workerSid)25{26return WorkerResource.Fetch(workspaceSid, workerSid);27}2829public virtual WorkerResource UpdateWorker(string pathWorkspaceSid, string pathSid, string activitySid = null,30string attributes = null, string friendlyName = null)31{32return WorkerResource.Update(pathWorkspaceSid, pathSid, activitySid, attributes, friendlyName);33}3435[HttpPost]36public ActionResult Incoming(string from, string body)37{38var workspaceSid = Singleton.Instance.WorkspaceSid;39var workerSid = Singleton.Instance.Workers[from];40var idleActivitySid = Singleton.Instance.IdleActivitySid;41var offlineActivitySid = Singleton.Instance.OfflineActivitySid;42var message = "Unrecognized command, reply with \"on\" to activate your worker or \"off\" otherwise";4344var worker = FetchWorker(workspaceSid, workerSid);4546if (body.Equals(On, StringComparison.InvariantCultureIgnoreCase))47{48UpdateWorker(workspaceSid, workerSid, idleActivitySid, worker.Attributes, worker.FriendlyName);49message = "Your worker is online";50}5152if (body.Equals(Off, StringComparison.InvariantCultureIgnoreCase))53{54UpdateWorker(workspaceSid, workerSid, offlineActivitySid, worker.Attributes, worker.FriendlyName);55message = "Your worker is offline";56}5758return TwiML(new MessagingResponse().Message(message));59}60}61}
Congratulations! You finished this tutorial. As you can see, using Twilio's TaskRouter is quite simple.
If you're a .NET developer working with Twilio, you might also enjoy these tutorials:
Voice JavaScript SDK Quickstart
Learn how to use Twilio Voice SDK to make browser-to-phone and browser-to-browser calls with ease.
Learn how to implement ETA Notifications using ASP.NET MVC and Twilio.