Engineering · 14 min read
The Complete Microsoft Graph API Permissions Guide for Teams Meeting Apps
A reference for developers building Teams meeting integrations and the IT admins approving them. The permissions are not complicated once you understand the two consent models — but the documentation is scattered, the error messages are misleading, and the wrong choice can mean weeks of rework before AppSource approval. This is the guide we wish existed when we built CallScrib.
TL;DR for developers and admins
If you're building a Teams meeting app, three things to settle before you start:
1. Pick a consent model. Resource Specific Consent (RSC) for meeting-scoped, install-per-chat apps. Application permissions for tenant-wide, admin-installed apps that need to read meetings the app was never invited to.
2. If you choose application permissions, you need an Application Access Policy. Tenant admin consent alone is not enough. The PowerShell step is the one most developers miss and the source of most 403 errors against the transcript endpoint.
3. Transcripts are SharePoint files, not Graph entities. `transcriptContentUrl` returns a SharePoint URL that needs its own auth path. A working Graph token does not automatically work against SharePoint.
The rest of this article unpacks each of these and the specific permission names you'll request. For the strategic context on why bot-based notetakers are losing ground to Graph API integrations, see our Microsoft Teams 2026 bot ban admin guide.
The AI notetaker built for Microsoft Teams. No bot in your meetings. 7 meetings per month free, no credit card.
The two consent models, side by side
Microsoft Graph exposes Teams meeting data under two different consent models. They look similar in the permission picker, but they behave differently at runtime and they have very different review burdens for AppSource.
Resource Specific Consent (RSC). Permissions are granted at the chat or meeting level. When a user adds your app to a meeting chat, the install flow asks for permissions scoped to that chat only. No tenant admin needs to be involved. Permission names end in `.Chat` — for example `OnlineMeetingTranscript.Read.Chat`, `ChatMessage.Send.Chat`, `OnlineMeeting.ReadBasic.Chat`. Your app can read transcripts and post messages in chats where it's installed, and nowhere else.
Application permissions. Permissions are granted tenant-wide by an M365 administrator. Once consent is given, your app can read meetings and transcripts across the entire tenant — for any user, any meeting, any time. Permission names end in `.All` — for example `OnlineMeetingTranscript.Read.All`, `OnlineMeetings.Read.All`, `Calendars.Read`. Your app calls Graph as itself (client credentials flow), not on behalf of a user.
Pick RSC if your app is installed per-chat by end users, summaries only need to appear in the chat where the app lives, and you want to avoid requiring tenant admin involvement at install time.
Pick application permissions if your app needs to process meetings that happened before it was installed, you need tenant-wide coverage without users adding the app to every chat, or you're integrating with a CRM or data warehouse that reads transcripts in bulk.
The wrong choice is hard to reverse: switching from RSC to application permissions after launch means an entirely new admin consent flow and a new round of AppSource review. Pick once, deliberately. See our Graph API vs bot framework breakdown for the architectural context.
The core meeting transcript permissions
These are the permissions you'll actually request to read Teams meeting transcripts. Each has a `.Chat` (RSC) and `.All` (application) variant, and they are NOT interchangeable — the variant you request determines which endpoints and which meetings your app can see.
`OnlineMeetingTranscript.Read.Chat` (RSC). Read the transcript of meetings in chats where your app is installed. This is what most consumer-grade Teams meeting apps need. Granted at install time by any chat member who has admin rights for the chat.
`OnlineMeetingTranscript.Read.All` (Application). Read transcripts of all meetings in the tenant. Granted once by a tenant admin. Requires an Application Access Policy (next section) before any actual transcript read will succeed.
`OnlineMeeting.ReadBasic.Chat` (RSC). Read basic meeting metadata — title, start time, end time, organizer — for meetings in installed chats. Almost always paired with the transcript permission so you can label the summary.
`OnlineMeetings.Read.All` (Application). Read meeting metadata across the tenant. Required alongside `OnlineMeetingTranscript.Read.All` if you want to know what meeting you're reading.
`ChatMessage.Send.Chat` (RSC). Send an Adaptive Card or message into the chat where your app is installed. Required to post summaries back to the meeting chat. No application-permission equivalent exists — to post into a chat from a tenant-scoped app, you use the bot framework, not Graph.
`CallRecords.Read.All` (Application). Read Teams Phone call records. Needed for PSTN call summarization and only available as an application permission. There is no `CallRecords.Read.Chat` because PSTN calls are not chat-scoped.
`Calendars.Read` (Application). Read calendar events tenant-wide. Needed if you want to read upcoming meetings, pre-fetch organizer info, or set up `recordAutomatically: true` on scheduled meetings before they start. Frequently the first permission a reviewer questions because the name sounds broader than it is — be specific in your privacy policy about what you read and why.
Application Access Policy — the step that breaks most integrations
If you go the application-permission route, granting tenant admin consent is necessary but not sufficient. Without an Application Access Policy, every request to read a meeting transcript returns:
`403 Forbidden — Application is not allowed to access this resource.`
This is the single most common reason a Graph integration appears to work in dev and fails in production. The admin clicks consent, the OAuth flow succeeds, the token is issued — and every transcript read still fails. The missing piece is a tenant-side PowerShell configuration that explicitly authorizes your app id to call meeting endpoints on behalf of users.
What the policy does. It tells Microsoft Teams that your registered application is allowed to call the OnlineMeeting and OnlineMeetingTranscript endpoints for the specified users (or all users). Without it, the consent grant is recognized at the directory level but the actual API call is rejected at the service level.
Creating the policy (admin runs once). From a PowerShell session with the MicrosoftTeams module:
`Install-Module -Name MicrosoftTeams -Force`
`Connect-MicrosoftTeams`
`New-CsApplicationAccessPolicy -Identity "CallScrib-Policy" -AppIds "<your-app-id>" -Description "Grant CallScrib access to meeting transcripts"`
`Grant-CsApplicationAccessPolicy -PolicyName "CallScrib-Policy" -Global`
Replace `<your-app-id>` with the Entra app registration's client id, not the Teams app id. The `-Global` switch applies the policy to all users in the tenant; you can scope to specific users with `-Identity <UserUPN>` instead.
Propagation. The policy takes effect within minutes but Microsoft documents up to 30 minutes in some tenants. If the 403 persists after granting, wait, then retry — do not assume the policy didn't apply.
Reviewer-facing implication. Document the Application Access Policy step prominently in your admin setup guide. Microsoft AppSource reviewers explicitly test that an admin can install your app, follow your documentation, and get to a working state. Apps whose docs skip this step regularly fail certification on rule 1140.4.1.17 (admin can complete setup unaided).
transcriptContentUrl and the SharePoint auth trap
When you call `GET /communications/onlineMeetings/{id}/transcripts/{transcriptId}/content`, Graph doesn't return the transcript text directly. It returns a redirect (or a metadata object containing `transcriptContentUrl`) that points to a SharePoint or OneDrive file. This is genuinely confusing because Microsoft does not surface anywhere that transcripts are SharePoint files behind the scenes.
What this means for your code. A bearer token scoped to `https://graph.microsoft.com/.default` may not be accepted by SharePoint. Depending on the redirect behavior and your HTTP client, you may need a separate token scoped to `https://<tenant>.sharepoint.com/.default` or to follow the redirect with credentials that include the appropriate SharePoint permission grant.
Permissions you may also need. For some tenant configurations, reading the transcript content from SharePoint requires `Files.Read.All` or `Sites.Read.All` as application permissions on top of the meeting-transcript permission. The redirect itself succeeds; the content read fails with another 403 if these are missing.
Symptoms to watch for. The Graph call to list transcripts succeeds. The Graph call to fetch the transcript content returns a 302 redirect (or a content URL). Following that URL with your Graph token returns `403 Permission denied` or an HTML SharePoint sign-in page. The fix is almost always either a SharePoint-scoped token or a Files permission grant, not anything to do with the meeting-transcript permission itself.
If you're using the official `@microsoft/microsoft-graph-client` SDK, the redirect is handled transparently in most cases — but in serverless environments where you control the HTTP layer (Azure Functions, Lambda), you may need to handle the redirect yourself and re-acquire a token for the redirect target.
Graph API native, no bot, summaries posted to Teams chat in 60 seconds. From $5 per user per month.
Start free trialA decision tree for choosing permissions
When a developer asks "what permissions do I need?" the honest answer is: it depends on three questions. Walk through them in order.
Question 1: Is your app installed per-chat by end users, or tenant-wide by an admin?
Per-chat → RSC permissions (`.Chat` suffix). Tenant-wide → application permissions (`.All` suffix). If both, build two manifests; do not try to declare both in one.
Question 2: Do you need to process meetings that happened BEFORE your app was installed?
Yes → application permissions, no exceptions. RSC cannot reach meetings that pre-date the install.
No → either model works; RSC is preferable for the lower review burden.
Question 3: Do you need to summarize PSTN phone calls (Teams Phone), not just scheduled meetings?
Yes → application permissions, specifically `CallRecords.Read.All` and `OnlineMeetingTranscript.Read.All`. No RSC equivalent exists for phone calls.
No → RSC remains viable if questions 1 and 2 also allowed it.
Most successful Microsoft 365 meeting apps end up on the application-permission path because the answer to question 2 or 3 is "yes." It's the higher-friction path at install time, but it's the only one that delivers comprehensive coverage. RSC is cleaner for a "summarize this specific meeting" tool but limiting for anything more ambitious. For setup-side context, our Teams meeting transcription guide covers the admin steps that happen before any of these permissions matter.
Common 403 errors and what they actually mean
Microsoft's 403 messages on meeting endpoints are not always literal. Here are the four most common ones and the root cause behind each.
`Application is not allowed to access this resource.` This is the missing Application Access Policy, 95% of the time. Tenant admin consent is granted but no `New-CsApplicationAccessPolicy` was created or granted for your app id. Run the PowerShell from the section above.
`Permission denied.` This is usually the SharePoint trap on `transcriptContentUrl`. The initial Graph call succeeded; you followed the content URL with a Graph token that SharePoint doesn't accept. Acquire a SharePoint-scoped token or add `Files.Read.All`.
`Forbidden — the user does not have access to the requested resource.` When using delegated permissions (less common for transcript reads), the signed-in user does not have access to the meeting. This is correct behavior — you cannot delegate access the user doesn't have. Switch to application permissions if you need a service-account model.
`Forbidden — request is throttled.` Despite the 403 label, this is a rate limit, not a permission problem. Microsoft Graph has per-app and per-tenant throttling on meeting endpoints. Implement exponential backoff and honor the `Retry-After` header. Throttling shows up disproportionately in load tests and CI integration tests because the request rate is artificially compressed.
When debugging, the single most useful diagnostic is the `request-id` (or `client-request-id`) header in the failed response. Microsoft Support can trace by that id; "I'm getting 403s" without it is a much slower conversation.
Permission comparison table
A reference for what each permission grants and which scope it lives in.
`OnlineMeetingTranscript.Read.Chat` — RSC — Read transcripts of meetings in installed chats — No admin consent — No Application Access Policy required
`OnlineMeetingTranscript.Read.All` — Application — Read all transcripts tenant-wide — Tenant admin consent required — Application Access Policy required
`OnlineMeeting.ReadBasic.Chat` — RSC — Read meeting metadata in installed chats — No admin consent — No Application Access Policy required
`OnlineMeetings.Read.All` — Application — Read meeting metadata tenant-wide — Tenant admin consent required — Application Access Policy required
`ChatMessage.Send.Chat` — RSC — Post into installed chats — No admin consent — No Application Access Policy required
`CallRecords.Read.All` — Application — Read Teams Phone call records — Tenant admin consent required — No Application Access Policy required
`Calendars.Read` — Application — Read calendar events tenant-wide — Tenant admin consent required — No Application Access Policy required
`Files.Read.All` (sometimes needed) — Application — Required if `transcriptContentUrl` redirects to SharePoint and your Graph token is rejected — Tenant admin consent required — No Application Access Policy required
Notice that not every application permission requires an Application Access Policy. The policy is specifically for `OnlineMeeting*` endpoints. `CallRecords.Read.All` and `Calendars.Read` work with consent alone.
How CallScrib uses these permissions
Disclosure since this is the CallScrib blog. CallScrib uses Resource Specific Consent for in-chat operation (summaries posted to the meeting chat where the app is installed) and application permissions for tenant-wide meeting and phone call coverage. The specific permissions we declare:
RSC permissions (granted per chat at install time). `OnlineMeetingTranscript.Read.Chat` for reading the transcript of the meeting the chat belongs to. `ChatMessage.Send.Chat` for posting Adaptive Card summaries back into the chat. `OnlineMeeting.ReadBasic.Chat` for the meeting title and timing on the summary card.
Application permissions (granted once by tenant admin). `OnlineMeetingTranscript.Read.All` for tenant-wide meeting transcript reads (used when an organization installs CallScrib tenant-wide rather than per chat). `CallRecords.Read.All` for Teams Phone call summaries. `Calendars.Read` for the auto-record scheduling feature that allows an admin to opt into recording every scheduled meeting tenant-wide.
We maintain an Application Access Policy on customer tenants as part of the documented install process. Our admin setup page walks the M365 admin through the two PowerShell commands. Customers without an admin willing to run PowerShell can still use the RSC mode by installing CallScrib per chat — at the cost of per-meeting install friction.
If you're evaluating CallScrib specifically, see our pricing and setup guide. If you're building your own integration and this article was useful, the Microsoft Learn permission reference is the canonical source for everything above — we just translated it into the parts that actually matter when you're shipping.
Frequently Asked Questions
What is OnlineMeetingTranscript.Read.Chat?
OnlineMeetingTranscript.Read.Chat is a Resource Specific Consent (RSC) permission that allows a Teams app to read the transcript of meetings in chats where the app is installed. It is granted per chat at install time and does not require tenant admin consent.
What is the difference between OnlineMeetingTranscript.Read.Chat and OnlineMeetingTranscript.Read.All?
Read.Chat is an RSC permission scoped to chats where the app is installed; users install the app per chat. Read.All is an application permission scoped to the entire tenant; a tenant admin grants it once. Read.All also requires an Application Access Policy before transcript reads will succeed.
What is Resource Specific Consent (RSC)?
Resource Specific Consent is Microsoft's consent model for Teams apps where permissions are granted at the chat, channel, or team level rather than tenant-wide. RSC permissions end in .Chat, .Channel, or .Team. They are granted by a chat owner at install time without requiring a Microsoft 365 admin.
What is an Application Access Policy and do I need one?
An Application Access Policy is a tenant-side PowerShell configuration that explicitly authorizes a Microsoft 365 application to call OnlineMeeting and OnlineMeetingTranscript endpoints. You need one if you use application permissions (the .All variants) for meeting transcripts. Without it, every call returns 403 Forbidden even after tenant admin consent is granted.
Why am I getting a 403 on transcriptContentUrl?
The 403 on transcriptContentUrl is almost always because transcripts are stored as SharePoint files. The redirect succeeds but your Microsoft Graph token is rejected by SharePoint. You need either a SharePoint-scoped token or the Files.Read.All application permission, depending on the tenant configuration.
Do RSC permissions require tenant admin consent?
No. RSC permissions are granted at install time by the chat owner installing the app. This is the main reason to choose RSC over application permissions when possible — there is no tenant admin gate, which dramatically reduces install friction.
Can I use Microsoft Graph meeting permissions without publishing to AppSource?
Yes for internal apps installed via sideloading in your own tenant, with full admin consent. For multi-tenant distribution to customers, AppSource publishing is effectively required because customers expect the AppSource trust signals (Publisher Attestation, certification badge) before granting tenant-wide consent.
Does Microsoft 365 Copilot use the same Graph API permissions?
Microsoft 365 Copilot uses Microsoft-internal APIs that are not available to third-party developers. The public OnlineMeetingTranscript permissions are what third-party apps like CallScrib use to deliver similar functionality without requiring a Copilot license.
How long does the Application Access Policy take to propagate?
Microsoft documents up to 30 minutes for full propagation, though most tenants see the policy take effect within 1 to 5 minutes. If you see 403 errors immediately after granting, wait and retry rather than assuming the policy failed to apply.
Are there separate permissions for Teams Phone (PSTN) calls?
Yes. Teams Phone call records are accessed via CallRecords.Read.All (application permission only — no RSC equivalent exists for PSTN). For transcripts of PSTN calls, OnlineMeetingTranscript.Read.All is still the correct permission, because Teams Phone calls produce online-meeting-shaped transcript records in Microsoft Graph.
Does my app need Calendars.Read?
Only if you need to read upcoming meetings before they occur — for example, to set recordAutomatically:true on scheduled meetings, or to pre-fetch organizer information. If your app only processes meetings after they end (the common case), you do not need Calendars.Read.