How to know when voicemail answers a Lync call

If you have a UCMA application that does call routing, particularly one that is designed to get callers to an available person, you may need a way to figure out whether your call has been answered by the person you were trying to dial, using Lync; whether it has been picked up by Exchange UM voicemail; or whether it has been redirected to another Lync user or a PSTN phone number. This way, you can avoid situations like sending an irritable customer who wants immediate assistance to the voicemail box of someone whose name they have never heard before. Now, one option you have is to simply prevent calls from being redirected, using the Ms-Sensitivity SIP header. This is a sort of "brute force" approach, which I''ll describe in another post. But let''s say you do want to allow the call to go to voicemail (rather than simply ringing and ringing until it times out), but you want your UCMA application to find out as soon as this happens.

There''s not a built-in way to do this in UCMA; in other words, you can''t look at an AnsweredByVoicemail property on the call object or watch for a VoicemailAnswered event. Neither of those exists in the API. What you can do is take a close look at some of the SIP headers that come back to your UCMA application when the call is accepted, and interpret those to figure out who actually answered your call.

Here''s how you can do it. First of all, as a baseline,let''s look at a typical block of code to establish a plain vanilla two-party audio call:

[csharp] AudioVideoCall call = new AudioVideoCall(_conversation);

try { call.BeginEstablish(_destinationSipUri,null, establishAsyncResult => { try { call.EndEstablish(establishAsyncResult); } catch (RealTimeException eX) { Console.WriteLine(eX); } }, null); } catch (InvalidOperationException ex) { Console.WriteLine(ex); } [/csharp]

Now, the AudioVideoCall.EndEstablish method actually returns an object of type CallMessageData, and you can use this object to find out the contents of individual SIP headers in the 200 OK response that the UCMA app receives when the call is accepted.

The specific header that we''re interested in here is the Contact header. By looking at some of the parameters on this header, we''ll be able to tell whether we''ve reached the actual user, or an Exchange UM voicemail box.

Here''s some basic code that retrieves the SIP headers from the 200 OK and writes out the Contact header to the console:

[csharp] AudioVideoCall call = new AudioVideoCall(_conversation);

try { call.BeginEstablish(_destinationSipUri, null, establishAsyncResult => { try { CallMessageData data = call.EndEstablish(establishAsyncResult);

Console.WriteLine("Established call");

IEnumerable headers = data.MessageData.SignalingHeaders;

SignalingHeader contactHeader = headers.FirstOrDefault(h => h.Name.ToUpper() == "CONTACT");

Console.WriteLine(contactHeader.GetValue()); } catch (RealTimeException eX) { Console.WriteLine(eX); } }, null);

Console.WriteLine("Establishing call");

} catch (InvalidOperationException ex) { Console.WriteLine(ex); } [/csharp]

If you like, try placing a few calls with this code and seeing what the Contact header in the response looks like when they go to voicemail, when they get answered, when they get redirected to a PSTN number, etc.

If I have the application place a call to my own SIP URI, and I answer it in the Lync client, the Contact header in the 200 OK looks something like this:

<sip:michael@domain.local;opaque=user:epid:68ZWuWgoTFGKLmrVg2wKdAAA;gruu>

What if I let the call go to voicemail? In that case, I get a Contact header like this one:

<sip:exchange.domain.local:5065;transport=Tls>;automata;text;audio;video;image

As you can see, the Contact header now contains a SIP URI made up of my Exchange server FQDN, a port number, and transport=Tls, plus a bunch of extra parameters like automata.

Another possibility is that I redirect the call to a PSTN number, which produces a Contact header like this:

<sip:mediationsrvr@domain.local;gruu;opaque=srvr:Mediation Server:MISenR4c2VmzcA-yvJm-bAAA;grid=d0a9c2ad610c4665abfa161398f3ba00>;isGateway

So, basically it has an address identifying the Mediation Server.

Yet another possibility is that I redirect the call to a different Lync user, in which case the Contact header will be like my first example, but will not match the original destination SIP URI of the call.

So, we can grab the Contact header when the call is accepted, and look for the following things:

  • If the Contact header contains the original destination SIP URI, then we know that the intended recipient answered the call in Lync.
  • If the Contact header contains the parameter automata, it was answered by the Exchange UM service for voicemail, or possibly by some other UC application.
  • If the Contact header contains the parameter opaque=srvr:MediationServer, or the parameter isGateway, it''s gone to a PSTN location.
  • If the Contact header contains some other user''s SIP URI, but not the original destination URI, it was likely forwarded to another Lync user and answered in the Lync client.

The following sample code shows how you might use these different Contact headers to identify how the call got answered.

[csharp] private void PlaceCall() { _conversation = new Conversation(_endpoint); AudioVideoCall call = new AudioVideoCall(_conversation);

try { call.BeginEstablish(_destinationSipUri, null, establishAsyncResult => { try { CallMessageData data = call.EndEstablish(establishAsyncResult);

Console.WriteLine("Established call");

IEnumerable headers = data.MessageData.SignalingHeaders;

SignalingHeader contactHeader = headers.FirstOrDefault(h => h.Name.ToUpper() == "CONTACT");

if (contactHeader.GetValue().Contains(_destinationSipUri)) { Console.WriteLine("Answered by the dialed user in Lync."); } else if (contactHeader.GetValue().Contains("automata")) { Console.WriteLine("Answered by an application (probably voicemail)."); } else if (contactHeader.GetValue().Contains("isGateway") || contactHeader.GetValue().Contains("srvr:MediationServer")) { Console.WriteLine("Routed to a PSTN number."); } else { Console.WriteLine("Answered by another user."); } } catch (RealTimeException eX) { Console.WriteLine(eX); } }, null);

Console.WriteLine("Establishing call");

} catch (InvalidOperationException ex) { Console.WriteLine(ex); } } [/csharp]

This covers the four situations I described above:

  1. Call answered by the original destination user, in Lync
  2. Call answered by a different user (to whom it was forwarded) in Lync
  3. Call forwarded to voicemail
  4. Call forwarded to a PSTN phone

This approach, although it requires digging into SIP headers a bit, works quite well for determining whether a call got answered in Lync, or forwarded to voicemail or somewhere else.

Now, if you also need to recognize when a voicemail box picks up on a PSTN phone, you''ll need to take a different approach altogether. The approach I recommend is using the VoiceActivityChanged event on the Recorder object to count how long the first utterance is when the call is answered. Usually if a person picks up, what they say will be much shorter than if a voicemail message answers. I''ll leave the details on this for a future post.

Let me know if you have any questions about this approach, or if you find other cool things you can do with it.