Treat the initial BotDisabledByAdmin as a blocked delivery, retain the conversation reference, and—only for a newly initiated admin rollout—queue a deduplicated, bounded delayed retry while separately checking installation and user-level app availability.
Microsoft documents BotDisabledByAdmin as meaning that an administrator has blocked interactions between that user and the bot. The formal status-code guidance classifies this 403 as non-retryable and says to stop posting until the user explicitly interacts with the bot ^1^.
However, Microsoft also says app-availability changes can take up to 24 hours to take effect. In rare cases, the change can take up to six days to appear in the client ^2^. Your per-user failures immediately after large rollouts, followed by success without an admin change, are therefore consistent with propagation, but the documentation does not guarantee that an installationUpdate/add event and proactive-message authorization become effective atomically.
Can the two cases be distinguished?
Not from BotDisabledByAdmin alone. The same response does not expose whether the evaluated policy is the intended final policy or a still-propagating service state.
Use these independent checks to make propagation more or less likely:
- Check personal installation with Microsoft Graph.
GET https://graph.microsoft.com/v1.0/users/{user-id}/teamwork/installedApps
?$expand=teamsApp
&$filter=teamsApp/id eq '{teamsAppId}'
- Empty array: Teams does not currently report the app installed for that user.
- One installation object: installation is visible, but this does not prove that user-level availability has propagated to the messaging authorization path.
This requires an appropriate Graph permission; the least-privileged application permission documented for listing another user’s installations is TeamsAppInstallation.ReadForUser.All, with tenant-admin consent ^3^. Use the catalog-generated teamsAppId, not automatically the manifest ID. For an organizational catalog app, these can differ; Microsoft documents how to retrieve and use the catalog ID ^4^.
- Have a Teams administrator check effective availability.
- Teams admin center → Manage apps → your app → Users and groups → Availability
- Also check Manage users → affected user → Apps.
If the tenant still uses permission policies instead of app-centric management, verify the user’s assigned app permission policy. Setup/install policies and permission/access policies are separate controls ^5^.
- Interpret the combined state cautiously.
|
Graph installation |
Admin availability |
Likely interpretation |
|
Missing |
Allowed |
Installation or rollout has not completed |
|
Present |
Not allowed |
Genuine effective block |
|
Present |
Allowed, rollout recent |
Propagation/internal-state inconsistency is plausible |
|
Present |
Allowed, older than expected window |
Escalate; neither public check explains the 403 |
These checks cannot conclusively separate propagation from an internal service-state inconsistency because there is no documented API that returns “availability propagation pending.”
Bounded retry for rollout-triggered failures
Because this case shows later success and the rollout is recent, a delayed retry is a practical workaround reported for this scenario, but it is not the formal retry contract for 403 responses ^6^.
A reasonable application policy is:
- Attempt the immediate welcome once.
- On exactly
403/BotDisabledByAdmin, retry at approximately 15 minutes, 1 hour, 6 hours, and 24 hours.
- Deduplicate by tenant, user, app version, and installation event.
- Stop after success, uninstall, or any user interaction.
- Do not indefinitely retry; after the bounded window, mark the welcome pending and escalate persistent cases.
const delays = [15 * 60, 60 * 60, 6 * 3600, 24 * 3600];
async function handleWelcome(job: WelcomeJob) {
try {
await sendWelcome(job.conversationReference);
await markDelivered(job.key);
} catch (e) {
if (!isBotDisabledByAdmin(e) || job.attempt >= delays.length) throw e;
await enqueueOnce(job.key, {
...job,
attempt: job.attempt + 1
}, delays[job.attempt]);
}
}
Continue using the serviceUrl, tenant ID, and conversation data captured from the incoming installation activity. Teams explicitly supports caching conversation information from installation events for later proactive messages ^7^.
For failures persisting beyond the expected rollout period despite Graph showing the installation and the admin center showing availability, open a Microsoft support case with timestamps, tenant/user correlation identifiers, MS-CV or request IDs, rollout time, app catalog ID, and the full Connector response.
References
- Send and receive messages (teams-sdk-csharp)
- Use app centric management to manage access to apps
- List apps installed for user
- Proactively install your bot for users using Microsoft Graph
- Know how to manage access to and install Teams apps
- Bot Service: Teams Channel Listing Returns 403 BotDisabledByAdmin ...
- Send proactive messages - Teams | Microsoft Learn