Enable proactive bot installation and proactive messaging in Teams with Microsoft Graph (Public Preview)

Important

Microsoft Graph and Microsoft Teams public previews are available for early-access and feedback. Although this release has undergone extensive testing, it is not intended for use in production.

Proactive messaging in Teams

Proactive messages are initiated by bots to start conversations with a user. They serve many purposes including sending welcome messages, conducting surveys or polls, and broadcasting organization-wide notifications. Proactive messages in Teams can be delivered as either ad-hoc or dialog-based conversations:

Message Type Description
Ad-hoc proactive message The bot interjects a message without interrupting the conversation flow.
Dialog-based proactive message The bot creates a new dialog thread, takes control of a conversation, delivers the proactive message, closes, and returns control to the previous dialog.

See, Send proactive notifications to users SDK v4

Proactive app installation in Teams

Before your bot can proactively message a user, it needs to be installed either as a personal app, or in a team where the user is a member. At times, you may need to proactively message users that have not installed or previously interacted with your app. For instance, the need to message vital information to everyone in your organization. For such scenarios, you can use the Microsoft Graph API to proactively install your bot for your users.

Permissions

Microsoft Graph teamsAppInstallation resource type permissions allow you to manage your app's installation lifecycle for all user (personal) or team (channel) scopes within the Microsoft Teams platform:

Application permission Description
TeamsAppInstallation.ReadWriteSelfForUser.All Allows a Teams app to read, install, upgrade, and uninstall itself for any user, without prior sign in or use.
TeamsAppInstallation.ReadWriteSelfForTeam.All Allows a Teams app to read, install, upgrade, and uninstall itself in any team, without prior sign in or use.

To use these permissions, you must add a webApplicationInfo key to your app manifest with the following values:

  • id — your Azure AD app id.
  • resource — the resource URL for the app.

Note

  • Your bot requires application not user delegated permissions because the installation is not for yourself but for others.

  • An Azure AD tenant administrator must explicitly grant permissions to an application. After an application is granted permissions, all members of the Azure AD tenant will gain the granted permissions.

Enable proactive app installation and messaging

Important

Microsoft Graph will only install apps published within your organization's app catalog or in AppSource.

✔ Create and publish your proactive messaging bot for Teams

To get started, you will need a bot for Teams with proactive messaging capabilities and published in your organization's app catalog or in AppSource.

Tip

The production-ready Company Communicator app template enables broadcast messaging and is a good foundation for building your proactive bot application.

✔ Get the teamsAppId for your app

1. You will need the teamsAppId for the next steps.

The teamsAppId can be retrieved from your organization's app catalog:

Microsoft Graph page reference: teamsApp resource type

HTTP GET request:

GET https://graph.microsoft.com/beta/appCatalogs/teamsApps?$filter=externalId eq '{IdFromManifest}'

The request will return a teamsApp object. The returned object's id is the app's catalog generated app id and is different from the "id:" that you provided in your Teams app manifest:

{
  "value": [
    {
      "id": "b1c5353a-7aca-41b3-830f-27d5218fe0e5",
      "externalId": "f31b1263-ba99-435a-a679-911d24850d7c",
      "name": "Test App",
      "version": "1.0.1",
      "distributionMethod": "Organization"
    }
  ]
}

2. If your app has already been uploaded/sideloaded for a user in the personal scope, you can retrieve the teamsAppId as follows:

Microsoft Graph page reference: List apps installed for user

HTTP GET request:

GET https://graph.microsoft.com/beta/users/{user-id}/teamwork/installedApps?$expand=teamsApp&$filter=teamsApp/externalId eq '{IdFromManifest}'

3. If your app has already been uploaded/sideloaded for a channel in the team scope, you can retrieve the teamsAppId as follows:

Microsoft Graph page reference: List apps in team

HTTP GET request:

GET https://graph.microsoft.com/beta/teams/{team-id}/installedApps?$expand=teamsApp&$filter=teamsApp/externalId eq '{IdFromManifest}'

Tip

You can filter on any of the fields of the teamsApp object to narrow the list of results.

✔ Determine whether your bot is currently installed for a message recipient

Microsoft Graph page reference: List apps installed for user

HTTP GET request:

GET https://graph.microsoft.com/beta/users/{user-id}/teamwork/installedApps?$expand=teamsApp&$filter=teamsApp/id eq '{teamsAppId}'

This request will return an empty array if the app is not installed, or an array with a single teamsAppInstallation object if it has been installed.

✔ Install your app

Microsoft Graph page reference: Install app for user

HTTP POST request:

POST https://graph.microsoft.com/beta/users/{user-id}/teamwork/installedApps
{
   "teamsApp@odata.bind" : "https://graph.microsoft.com/beta/appCatalogs/teamsApps/{teamsAppId}"
}

If the user has Microsoft Teams running, they may see the app install immediately. Alternatively, a restart may be necessary to see the installed app.

✔ Retrieve the conversation chatId

When your app is installed for the user, the bot will receive a conversationUpdate event notification that will contain the necessary information to send the proactive message.

The chatId can also be retrieved as follows:

Microsoft Graph page reference: Get chat

1. You will need your app's {teamsAppInstallationId}. If you don't have it, use the following:

HTTP GET request:

GET https://graph.microsoft.com/beta/users/{user-id}/teamwork/installedApps?$expand=teamsApp&$filter=teamsApp/id eq '{teamsAppId}'

The id property of the response is the teamsAppInstallationId.

2. Make the following request to fetch the chatId:

HTTP GET request (permission — TeamsAppInstallation.ReadWriteSelfForUser.All):

 GET https://graph.microsoft.com/beta/users/{user-id}/teamwork/installedApps/{teamsAppInstallationId}/chat

The id property of the response is the chatId.

Alternately, you can retrieve the chatId with the request below, but it will require the broader Chat.Read.All permission:

HTTP GET request (permission — Chat.Read.All):

GET https://graph.microsoft.com/beta/users/{user-id}/chats?$filter=installedApps/any(a:a/teamsApp/id eq '{teamsAppId}')

✔ Send proactive messages

Once your bot has been added for a user or team and has acquired the necessary user information, it can begin to send proactive messages.

The following code snippet is from the Microsoft Bot Framework Samples for C#.

using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Schema;

namespace Microsoft.BotBuilderSamples
{
    public class ProactiveBot : ActivityHandler
    {
        // Message to send to users when the bot receives a Conversation Update event
        private const string WelcomeMessage = "Welcome to the Proactive Bot sample.  Navigate to http://localhost:3978/api/notify to proactively message everyone who has previously messaged this bot.";

        // Dependency injected dictionary for storing ConversationReference objects used in NotifyController to proactively message users
        private readonly ConcurrentDictionary<string, ConversationReference> _conversationReferences;

        public ProactiveBot(ConcurrentDictionary<string, ConversationReference> conversationReferences)
        {
            _conversationReferences = conversationReferences;
        }

        private void AddConversationReference(Activity activity)
        {
            var conversationReference = activity.GetConversationReference();
            _conversationReferences.AddOrUpdate(conversationReference.User.Id, conversationReference, (key, newValue) => conversationReference);
        }

        protected override Task OnConversationUpdateActivityAsync(ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            AddConversationReference(turnContext.Activity as Activity);

            return base.OnConversationUpdateActivityAsync(turnContext, cancellationToken);
        }

        protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            foreach (var member in membersAdded)
            {
                // Greet anyone that was not the target (recipient) of this message.
                if (member.Id != turnContext.Activity.Recipient.Id)
                {
                    await turnContext.SendActivityAsync(MessageFactory.Text(WelcomeMessage), cancellationToken);
                }
            }
        }

        protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
        {
            AddConversationReference(turnContext.Activity as Activity);

            // Echo back what the user said
            await turnContext.SendActivityAsync(MessageFactory.Text($"You sent '{turnContext.Activity.Text}'"), cancellationToken);
        }
    }
}

View additional code samples