Handling an Incoming MS Teams Call within your Application

Introduction

Integrating with Microsoft Teams can be a daunting task. Especially for things that aren't really covered using MS Graph or for which no easy to use SDK is made available. One of those things is handling an incoming Teams call (PSTN) for which you want to show caller information in your own application(s).

In such a case MS Graph isn't an option as it can take up to 15 minutes for you to be notified of an incoming call. By that time you probably already handled the call and got yourself a coffee..

Note: this article assumes you're interested in the "connected" -state of an incoming call (= picked up). If you're more interested in the "ringing" -state, you'll probably need to take the alternative approach as described at the end of the article.

After reading this post, you should have an understanding of how you can leverage incoming calls from Microsoft Teams and integrate with your own applications. Additionally, you should have all the fundamentals to handle different use-cases (e.g. redirect further to multiple different applications).

Flow

This is more or less what the flow would look like:

  1. Someone dials a user having a Teams phone-number

  2. At pickup/connect, Teams will start the conversation between the two parties. Additionally, it will open the URL specified as a Calling Policy.

  3. A website is opened in a new browser-tab and immediately redirects to our protocol passing in the phone-number as the argument. This is how we pass the information to our client application for further handling. The user will receive a popup asking whether to accept sending the request to the custom protocol.

    1. After a certain delay, the website closes the opened browser-tab.
  4. Windows will lookup the protocol-handler in the Windows Registry. The registered application will start and the argument passed along.

  5. An application handles the argument(s) passed in according to your needs. Some use-cases might include:

    1. Retrieve some information and display it on-screen

    2. Decide on one-or-more parameters what application to trigger

The Components

So, let's build a fully working example based on the information mentioned above. We'll need:

  • To already have Microsoft Teams Phone functionality enabled

  • A website page we use from Microsoft Teams whenever a call comes in

    • A redirect from this website page to trigger a custom-protocol
  • To have set some Microsoft Teams -settings

  • An application having registered this custom-protocol, handling the information passed along.

Enable Microsoft Teams Calling Functionality

Setting up Teams Phone within your organization is outside the scope of this blog post as it is too specific to each company's needs. More information can be found here: https://learn.microsoft.com/en-us/microsoftteams/setting-up-your-phone-system

A Redirect Website

This is a very simple static website having a single index.html file to handle the redirect. The HTML for this page could look like the following:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Sample redirect page</title>
    <base href="/" />
    <script type="text/javascript">
        function delay(time) {
          return new Promise(resolve => setTimeout(resolve, time));
        }

        function redirectToApp() {
            const urlParams = new URLSearchParams(window.location.search);
            const callingNumber = urlParams.get('number');            
            window.location.href = `myapp:${callingNumber}`;

            delay(30000).then(() => window.close());
        }
    </script>
</head>

<body onload="redirectToApp()">
  <p>If you are not redirected, <a href="javascript:redirectToApp()">click here</a> to open the application.</p>
</body>

</html>

You can host this application as you like. In this blog I've used an Azure Storage Account to host the index.html file. But feel free to use whatever suits your needs.

Additional Settings - MS Teams - Admin

Within MS Teams Admin Center we'll first configure the URL to open whenever an incoming call occurs. To manage this, go to your Teams Admin Center > Voice > Calling Policy and do the following:

  • Enable the "Open apps in browser for incoming PSTN calls" -checkbox

  • Provide the URL of the previously created "Redirect Website"

Link: https://learn.microsoft.com/en-us/microsoftteams/teams-calling-policy

Additional Settings - MS Teams - User

Additionally, we'll need to enable this for ourselves as well. Meaning, every user must also indicate in its settings whether or not to allow opening a website on pickup of an incoming call.

Go to Teams > Settings > Calls and make sure "Open apps in browser" is turned on.

Note: this option is only available if your logged-in user is registered as a Teams Phone User.

A demo-application handling the protocol

We'll create a simple demo application capable of

  • Registering the custom-protocol with Windows

  • Handling the incoming call by displaying the number of the caller

First we create a blank solution, create a new Console Application which we'll call "WindowsProtocol.Client" and attach it to the solution.

mkdir WindowsProtocol
cd WindowsProtocol

dotnet new sln -n WindowsProtocol
dotnet new console -n WindowsProtocol.Client
dotnet sln WindowsProtocol.sln add .\WindowsProtocol.Client\WindowsProtocol.csproj

Next, we'll host the application as an MSIX package.

In case you're wondering what it is and why we should use it: MSIX is a packaging format created by Microsoft that's used for distributing applications on Windows platforms. It's designed to be a modern replacement for traditional Windows Installer (MSI) and ClickOnce deployment methods, offering various improvements, features and extensions.

One of these is tnhe ability to register a windows.protocol. An example of such a protocol could be the tel -protocol. But since this is typically already registered with Microsoft Teams, we don't want to mess that up. So let's use our own protocol called myapp instead.

Once the application is installed and the protocol registered, we can easily trigger it from our web-browser by calling for example: myapp:12345.

For this part we'll require a bit of Visual Studio 2022 to create the Windows Application Packaging Project as doing that from the command-line would take us a bit too far.

Open up the Solution file and search for the project template called "Windows Application Packaging Project".

Select "Next" and specify a name for the project. (eg. WindowsProtocol.Client.Package). Click <Create> to create the Package project.

Additionally, it will ask us what version of Windows we intend to target. Leave the default values for now and click <OK>.

Next, we need to link the Package Project with the actual application we intend to host within our MSIX package. Right-click on the (package) project dependencies and select "Add Project Reference".

Select our WindowsProtocol.Client project from the list and make sure the checkbox is ticked.

Moving on we'll register the windows protocol we want to use with our app when we install the MSIX package.

First, right-click the Package.appxmanifest file and select "View Code". Since we'll need to use an argument for passing in our caller's number, we'll need to add an additional XML namespace mapping at the top of the package.

Next, we add an extension to handle the registration of the windows protocol.

Alright, almost there. We only need to capture the argument in our Console application. You're program.cs file should look something like the following.

internal class Program
{
    static void Main(string[] args)
    {
        if (args.Length == 0)
            Console.WriteLine("Hello, World!");

        // Argument 0 = /inbound, Argument 1 = number passed in
        if (args.Length > 0)
            Console.WriteLine($"The world is calling from number {args[1]}!"); 

        Console.WriteLine("Press <ANY> key to continue.");
        Console.ReadLine();
    }
}
  • Argument 0 : '/inbound' as specified in our application manifest

  • Argument 1 : the calling-number value

Final Touch - Microsoft Edge

Since we are asking Teams to open a URL on our behalf, which in turn tries to open a protocol-handler, Microsoft Edge (or other Chromium -based browser) will ask if this is really an action we want to perform and require a user-gesture in order to execute the action.

This is a bit annoying as it disrupts our automated flow, but luckily there is a way around this (well at least for Edge). By adding an additional policy to Edge we can specify which protocols to auto-accept and execute.

To add a Policy to Microsoft Edge you can either use the Group Policy Editor, or directly in the registry. It should look something like this:

Link: https://learn.microsoft.com/en-us/deployedge/microsoft-edge-policies#donotsilentlyblockprotocolsfromorigins

Testing the Solution

  1. Make sure you have been assigned a Microsoft Teams phone-number

  2. Start your application (MSIX) to register the application protocol

  3. Call the phone-number assigned to you

  4. Your Microsoft Edge browser should open asking you permission to use the application protocol

  5. Click <Open>

  6. Your application should start and display the caller number

Remarks

Within MS Teams, the URL will only be opened for an actual phone number. An internal number (e.g. between colleagues), or an anonymous number will not trigger the action.

Alternative

If the above approach is for some reason not usable, say, for example, you want to trigger some action on the "ringing" -state of an incoming call, then you could consider using Teams Wizard from LyncWizard. Teams Wizard is a free tool that extends Microsoft Teams with an additional feature called "Execute actions".

Whenever an incoming call occurs the application could execute a command or open an URL with the phone number as argument. It supports two events:

  • Incoming call: triggered on an incoming call

  • On-the-phone: triggered by presence state "On-the-phone", occurs for incoming or outgoing calls.

Link: https://www.lyncwizard.com/products.html#TeamsWizard