Unable to send pro-active messages to users on Microsoft Teams

Subramani N 5 Reputation points
2025-09-24T13:02:47.8333333+00:00

I have built an application to send pro-active messages to users on Microsoft Teams. I am using C# Bot Framework SDK. The application is designed to support users from different tenants [Application Type: multi-tenant] and I have tested the same in the past by installing the app to users belonging to a different tenant than that where the Azure Bot Service is hosted and where the App is registered. Please note that the App doesn't require any API permission but just needs to be installed by the user.

After the recent changes made by Microsoft on July 31, 2025, I am no longer able to create an Azure Bot Service having App Type as multi-tenant. We plan to publish this application to the Microsoft Teams store. I got to know from the GitHub Issue - https://github.com/OfficeDev/Microsoft-Teams-Samples/issues/1747 that once the application is certified by Microsoft it will be multi-tenant. Therefore, I created the Azure Bot Service and App Registration with App Type as single-tenant as multi-tenant option was no longer available.

In the single-tenant configuration, I tested it by installing the application to user belong to the same tenant where the Azure Bot service is hosted and the App is registered. The code started to return an unauthorized exception. Neither I was not able to send out a Welcome Adaptive message card when the Bot application was installed by the user nor I was able to send out a pro-active message to the user.

Code Snippet

//App Credentials
MicrosoftAppCredentials _credentials = new MicrosoftAppCredentials(_configuration["MicrosoftAppId"], _configuration["MicrosoftAppPassword"]);

//Creating Conversation Parameter for Personal Chat between the Bot and the End User
var conversationParameters = new ConversationParameters { IsGroup = false, Bot = new ChannelAccount($"28:{this._credentials.MicrosoftAppId}"), Members = new ChannelAccount[] { new ChannelAccount(user.Id) }, TenantId = proactiveMessage.AzureEntraTenantId };

//Create Conversation and Send Pro-Active Message
await ((BotAdapter)_adapter).CreateConversationAsync( this._credentials.MicrosoftAppId, Microsoft.Bot.Connector.Channels.Msteams, this._serviceURL, null, conversationParameters, async (turnContext, cts) => { await turnContext.SendActivityAsync(proactiveMessageCard, cts); }, default);

Error Message:
**
Failed to acquire token for client credentials. (AADSTS700016: Application with identifier 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' was not found in the directory 'Bot Framework'. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You may have sent your authentication request to the wrong tenant.

Attempt 1:

The app doesn't need any API permissions—users just need to install it. Therefore, the admin consent or user consent is not required. I verified the details of the application that is the Microsoft App Id, Microsoft App Password, Tenant ID etc. and found it to be correct.

Attempt 2:

Add extra code to generate a token so as to verify the token generation. I used GetTokenAsync() of MicrosoftAppCredentials. I observed an error in the token generation. After adding the Tenant Id to the MicrosoftAppCredentials object, I was able to generate a token but still I was receiving an unauthorized exception when trying to create a conversation and send the pro-active message to the user.

//App Credentials
MicrosoftAppCredentials _credentials = new MicrosoftAppCredentials(_configuration["MicrosoftAppId"], _configuration["MicrosoftAppPassword"]);

_credentials.ChannelAuthTenant = proactiveMessage.AzureEntraTenantId;
var azureToken = await _credentials.GetTokenAsync();

I tried to capture the OAuthScope and the OAuthEndpoint details from the object of MicrosoftAppCredentials and are mentioned below
OAuthScope - https://api.botframework.com

OAuthEndpoint (Specific to the tenant) - https://login.microsoftonline.com/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

Attempt 3:

It was mentioned that in the App Registration if you toggle the Support Types from Single-Tenant to Multi-Tenant and then back to Single-Tenant then it would resolve the issue. However, it didn't work as well.

Reference: https://stackoverflow.com/questions/71031057/microsoft-bot-framework-bot-missing-from-bot-framework-tenant-how-to-add

Attempt 4:

I tried to use the region specific service URL (https://smba.trafficmanager.net/emea) instead of (https://smba.trafficmanager.net/teams/) but still there was an unauthorized exception.

I’d really appreciate any help or guidance you can provide on resolving this issue. Thank you in advance for your time and support!

Microsoft Teams | Development
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Steven-N 11,465 Reputation points Microsoft External Staff Moderator
    2025-09-24T14:10:41.7066667+00:00

    Hi Subramani N

    Thanks for reaching out to Microsoft Q&A forum support

    Based on my research and your description, I determine that the error AADSTS700016 you encountered because of the Bot Framework Connector is trying to validate your bot's App ID in its internal "Bot Framework" tenant, but your bot is registered as a single-tenant app in your own Azure AD. Single-tenant apps are restricted to your tenant, so the Bot Framework (a Microsoft-managed service) can't locate or authenticate it during actions like CreateConversationAsync.

    As I am forum moderator, I have to admit this issue became more common and sometimes has been reported by the user after Microsoft deprecated multi-tenant bot registrations on July 31, 2025, pushing many developers to single-tenant setups. Unfortunately, the Bot Framework SDK doesn’t automatically adjust its token authority logic to handle single-tenant scenarios correctly unless configured explicitly.

    Regarding the solution to this issue, I’d like to offer a direction based on my current understanding and experience. Given the complexity of the authentication flow and the technical depth involved, I may not be able to provide a definitive fix at this stage. However, I encourage you to review the suggested approach and let me know how it works in your environment.

    If these steps don't fully resolve the problem, I'm happy to continue working with you to explore the next steps. We can troubleshoot it further together and refine the solution as we go.

    Option 1: App Registration Updates (Azure Portal):

    • Go to Azure Portal > Microsoft Entra ID > App Registrations > Your App.
    • Under Authentication > Platform configurations, ensure the redirect URI is set to https://token.botframework.com/.auth/web/redirect. This is critical for Bot Framework token exchange.

    Note: No API permissions are needed, as you mentioned, but confirm Supported account types remains "Accounts in this organizational directory only (Single tenant)". If you haven't already, generate a new client secret under Certificates & secrets (the old one might be cached invalidly post-deprecation).

    As you tried in Attempt 3, toggle Supported account types to "Accounts in any organizational directory (multi-tenant)" and save. Wait 5-10 minutes for propagation, then toggle back to "Single tenant" and save again. This registers a shadow entry in the Bot Framework directory, resolving the "not found" error.

    Option 2: Azure Bot Resource Updates:

    • In Azure Portal > Azure Bot > Your Bot > Configuration:
      • Set Microsoft App ID to your App Registration's ID.
      • Set Microsoft App Password to the new client secret.
      • Set App Type to "Single Tenant" (explicitly; don't leave it blank).
      • Under Messaging endpoint, use https://smba.trafficmanager.net/teams/ (global; avoid region-specific like /emea unless your users are exclusively in that region, as it can cause service URL mismatches).
    • Enable the Microsoft Teams channel under Channels.

    Option 3: Code Adjustments for Proactive Messaging (optional)

    I have overviewed your code snippet and seem like it mostly correct, but the MicrosoftAppCredentials needs explicit single-tenant handling to override the default "common" authority (which assumes multi-tenant and hits login.microsoftonline.com/common, triggering the "Bot Framework" directory lookup). Setting ChannelAuthTenant helps for token generation but isn't sufficient for CreateConversationAsync and you need to ensure the entire adapter/credentials stack uses your tenant's authority. I mean, you can try to update your credentials initialization and proactive message logic as follows:

    using Microsoft.Bot.Connector.Authentication;
    using Microsoft.Bot.Builder;
    using Microsoft.Bot.Schema;
    using Microsoft.Extensions.Configuration;
     
    // In your service or controller
    public class ProactiveService
    {
        private readonly IBotFrameworkHttpAdapter _adapter;
        private readonly IConfiguration _configuration;
        private readonly string _botAppId;
        private readonly string _botAppPassword;
        private readonly string _tenantId;  // Your single-tenant ID, e.g., from config or proactiveMessage.AzureEntraTenantId
     
        public ProactiveService(IBotFrameworkHttpAdapter adapter, IConfiguration configuration)
        {
            _adapter = adapter;
            _configuration = configuration;
            _botAppId = _configuration["MicrosoftAppId"];
            _botAppPassword = _configuration["MicrosoftAppPassword"];
            _tenantId = _configuration["MicrosoftAppTenantId"];  // Add this to appsettings.json
        }
     
        public async Task SendProactiveMessageAsync(ProactiveMessage proactiveMessage)  // Assume this is your model
        {
            // Create single-tenant aware credentials with explicit tenant authority
            var appCredentials = new MicrosoftAppCredentials(_botAppId, _botAppPassword);
            appCredentials.AppId = _botAppId;
            appCredentials.Password = _botAppPassword;
            appCredentials.OAuthScope = "https://api.botframework.com";
            appCredentials.OAuthEndpoint = $"https://login.microsoftonline.com/{_tenantId}";  // Tenant-specific authority
            appCredentials.ChannelAuthTenant = proactiveMessage.AzureEntraTenantId;  // User's tenant (same as yours for testing)
     
            // Verify token acquisition (your Attempt 2)
            try
            {
                var token = await appCredentials.GetTokenAsync();
                // Log token for debugging: Console.WriteLine($"Token acquired: {token}");
            }
            catch (Exception ex)
            {
                // Log: Console.WriteLine($"Token error: {ex.Message}");
                throw;  // Re-throw if fails
            }
     
            // Service URL: Use global or region-specific; fetch dynamically if possible from stored conversation ref
            var serviceUrl = "https://smba.trafficmanager.net/teams/";  // Or "https://smba.trafficmanager.net/emea/" if EMEA users
     
            // Conversation parameters (your code is good; ensure TenantId matches)
            var conversationParameters = new ConversationParameters
            {
                IsGroup = false,
                Bot = new ChannelAccount($"28:{_botAppId}"),  // Teams prefix
                Members = new[] { new ChannelAccount(proactiveMessage.UserId) },  // Use user.Id or AAD object ID
                TenantId = proactiveMessage.AzureEntraTenantId  // Must be your single tenant ID
            };
     
            // Create conversation and send (use CloudAdapter if your _adapter is IBotFrameworkHttpAdapter)
            await ((IBotFrameworkHttpAdapter)_adapter).CreateConversationAsync(
                _botAppId,
                Channels.Teams,  // Explicit channel
                serviceUrl,
                appCredentials,  // Pass credentials explicitly
                conversationParameters,
                async (turnContext, cancellationToken) =>
                {
                    var proactiveMessageCard = new Activity
                    {
                        Attachments = new[] { YourAdaptiveCardAttachment },  // Welcome card
                        Type = ActivityTypes.Message
                    };
                    await turnContext.SendActivityAsync(proactiveMessageCard, cancellationToken);
                },
                default);
        }
    }
    

    Option 4: Configure update for environment

    In appsettings.json or environment vars:

    {  
     "MicrosoftAppId": "your-app-id",  
     "MicrosoftAppPassword": "your-new-secret",  
     "MicrosoftAppTenantId": "your-tenant-id"  // Explicitly add this}
     
    

    In your Startup.cs or adapter config:

    services.AddSingleton<MicrosoftAppCredentials>(sp =>
    {    
    var config = sp.GetService<IConfiguration>();    
    var credentials = new MicrosoftAppCredentials(config["MicrosoftAppId"], config["MicrosoftAppPassword"]);    credentials.OAuthEndpoint = $"https://login.microsoftonline.com/{config["MicrosoftAppTenantId"]}";    
    return credentials;
    });
    

    If these steps don't resolve it, share the full stack trace from GetTokenAsync or CreateConversationAsync (redact IDs). This setup should get your same tenant proactives working immediately, paving the way for store publication.

    This summary is based on my findings from the community and several relevant threads. However, it may not accurately reflect the behavior in question. To help you reach your goal more effectively, I recommend posting a thread on the Microsoft Tech Community forum. It’s a great platform for deeper technical discussions and connecting with individuals who have hands-on experience and expertise. They’re best positioned to provide guidance and valuable insights on this topic.   

    Apologies for redirecting you to the related development team support as the moderators in this community have limited resources to check the backend information, and to get the fast and better assistance we requested for it.  

    Best regards and looking forward to hearing from you


    If the answer is helpful, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".     

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

     

    1 person found this answer helpful.

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.