In the previous step we created a Task from an incoming phone call using <Enqueue workflowSid="WW0123401234..">
. In this step we will create another call and dequeue it to an eligible Worker when one becomes available.
Back in Part 1 of the Quickstart we created a Worker named Alice that is capable of handling both English and Spanish inquiries. With your Workspace open in the TaskRouter web portal, click 'Workers' and click to edit the details of our Worker Alice. Ensure that Alice is set to a non-available Activity state such as 'Offline'. Next, edit Alice's JSON attributes and add a contact_uri
field. Replace the dummy 555 number below with your own phone number.
Alice's modified JSON attributes:
{"languages": ["en", "es"], "contact_uri": "+15555555555"}
Ensure you use the E.164 format for the contact_uri
.
Or, as displayed in the web portal:
In this step, we again use <Enqueue>
to create a Task from an incoming phone call. When an eligible Worker (in this case Alice) becomes available, TaskRouter will make a request to our Assignment Callback URL. This time, we will respond with a special 'dequeue' instruction; this tells Twilio to call Alice at her 'contact_uri' and bridge to the caller.
For this part of the Quickstart, although not totally necessary it will be useful to have two phones available - one to call your Twilio number, and one to receive a call as Alice. Experienced Twilio users might consider using the Twilio Dev Phone as one of the endpoints.
Before we add the 'dequeue' assignment instruction we need to create a new Activity in our TaskRouter Workspace. One of the nice things about integrating TaskRouter with TwiML is that our Worker will automatically transition through various Activities as the call is assigned, answered and even hung up. We need an Activity for our Worker to transition to when the call ends.
With your Workspace open in the TaskRouter web portal, click 'Activities' and then 'Create Activity'. Give the new Activity a name of 'WrapUp' and a value of 'unavailable'. Once you've saved it, make a note of the Activity Sid:
To return the 'dequeue' assignment instruction, modify TwilioTaskRouterServlet
assignment_callback endpoint to now issue a dequeue instruction, substituting your new WrapUp ActivitySid between the curly braces:
1import java.io.IOException;2import java.util.HashMap;3import java.util.Map;45import javax.servlet.http.HttpServlet;6import javax.servlet.http.HttpServletRequest;7import javax.servlet.http.HttpServletResponse;89import org.json.simple.JSONObject;1011import com.twilio.Twilio;12import com.twilio.rest.taskrouter.v1.workspace.Task;13import com.twilio.rest.taskrouter.v1.workspace.task.Reservation;14import com.twilio.twiml.*;1516public class TwilioTaskRouterServlet extends HttpServlet {1718private String accountSid;19private String authToken;20private String workspaceSid;21private String workflowSid;2223@Override24public void init() {25accountSid = this.getServletConfig().getInitParameter("AccountSid");26authToken = this.getServletConfig().getInitParameter("AuthToken");27workspaceSid = this.getServletConfig().getInitParameter("WorkspaceSid");28workflowSid = this.getServletConfig().getInitParameter("WorkflowSid");2930Twilio.init(accountSid, authToken);31}3233// service() responds to both GET and POST requests.34// You can also use doGet() or doPost()35@Override36public void service(final HttpServletRequest request, final HttpServletResponse response)37throws IOException {38if (request.getPathInfo() == null || request.getPathInfo().isEmpty()) {39return;40}4142if (request.getPathInfo().equals("/assignment_callback")) {43response.setContentType("application/json");4445Map<String, String> dequeueInstruction = new HashMap<String, String>();46dequeueInstruction.put("instruction", "dequeue");47dequeueInstruction.put("from", "<caller_number>");48dequeueInstruction.put("post_work_activity_sid", "WA0123401234...");4950response.getWriter().print(JSONObject.toJSONString(dequeueInstruction));51} else if (request.getPathInfo().equals("/create_task")) {52response.setContentType("application/json");53response.getWriter().print(createTask());54} else if (request.getPathInfo().equals("/accept_reservation")) {55response.setContentType("application/json");56String taskSid = request.getParameter("TaskSid");57String reservationSid = request.getParameter("ReservationSid");58response.getWriter().print(acceptReservation(taskSid, reservationSid));59} else if (request.getPathInfo().equals("/incoming_call")) {60response.setContentType("application/xml");61response.getWriter().print(handleIncomingCall());62} else if (request.getPathInfo().equals("/enqueue_call")) {63response.setContentType("application/xml");64response.getWriter().print(enqueueTask());65}66}6768public String createTask() {69String attributes = "{\"selected_language\":\"es\"}";7071Task task = Task.creator(workspaceSid, attributes, workflowSid).create();7273return "{\"task_sid\":\"" + task.getSid() + "\"}";74}7576public String acceptReservation(final String taskSid, final String reservationSid) {77Reservation reservation = Reservation.updater(workspaceSid, taskSid, reservationSid)78.setReservationStatus(Reservation.Status.ACCEPTED).update();7980return "{\"worker_name\":\"" + reservation.getWorkerName() + "\"}";81}8283public String handleIncomingCall() {84VoiceResponse twiml =85new VoiceResponse.Builder()86.gather(new Gather.Builder()87.say(new Say.Builder("Para Español oprime el uno.").language(Say.Language.ES)88.build())89.say(new Say.Builder("For English, please hold or press two.")90.language(Say.Language.EN).build())91.numDigits(1).timeout(5).build())92.build();9394try {95return twiml.toXml();96} catch (TwiMLException e) {97return "Error creating TwiML: " + e.getMessage();98}99}100101public String enqueueTask() {102com.twilio.twiml.Task task =103new com.twilio.twiml.Task.Builder().data("{\"selected_language\":\"es\"}").build();104105EnqueueTask enqueue = new EnqueueTask.Builder(task).workflowSid(workflowSid).build();106107VoiceResponse twiml = new VoiceResponse.Builder().enqueue(enqueue).build();108109try {110return twiml.toXml();111} catch (TwiMLException e) {112return "Error creating TwiML: " + e.getMessage();113}114}115}
This returns a very simple JSON object from the Assignment Callback URL:
{"instruction":"dequeue", "from": "<caller_number>", "post_work_activity_sid": "WA01234012340123401234"}
The JSON instructs Twilio to dequeue the waiting call and, because we don't include an explicit "to" field in our JSON, connect it to our Worker at their contact_uri
. This is convenient default behavior provided by TaskRouter.
In the next step, we test our incoming call flow from end-to-end.