I flipped the new transcript API kill-switch for a week. Here’s what broke.

Microsoft's new tenant control blocks Graph API access to Teams transcripts regardless of app permissions. I tested it for a week in a pilot tenant to see what would break—and what you need to…

The first thing that died was a Power Automate flow nobody remembered building. It summarised every recorded project standup and dropped the notes into a SharePoint list. Ran fine for eighteen months. Then, roughly ten minutes after I flipped the new tenant setting, it started throwing 403 Forbidden on the transcript call — and the app permission it depended on was still sitting there, granted and green in Enterprise Applications.

That’s the whole story of this control in one sentence. It doesn’t care about your app permissions. It sits in front of them.

Office365itpros surfaced it on September 21, and the moment I read the writeup I wanted to know one thing the announcement wasn’t going to tell me: what actually falls over when you say no. So I stood it up in a pilot tenant with a handful of real integrations pointed at it and turned it off for API access. A week of field notes follows.

What the switch is, and what it very much is not

This is a binary, tenant-wide gate on programmatic access to Teams meeting transcripts through Microsoft Graph. Block it, and Graph calls to the transcript endpoints stop returning content for the whole tenant. Allow it, and you’re back to the world you already knew, where app permissions decide who gets in.

What it does not touch, and I checked this before I trusted it:

  • Humans opening a transcript in the Teams client. Meeting organisers and attendees read transcripts through the UI exactly as before.
  • Whether transcription happens at all. Transcripts are still generated and stored per your existing meeting policies. This gate is about reading them via API, not creating them.
  • eDiscovery and Purview surfaces that don’t route through the Graph transcript APIs.

So if someone in a governance meeting says “great, we’ve stopped transcripts leaking,” correct them. You’ve stopped one path — the API path. The transcript file still exists, still lives in the organiser’s OneDrive, still shows in the client. This is an access-control change, not a data-retention change. Conflating the two is how you end up with a false sense of security and an auditor who is not amused.

How it layers with the permissions you already granted

Here’s the mental model that made it click for me. Before this, transcript API access was a single check: does the app hold OnlineMeetingTranscript.Read.All (application) or OnlineMeetingTranscript.Read.Chat (delegated, chat-scoped) with admin consent? Pass, and you’re in.

Now there are two gates in series. The tenant switch is the first. The permission is the second. Both must be satisfied. The switch being set to block is an override that no amount of consent will beat — which is exactly the point, and exactly what surprised the flow owner who was convinced their app was “approved so it should work.”

App consent answers “is this app allowed?” The tenant switch answers “is anything allowed?” You can win the first and still lose.

On the PowerShell surface: as of writing I could toggle it in the Teams admin center, but I could not confirm a documented CsTeams* cmdlet or Graph settings endpoint that flips it cleanly, so I’m not going to hand you an invented cmdlet name and let you script against a ghost. Check the current Teams PowerShell reference before you build automation around it. Toggle it in the portal for now, and treat the audit — which is fully scriptable — as the part worth automating.

The audit I wish I’d run before touching anything

I turned the thing on first and then went looking for what I’d broken. Do it the other way around. The useful inventory question isn’t “who called the transcript API last Tuesday” — sign-in logs won’t tell you the endpoint. It’s “which service principals hold the permissions that would let them,” and that’s a clean query against Graph.

This finds every app in your tenant granted either transcript permission on Microsoft Graph. Read-only, paginated, with the error handling you actually need when a tenant has thousands of service principals:

audit.ps1PowerShell
# Requires: Microsoft.Graph
# Read-only inventory. No -WhatIf needed - this changes nothing.
Connect-MgGraph -Scopes "Application.Read.All","Directory.Read.All" -NoWelcome
​
$graphAppId = "00000003-0000-0000-c000-000000000000"  # Microsoft Graph
$targetRoles = @("OnlineMeetingTranscript.Read.All","OnlineMeetingTranscript.Read.Chat")
​
try {
    $graphSp = Get-MgServicePrincipal -Filter "appId eq '$graphAppId'" -ErrorAction Stop
}
catch {
    Write-Error "Could not resolve the Microsoft Graph service principal: $($_.Exception.Message)"
    return
}
​
# Map the app-role GUIDs we care about
$roleMap = @{}
foreach ($role in $graphSp.AppRoles) {
    if ($targetRoles -contains $role.Value) { $roleMap[$role.Id] = $role.Value }
}
​
# Who has been assigned those roles? Paginate with -All.
try {
    $assignments = Get-MgServicePrincipalAppRoleAssignedTo `
        -ServicePrincipalId $graphSp.Id -All -ErrorAction Stop
}
catch {
    Write-Error "Failed to read app role assignments: $($_.Exception.Message)"
    return
}
​
$results = foreach ($a in $assignments) {
    if ($roleMap.ContainsKey($a.AppRoleId)) {
        [pscustomobject]@{
            AppDisplayName = $a.PrincipalDisplayName
            ServicePrincipalId = $a.PrincipalId
            Permission = $roleMap[$a.AppRoleId]
            AssignedOn = $a.CreatedDateTime
        }
    }
}
​
$results | Sort-Object AppDisplayName | Format-Table -AutoSize
$results | Export-Csv .\transcript-api-consumers.csv -NoTypeInformation

That CSV is your blast radius. Every row is something that will start returning 403 the moment you block. Delegated OnlineMeetingTranscript.Read.Chat usage is trickier — it rides on interactive or on-behalf-of flows — so also pull service-principal sign-ins from the Entra sign-in logs (Get-MgAuditLogSignIn, filtered to app activity against the Graph resource) to catch anything the app-role query misses. Then, and only then, do you get to have the block-or-allow conversation.

What the week actually taught me

Three things broke that mattered, in descending order of “who’s going to phone me.”

The forgotten Power Automate flow, already mentioned — low stakes, but a perfect illustration of how these dependencies hide. Second: a third-party meeting-analytics tool that scored “talk time” and action items. It degraded quietly, no error to the end user, just stale dashboards, which is worse than a loud failure because nobody notices for days. Third, and this is the one that changed my recommendation: an internal Copilot-style agent that pulled transcripts to draft follow-up emails. It didn’t error either. It just started drafting from thinner context and produced worse output, and the people using it assumed the model had gotten dumber.

That’s the pattern to sit with. Human access looks completely healthy the whole time. Everyone can still open transcripts in Teams. The failures are all downstream, silent, and land on the people least equipped to diagnose a 403 they never see.

Do I think you should block it? For a lot of tenants, honestly, yes — transcript content is among the most sensitive data you hold, and an API gate that no consent grant can bypass is a genuinely good governance primitive. But block-by-default without the inventory is how you take out a compliance archiving pipeline and find out during an audit. Run the query first. Look at every row. For each one, decide: is this app trusted enough to keep, or is this exactly the exposure the switch exists to close?

If I were doing it again in production, I’d run the audit, socialise the CSV with the app owners, give them two weeks to justify their dependency, and only then flip it. The switch is fast. Cleaning up after flipping it blind is not.