Many organizations want to build applications that can read or manage Microsoft Teams meetings without exposing sensitive mailbox or calendar data. A common question is whether granting OnlineMeetings.ReadWrite.All (application permission) in Entra ID gives an app access to user emails or calendar event bodies. The short answer is: No, it does not.
This post walks through a real-world test scenario, explains how Teams and Exchange permissions differ, and shows how to safely authorize an app to read Teams OnlineMeeting objects while keeping mailbox content fully protected.
I used Microsoft Copilot AI to assist me with technical guidance. Not everything worked as Copilot initially suggested, but I was able to ask follow up questions as I was navigating the issues during configurations/testing. I used Postman to conduct the testing. At the end of all the conversations, I asked Copilot to write a blog post based on the conversations in the context and it did very nicely! Gen AI (Copilot) makes me (and you) more productive than ever before. Copilot makes mistake and sometimes it is because it does not have the right context (or questions), it is our job to validate the answers/responses. In my case, I used my lab tenant to validate the answers.
Teams Online Meetings vs Outlook Calendar Events
When a user creates a meeting in Outlook and adds a Teams link, two separate objects are created:
- Outlook Calendar Event (Exchange Online) Contains subject, body, attachments, notes, and all mailbox content.
- Teams OnlineMeeting Object (Teams Cloud Communications) Contains join URL, meeting options, organizer, start/end time, and metadata.
These two objects look related, but they live in different systems. The OnlineMeetings API only touches the Teams object, not the Outlook calendar event.
What OnlineMeetings.ReadWrite.All Can Do
This permission allows an app to:
- Read Teams online meeting metadata
- Create or update Teams meetings
- Access meeting options (lobby settings, presenters, chat settings, etc.)
It cannot access:
- Emails
- Mailbox folders
- Calendar events
- Calendar event bodies
- Attachments
- Teams chat messages
- Files or SharePoint content
This separation is intentional and enforced by Microsoft Graph.
Why You Need a Teams Application Access Policy
Even with OnlineMeetings.ReadWrite.All granted, Teams will not allow app-only access until you explicitly authorize the app using a Teams Application Access Policy. This is a Teams-specific authorization mechanism and is completely separate from Exchange RBAC.
Without this policy, Graph returns:
No application access policy found for this app on the user
To authorize the app for all users, assign the policy to the global scope: Here is sample logical code and sample code-
New-CsApplicationAccessPolicy -Identity OnlineMeetingsTestPolicy -AppIds "<your-app-id>"
Grant-CsApplicationAccessPolicy -PolicyName OnlineMeetingsTestPolicy -Global
# Install module (if needed)
Install-Module MicrosoftTeams
# Connect to Teams
Connect-MicrosoftTeams
# Create the policy
New-CsApplicationAccessPolicy -Identity OnlineMeetingsTestPolicy -AppIds "4a8f9bfc-d8bc-46ad-9de3-0ed0d0f5323e"
# Assign it to the Global scope
Grant-CsApplicationAccessPolicy -PolicyName OnlineMeetingsTestPolicy -Global
# Assign it to the group scope to limit the app data boundary
# Grant-CsApplicationAccessPolicy -PolicyName OnlineMeetingsTestPolicy -Group "Graph-Demo@aspnet4you2.onmicrosoft.com"
This enables the app to read Teams OnlineMeeting objects for every user in the tenant.
Reading a Teams Online Meeting Using Microsoft Graph
Once the policy is applied, you can query a meeting using the joinWebUrl. For example:Code
GET https://graph.microsoft.com/v1.0/users/<user-object-id>/onlineMeetings?$filter=joinWebUrl eq 'https://teams.microsoft.com/meet/replace-your-meeting-id-here?p=qFL6TTk4mdXTDQ9u1W'
Sample response:
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('26846880-50de-4a3a-8dc1-4bc40a62c7f6')/onlineMeetings",
"value": [
{
"creationDateTime": "2026-07-20T02:59:22.4689156Z",
"startDateTime": "2026-07-20T13:30:00Z",
"endDateTime": "2026-07-20T14:00:00Z",
"joinUrl": "https://teams.microsoft.com/l/meetup-join/{truncated}",
"meetingCode": "262361219403000",
"isBroadcast": false,
"autoAdmittedUsers": "everyoneInCompany",
"joinWebUrl": "https://teams.microsoft.com/l/meetup-join/{truncated}",
"subject": "Online Meetings Test",
"meetingType": "scheduled",
"isEntryExitAnnounced": true,
"isEndToEndEncryptionEnabled": false,
"allowedPresenters": "everyone",
"allowAttendeeToEnableMic": true,
"allowAttendeeToEnableCamera": true,
"allowMeetingChat": "enabled",
"allowedLobbyAdmitters": "organizerAndCoOrganizersAndPresenters",
"shareMeetingChatHistoryDefault": "none",
"allowTeamworkReactions": true,
"anonymizeIdentityForRoles": [],
"recordAutomatically": false,
"allowParticipantsToChangeName": false,
"allowTranscription": true,
"allowRecording": true,
"allowWhiteboard": true,
"allowBreakoutRooms": true,
"allowLiveShare": "enabled",
"allowPowerPointSharing": true,
"allowCopyingAndSharingMeetingContent": true,
"meetingOptionsWebUrl": "https://teams.microsoft.com/meetingOptions/?organizerId={truncated}",
"iCalUid": "{truncated}",
"expiryDateTime": "2026-09-18T14:00:00Z",
"cloudVideoInteropInfo": {},
"id": "{truncated}",
"participants": {
"organizer": {
"upn": "Paul.Smith@aspnet4you2.onmicrosoft.com",
"role": "presenter",
"identity": {
"user": {
"@odata.type": "#microsoft.graph.communicationsUserIdentity",
"id": "26846880-50de-4a3a-8dc1-4bc40a62c7f6",
"tenantId": "c33386cf-6e11-484c-a983-b49975ce571a"
}
}
},
"attendees": [
{
"upn": "Bob.Smith@aspnet4you2.onmicrosoft.com",
"role": "attendee",
"identity": {
"user": {
"@odata.type": "#microsoft.graph.communicationsUserIdentity",
"id": "24e66b3d-33a7-4639-9dbc-f1d61883c9d4",
"tenantId": "c33386cf-6e11-484c-a983-b49975ce571a"
}
}
}
]
},
"chatInfo": {
"threadId": "19:meeting_OWRlZTM5ZGQtMDc4Yy00ZGUwLWE2YzUtYmE0OTcyZDVlY2Y2@thread.v2",
"messageId": "0"
},
"joinInformation": {
"content": "data:text/html,%3cdiv+style{truncated}",
"contentType": "html"
},
"joinMeetingIdSettings": {
"isPasscodeRequired": true,
"joinMeetingId": "{truncated}",
"passcode": "{truncated}"
},
"lobbyBypassSettings": {
"scope": "organization",
"isDialInBypassEnabled": false
}
}
]
}
If Teams created an OnlineMeeting object, Graph returns full metadata including:
- subject
- startDateTime / endDateTime
- organizer and attendees
- meetingCode and passcode
- meetingOptionsWebUrl
- allowedPresenters
- chatInfo
- joinMeetingIdSettings
This confirms the app can read Teams meeting metadata safely.
Testing Mailbox Access: Why It Fails (And Why That’s Good)
Using the same access token, we attempted to read email:Code
GET https://graph.microsoft.com/v1.0/users/Paul.Smith@aspnet4you2.onmicrosoft.com/messages
Graph returned:
ErrorAccessDenied
This proves:
- The app has no Exchange Online permissions
- OnlineMeetings.ReadWrite.All does not grant mailbox access
- Teams Application Access Policies do not affect email or calendar
- Exchange RBAC protections remain fully intact
This is exactly the isolation most organizations want.
Final Summary
By combining:
- OnlineMeetings.ReadWrite.All (Teams permission)
- Teams Application Access Policy (Global scope)
You can safely allow an app to read Teams OnlineMeeting objects for all users without exposing:
- emails
- calendar bodies
- attachments
- mailbox folders
This architecture provides strong separation between Teams meeting metadata and Exchange mailbox content, enabling secure meeting governance, compliance, and automation scenarios.
