POWER AUTOMATE + MICROSOFT GRAPH
Less guesswork.
Better Teams reporting.
A carefully scoped, read-only recipe book for team settings, channels, membership, apps, meetings and usage reports. Start with one successful GET, then build a report you can trust.
START SMALL, THEN SCALE
Beginner setup: a scheduled flow and HTTP
Try one user's joined teams first. The JSON foundation below applies to JSON recipes only; recording bytes, VTT and CSV have their own paths.
1. Pick an authorization route, not just a permission
Application / app-only: appropriate for unattended scheduled flows. Register an app in the same tenant/cloud, add only the recipe's relevant Application permission and obtain admin consent. Use /users/{userId}, never /me. A read permission does not bypass Teams policy or membership boundaries.
Delegated: an approved signed-in connection/custom connector with delegated scopes and user access; not the client-secret OAuth setup below. Admin consent/roles can still be required. Personal Microsoft accounts are not supported by these recipes.
Resource-specific consent (RSC): usually the narrowest application route for one team/chat, but requires a Teams app installation/manifest and resource-owner consent. *.Read.Group/*.Read.Chat is not a normal permission to add to an Entra app registration. The ordinary HTTP app-only guide uses the listed non-RSC permission; see RSC documentation.
Meeting APIs: app-only *.Read.All also needs an organizer-scoped application access policy configured by a tenant administrator. Allow up to 30 minutes for policy propagation.
2. Create the schedule and authenticated HTTP GET
- Power Automate: Create > Scheduled cloud flow. Start weekly with a restricted test recipient. HTTP commonly needs Premium licensing and must be allowed by your environment's DLP policy.
- Add the approved HTTP action, often the standard HTTP action with Active Directory OAuth. Connector screens differ; consult the exact connector docs if these fields are missing.
- Method:
GET. URI: the recipe's full URL after replacing placeholders. Body: empty. Queries: empty when already in the URI. For JSON recipes only, headerAccept: application/json. - Authentication: Active Directory OAuth; Authority: cloud-matched login host; Tenant: Directory tenant ID; Audience: Graph base URL only (no
/v1.0); Client ID: app client ID. - Use an approved credential/connection, certificate if supported. If a secret is required, retrieve its Value (not its ID) from an approved vault. Turn on Secure Inputs/Outputs for secret-bearing actions. Never put credentials in Compose, URLs, public source or screenshots; don't add a manual bearer token when OAuth is configured.
Select the cloud in the catalog to see its matched Graph audience and Authority. Worldwide and GCC use commercial hosts; GCC High and DoD do not. Availability must be checked per API, not inferred by changing the hostname.
3. Compose a sample, then Parse JSON
- Add Compose after HTTP; Inputs = HTTP Body, or
body('HTTP'). Save and test HTTP + Compose first. - Open Run history > Compose Outputs. Use a private sample to generate the Parse JSON schema; never paste tenant data in public tickets.
- Add Parse JSON: Content = HTTP Body; Schema = Generate from sample. Optional/absent/null properties need a schema that allows them. Empty samples cannot infer all fields.
- For collections use Apply to each on
body('Parse_JSON')?['value'], not the whole object.item()is one record; append only that record or selected fields. - For single objects, use properties directly without a value loop. Use Select and Create HTML table only on approved, minimized report columns. Remove the temporary Compose after schema testing.
Action names in expressions must match your flow. Output containing HTML is not trusted markup; don't render arbitrary Teams content as executable HTML.
4. Follow every page; don't report partial counts
- Initialize string
nextLinkwith the first full URI and an array for report rows outside the loop. - Add Do until
equals(variables('nextLink'), ''). Inside it, GETvariables('nextLink'), parse the current JSON page, and process itsvaluerecords. - After processing, set
nextLinktocoalesce(body('HTTP')?['@odata.nextLink'], ''). Use the returned URL as-is; do not rebuild its skip token or re-add queries. - Set a bounded iteration count/timeout appropriate to your data; run the first page before checking completion. If the loop reaches its limit with a nonempty nextLink, fail/notify explicitly instead of sending a 'complete' report.
Validate that nextLink is HTTPS on the expected Graph cloud host before sending credentials. Keep variable-updating loops sequential; don't enable parallel Apply to each when appending shared arrays. Honor Retry-After on 429; bound retries on transient 503/504.
5. Meeting lookup > metadata > content
Use an existing invite's join URL + organizer ID to resolve the onlineMeeting. Pass the returned Graph id to list recordings or list transcripts, then pass an artifact ID to a content recipe. Calendar event, chat, joinMeetingId and onlineMeeting IDs are not interchangeable.
No JSON parser for content: recordings return media bytes; transcripts return WebVTT/text. The activity report redirects to CSV. Save bytes/text with approved restricted file actions; inspect the actual connector representation and payload/timeout limits.
Recording/transcription must have been allowed and produced. Live events and standalone onlineMeeting-created meetings without calendar events are excluded here. Current transcript APIs support channel meetings with OnlineMeetingTranscript.Read.All + an application access policy; OnlineMeetingTranscript.Read.Chat does not.
6. Troubleshoot without widening access blindly
- 401
- Check tenant, client, credential Value/expiry, Audience, Authority and cloud. Check the documented new-tenant issue for team members.
- 403
- Check the exact permission type, consent, user access/admin role and organizer application access policy.
GraphAccessToTranscriptsDisabledrequires an admin decision;SpeakerAttributionNotAllowedpermits only the unattributed Accept format. Don't bypass tenant controls. - 404 / empty value
- Check cloud support, raw/encoded IDs, organizer context, exact join URL, calendar association, expiry and artifact existence. Empty data is not necessarily an error or evidence of no activity.
- 429 / 503 / 504
- Honor Retry-After and use bounded exponential retries. Notify the flow owner after failure; don't turn an error into an empty successful report.
- 302 / parser failure
- Inspect Content-Type and report redirect behavior. CSV, HTML, VTT and media bytes aren't JSON. Never send Graph authorization to a preauthenticated report-download host.
7. Security, licensing and cloud preflight
Keep app scope small; restrict flow co-owners, run-history access, report recipients and storage. Secure Inputs/Outputs can hide samples during diagnosis; sample safely, then protect production. Track credential rotation, retention and flow-failure ownership. This public site must contain no real tenant data or flow exports with secrets.
Confirm Power Automate Premium/connector licensing, Teams organizer entitlement, recording/transcription policy, data residency and DLP with your admins. No blanket Teams Premium or E5 prerequisite is claimed for these APIs; API permissions alone do not license a meeting feature.
Metering changed: Microsoft states Teams APIs stopped being metered on August 25, 2025; no Teams API billing setup is required. Old model A/B, evaluation quotas and metering advice should not be copied into new flows. See the current metered API list.
Cloud tables and Teams cloud implementation differences both matter. Shared-channel APIs are unsupported in government/China clouds. The activity report is global-only. No beta-only API is included.
PICK A QUESTION. BUILD A REPORT.
The recipe catalog
Cloud filtering hides unsupported endpoints. Shared-channel and tenant controls still apply. RSC filtering shows alternatives; it does not configure a Teams app.