14

I'd love some advice on my twilio setup for a problem I'm trying to solve.

Overview:

Each user in our system is provisioned a twilio phone number that they can hand out to anyone to contact them.

If personA contacts a user in our system (userB) via the provisioned twilio phone number, we'd like to connect them with userB if they are available. If userB is not available, we'd like to direct personA to voicemail. In other words, we want to make sure that we have control of the voicemail experience and the voicemail itself so that we can store it in our system, rather than having the voicemail be left on userB's device.

Current solution:

  • PersonA's incoming call gets added to a queue. At the same time, the system dials out userB.
  • UserB is asked to press 1 to accept the call. The reason for the explicit entry from UserB is to detect whether UserB is available to answer the call. (For example, if the call to UserB goes to their personal voicemail, the explicit digit entry will not happen telling us they are not available to answer.)
  • If UserB does not enter 1 in a specified amount of time, PersonA is directed to voicemail.
  • If UserB presses 1, call to UserB is modified (via twilio rest api) to dial the queue that PersonA is in to connect UserB and PersonA.

Problem with current solution:

In this solution, the control of when to divert personA's call to voicemail is controlled by the outcome of the call to UserB, which seems suboptimal. For instance, we may not be able to call UserB at all. In this case, personA would stay in the queue indefinitely.

What I'd like to happen in this case is to poll the queue personA is in to check the time in queue, and divert the call to voicemail if time in queue is greater than a threshold. However, it does not seem like it is possible to accurately know how long a call is unattended in a queue because:

  • The status of a call in a queue is in-progress even if the caller is listening to wait music. This is the same status as if PersonA's call had been answered.

  • If UserB dials into the queue, the call is only dequeued when the bridged parties disconnect, with no change in the call status of PersonA's call to indicate that they have been connected to UserB.

Questions

  • Is my understanding of why I cannot poll the call queue to divert calls to voicemail correct?
  • Should I instead be calling PersonA into a conference, and if UserB is available, connecting him/her to the conference that PersonA is in?
  • If I use a conference setup, what would be the easiest way to detect how long PersonA has been waiting in the conference so as to divert PersonA's call to voicemail in the event of UserB never joining the conference?
kajham
  • 1,241
  • 15
  • 19

1 Answers1

20

Twilio developer evangelist here.

I think you may have overcomplicated things a bit here with the queue. You can actually provide the message and gather within the original call without having to dial out yourself and eventually connect the two calls.

Here's how:

Your incoming call TwiML should look like this:

<Response>
  <Dial action="/call_complete" timeout="30">
    <Number url="/whisper">
      ONWARD DIAL NUMBER
    </Number>
  </Dial>
</Response>

Giving the <Number> noun a URL will play the TwiML contents of that URL before the two calls are connected. You can use <Gather> in here to make sure the user has answered the call and not their own voicemail system:

/whisper

<Response>
  <Gather numDigits="1" timeout="10" action="/gather_result">
    <Say voice="alice">You are receiving a call, press any key to accept</Say>
  </Gather>
  <Hangup/>
</Response>

The /gather_result needs to work out whether a key was pressed or not. If it was pressed then we head through to the call, which we can do with an empty response as this gives control back to the original <Dial>. If no number was pressed we hangup this end, which causes the original <Dial> to complete and direct onto its action attribute. (I'm not sure what language you're working with but here is some Rubyish pseudo code)

/gather_result

<Response>
  if params["Digits"] and params["Digits"].empty?
    <Hangup/>
  end
</Response>

/call_complete will then get called once the <Dial> action is over. If the status of the call at this point is "completed" or "answered" then the user has picked up the call and responded correctly to the whisper and we can hang up. If it's anything else, we redirect our call onto our voicemail recorder.

/call_complete

<Response>
  if params["DialCallStatus"] == "completed" or params["DialCallStatus"] == "answered"
    <Hangup/>
  else
    <Say voice="alice">The call could not be answered this time, please leave a message</Say>
    <Record action="/record_complete" />
  end
</Response>

Then finally your /record_complete action can do whatever you want with the recording URL and hang up the call.

/record_complete

<Response>
  <Hangup/>
</Response>

This can all be achieved with Twimlets, as described in this blog post. Let me know if this helps at all.

philnash
  • 70,667
  • 10
  • 60
  • 88
  • 1
    Thanks so much for the detailed answer here. This makes a lot of sense. Improving my setup now! – kajham Dec 16 '15 at 18:58
  • No problem, give me a shout on philnash@twilio.com if you have any trouble. – philnash Dec 16 '15 at 18:59
  • I may be doing something wrong, but I had to put a Hangup directive after the Gather verb so as to capture the situation where the Gather times out with no input. Twilio was not calling the action URL if no user input was entered before the timeout, so my `/whisper` was: ` You have an incoming call. Press 1 to answer. ` – kajham Dec 18 '15 at 03:24
  • 1
    Fair enough, I'll update my answer to reflect that. Thanks for the feedback! – philnash Dec 18 '15 at 11:00
  • What a perfect answer. Gonna update all my source code. Thank You very much – BoCyrill Sep 10 '17 at 14:08
  • @BoCyrill Thanks, glad it helps! – philnash Sep 11 '17 at 09:12
  • @philnash Any chance you'd post an update using Twilio Functions and TwiML Bins? Would that be the easiest way to build this without hosting a server? – Larry Silverman Mar 20 '18 at 19:15
  • You could definitely do this with Functions and TwiML bins and that would likely be the easiest way to do so without your own server. I don't have time to write up how you'd do it right now as I'm at conferences all week. The pseudo-code above should give you a good idea though, why not have a go and ask a SO question if you get stuck somewhere? – philnash Mar 21 '18 at 01:49
  • what if i am using in twiml. And in conference, we can't use what can I do now? – Haisam Hameed May 17 '19 at 18:30
  • @HaisamHameed This is about recording voicemail, which you wouldn't do during a conference. What are you really trying to do? – philnash May 17 '19 at 18:31
  • Currently, I am using the conference to handle incoming calls put them on hold,unhold and transfer call. What I am trying to do now is. If someone called on Twilio number and If no one answered. The caller should leave a message on voicemail. Is it possible ? – Haisam Hameed May 17 '19 at 18:34
  • Sure, how do you know that no-one answered? – philnash May 17 '19 at 18:35
  • This could happen. If someone calls on my twilio number. And I was away from the computer I couldn't pick. Now the caller should leave a message in that case.Are you getting my requirement here ? – Haisam Hameed May 17 '19 at 18:37
  • I am, I'm trying to get you to think about how you might trigger this. If you automatically drop a caller into a conference you need to have some idea of what means that no-one else has joined them. Also, perhaps starting with a conference isn't the best idea. What I think I'd do is use [``](https://www.twilio.com/docs/voice/twiml/enqueue) to put them in a queue initially, then try to make the connection by dialling your agent, if there's no answer then move the caller from the queue to `` a voicemail, and if there is an answer move them to a ``. Does that help? – philnash May 17 '19 at 18:41
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/193535/discussion-between-haisam-hameed-and-philnash). – Haisam Hameed May 17 '19 at 18:44
  • Great stuff! How would I go about implementing this with Functions/TwiML Bins? I'm having a hard time placing the code for the different routes. The Manage Numbers dashboard only provides two handler slots (A CALL COMES IN and PRIMARY HANDLER FAILS). – Karoh Jun 03 '19 at 16:58
  • @Karoh The webhook for when a call comes in should point to the first TwiML (the ``). Beyond then, each of the TwiML responses themselves point to where the next TwiML comes from, so you don't configure those URLs for your number, but within the TwiML itself. See, for example, the `url` attribute on the first `` or the `action` attribute for the ``. – philnash Jun 04 '19 at 01:01
  • Thanks! It would be incredibly helpful if you could add to your answer the TwiML for `/gatherResult` and `/callComplete`. – Karoh Jun 06 '19 at 19:00
  • @Karoh I did, they are the third and fourth code examples. Everything is in a bit of pseudo code, and the path is at the top of the example. What are you missing? – philnash Jun 06 '19 at 23:38
  • Thanks @philnash, yeah I was just looking to see the non-pseudo code in TwiML for that. But I realize it's asking a lot... – Karoh Jun 12 '19 at 00:50
  • @Karoh The TwiML itself is there though. Unless you mean you want the example written out in a specific programming language? – philnash Jun 12 '19 at 04:14