microsoft graph api
64 TopicsGraph /users query with mailboxSettings filters/expands excludes unlicensed users
Hello Microsoft Graph team, I would like to report what appears to be a bug in Microsoft Graph user queries involving mailboxSettings. Issue summary: When mailboxSettings is used in a filter or expand clause on /users, unlicensed users are not returned, even when they should match the filter. Example scenario: Filter used: mailboxSettings/userPurpose ne 'room' Observed behavior: Users without Exchange license/mailbox are excluded from results (or result set appears truncated to mailbox-enabled users). Expected behavior: All users should be considered in /users query evaluation. If a user has no mailbox settings, behavior should be consistent and documented (for example null handling), but those users should not be silently dropped from /users results unless explicitly filtered out by query semantics. Reproduction steps: In a tenant with mixed users: Licensed mailbox users Unlicensed users without mailbox Run a /users query that includes mailboxSettings in filter and/or expand. Compare returned users with a baseline /users query without mailboxSettings conditions. Notice unlicensed users disappear when mailboxSettings is involved. Sample request patterns: GET https://graph.microsoft.com/v1.0/users?$filter=mailboxSettings/userPurpose ne 'room' GET https://graph.microsoft.com/v1.0/users?$expand=mailboxSettings&$filter=mailboxSettings/userPurpose ne 'room'13Views0likes1CommentFrom hours to minutes: Rethinking Microsoft Intune compliance reporting with the Export API
By: Daniel Gerrity – Principal Product Manager | Microsoft Intune If you manage a large device fleet with Microsoft Intune, you’ve almost certainly needed to get reporting data out of the service at scale — compliance state, device inventory, app status, endpoint analytics, or one of the many other reports admins rely on for operations and audit evidence. Intune supports this pattern through the export API, which generates supported reports as asynchronous export jobs instead of requiring you to retrieve the same data through thousands of operational Graph calls. You can see the full list of reports available through the export API in Intune reports and properties available using Graph API documentation. In the illustrative scenario below, moving one nightly job from operational Graph reporting endpoints to the Intune export API (exportJobs) cuts the work from roughly 100,000 API calls to about 15 while producing the same report data. The runtime drops from ~2.5 hours to ~15 minutes. Here’s how, and why the pattern holds up as your fleet grows. Note on the numbers The figures below are a representative example for a hypothetical 50,000-device enterprise, “Contoso,” and are rounded for clarity. Your results may vary based on fleet size, policy count, and how many compliance settings you evaluate. The scenario Contoso runs a nightly job that answers a deceptively simple question: For each device, across every compliance policy assigned to it, what is the state of each individual setting? This is a classic per-device, per-compliance-policy, per-setting state export. It’s the raw material behind compliance dashboards, audit evidence, remediation targeting, and “why is this device noncompliant” investigations. In Intune’s reporting catalog, this is the DeviceStatusSummaryByCompliancePolicySettingsReportV3 report. Contoso has ~50,000 managed devices, and the job runs once a day. The old way: Operational Graph APIs The intuitive approach treats compliance data as something you fetch, per device, right now. The script: Enumerates the fleet (managedDevices). Loops over every device, and for each one calls the operational reporting or setting-state endpoints (such as managedDevices detail and settingStates) to pull that device’s per-policy, per-setting results. Pages through the results in small JSON pages (often 50 rows at a time), reassembling everything client-side. It works. It just doesn’t scale because the number of calls is a function of the number of devices. At roughly two operational calls per device, 50,000 devices is on the order of ~100,000 Graph calls per run. That volume brings its own tax: Throttling. You hit service protection limits and have to implement retry/back-off logic. Threading. To finish inside the window at all, you parallelize which means concurrency bugs, partial failures, and harder debugging. Fragility. A run that makes 100,000 calls has 100,000 chances to fail, and a mid-run failure often means starting over. Time. End=to-end, the job lands around ~2.5 hours. Every time Contoso onboards more devices, this job gets slower and more expensive - the worst possible scaling direction for something that runs every night. The new way: Export API (exportJobs) The export API flips the model. Instead of asking Graph to compute results device-by-device in real time, you ask Intune to generate the entire report once, server-side, and hand you back a single file. The flow is a short, asynchronous handshake: 1. POST /deviceManagement/reports/exportJobs { "reportName": "DeviceStatusSummaryByCompliancePolicySettingsReportV3", "format": "csv", ...optional filter/select... } → returns a jobId, status: "notStarted" 2. GET /deviceManagement/reports/exportJobs('{jobId}') → poll until status: "completed" (a handful of polls while it builds) → response includes a short-lived download URL 3. GET {download URL} → one zipped CSV containing every device × policy × setting row That’s the whole pattern: request → poll → download → unzip → load. One report, one file, the entire fleet inside it. Count the calls: one POST to start the job, a handful of GET polls while Intune builds the file, and one GET to download it - call it ~15 calls total. Not ~15 per device. ~15 for the whole 50,000-device run. And that number barely moves whether Contoso has 50,000 devices or 150,000. Runtime drops to about ~15 minutes, most of which is simply waiting for the export to finish - cheap poll calls, not active compute. Most importantly, the output schema is identical. The CSV columns match what the old per-device loop assembled, so nothing downstream; dashboards, warehouse tables, alerting, has to change. You swap the acquisition layer and leave everything else alone. Side by side Comparison Operational Graph APIs Recommended Export API ( exportJobs ) Pattern Per-device loop plus paging Async export → download one file API calls per run ~100,000 ~15 Calls scale with Number of devices Nothing. The handshake remains fixed. Runtime ~2.5 hours ~15 minutes Concurrency Threading required None needed Output schema — Unchanged and drop-in compatible Net result — ~6,000× fewer API calls ~10× faster runtime Why it scales: Roundtrips, not bytes Here’s the subtlety worth internalizing, because it’s easy to get wrong. There are two different costs in this job, and they scale on different axes: The number of API calls - round-trips across the wire. The volume of data - the actual compliance rows you move. The data volume is the same either way. 50,000 devices × N settings is 50,000 × N rows, whether you assemble them from 100,000 little paged responses or receive them in one CSV. The export doesn’t move less data - it moves the same data. And yes, that file grows with both device count and setting count. More devices, bigger file; more settings, bigger file. So the win isn’t fewer bytes. The win is fewer round-trips. Every one of those 100,000 operational calls relies on authentication, TLS setup, network latency, and service-protection (throttling) accounting regardless of how much data it returns. Multiply that fixed overhead by 100,000 and it dominates everything. The export only pays that tax twice: once to start the job, once to download the file. Intune does the assembly server-side and streams you the result in a single bulk transfer. That reframes the scaling story: Call count is essentially constant - it doesn’t grow with devices or settings. It’s one job and one download. Data volume grows with devices and settings but a bigger CSV is a bigger single download, not more calls. Bulk transfer is exactly what HTTP is good at. Runtime has a mild data dependency: a larger fleet takes Intune a little longer to build the file. But you absorb that as a few extra seconds of poll-waiting, not as thousands of extra calls you have to orchestrate, retry, and throttle-manage. What the IT admin actually gets Beyond the raw speed, here’s the value that shows up in day-to-day operations: Your maintenance window comes back. A 15-minute job leaves room for everything else. Fewer moving parts to maintain. No custom throttling handler, no thread pool, no resumability logic. Less code is less to break at 2 a.m. Reliability by design. Two roundtrips means two failure points, and the server does the heavy lifting of assembling a consistent snapshot. Lower cost and lower service impact. Eliminating ~100,000 calls is easier on your tenant’s throttling limits and a better citizen for the Intune service overall. Room to grow. Because call count is decoupled from device count, doubling the fleet doesn’t double the job, you just download a somewhat larger file. No downstream disruption. Same schema for the output means the migration is contained to the ingestion step which is a low-risk swap, not a re-platforming. When to use which The export API isn’t a universal replacement, it’s best used for bulk, point-in-time snapshots: Reach for exportJobs when you need the whole fleet’s state (or a large, filtered slice) on a schedule such as nightly compliance loads, audit exports, warehouse hydration. Stick with the operational endpoints when you need a single device right now, an interactive “check this one device” lookup, or a real-time remediation trigger where waiting on an async job doesn’t make sense. The two are complementary. The mistake isn’t using the operational APIs, it’s using them in a loop to reconstruct something the export API will hand you in one file. The takeaway The per-device loop feels natural because it mirrors how we think about devices, one at a time. But at fleet scale, the question isn’t “what’s the state of this device?” a hundred thousand times over. It’s “give me the state of everything,” once. The export API is built for exactly that question, and answering it the right way turned a multi-hour nightly grind into a coffee break - from ~100,000 calls to about 15, ~10× faster, with zero downstream changes. If you have any questions, leave a comment below or reach out to us on X: @IntuneSuppTeam!1.2KViews0likes0CommentsMigrating Teams Calling Bot (EchoBot) from VMSS to Windows Containers / ACI?
Hi everyone, I have built a Microsoft Teams Calling Bot using Application-Hosted Media based on the official C# EchoBot sample: https://github.com/microsoftgraph/microsoft-graph-comms-samples/tree/master/Samples/PublicSamples/EchoBot Currently, our signaling logic is fully serverless and runs efficiently on Azure Functions. However, the actual media processing (using the C# Calling SDK which relies on native Windows media binaries) is deployed on VMSS with Windows Server, just like the repository's default deployment guide suggests. Running these Windows VMs continuously is becoming extremely expensive, and we are looking for a cheaper, modern, or "on-demand" alternative to handle the media pipeline. Since raw audio processing strictly requires these native Windows-based Media binaries, I would like to ask the community: 1. Windows Containers (ACI / AKS): Has anyone successfully run this C# EchoBot/Media SDK inside Windows Containers (such as Azure Container Instances or AKS)? If so, how do you handle the public IP and port-binding requirements for the media sockets (since the platform needs direct public connectivity for UDP/TCP traffic)? 2. On-Demand Provisioning: Are there any known architectures or best practices for spinning up the media processor only when a call starts (e.g., triggering an ACI instance via the Azure Function signaling bot) and tearing it down afterwards to keep costs near zero when idle? 3. Alternative Approaches: If you have solved this high-infrastructure cost problem for a real-time raw audio bot in production, what architecture did you end up using? Any documentation, GitHub references, or architectural advice would be highly appreciated! Thanks!40Views0likes2CommentsRetrieve all Teams transcripts a bot has attended to using Graph API
Hi there, I've been struggling for a lot of time trying to get this done. Has anyone been able to achieve something like this ? I wanted to : 1- Get all the meetings and transcripts of the tenant 2- Filter on those where the bot was attending 3- Get the transcripts when available. 4- Add rules to restrict the bot's access Right now I am stuck with the OAuth : The application 'bot-transcript' asked for scope 'OnlineMeetings.Read.All' that doesn't exist on the resource '00000003-0000-0000-c000-000000000000'. But this permission was added, and really seems to exist. Right ? Thanks in advance for any kind of help you could give me.58Views0likes1CommentExporting all Microsoft Intune Enterprise App Management catalog apps to CSV using Microsoft Graph
By: Joe Lurie, Sr. Product Manager | Microsoft Intune Managing applications at scale is one of the biggest time-consuming tasks for IT admins. Between packaging installers, writing detection rules, and keeping everything up to date - it adds up fast. That's exactly the problem Enterprise App Management in Microsoft Intune is designed to address. Enterprise App Management gives you access to a curated Enterprise App Catalog with hundreds of popular Win32 apps that are pre-packaged, pre-tested, and ready to deploy. Microsoft handles the install commands, detection logic, and update-ready packaging. Automatic updates for catalog apps are expected to roll out in mid-2026, making the experience even more hands-off. You just pick the app, assign it, and move on. But what if you want a full inventory of what's available in the catalog? Maybe you're evaluating which apps your organization can migrate from manual packaging, or you want to share the list with your app owners for review. In this post, I'll show you how to pull the complete catalog using Microsoft Graph PowerShell and export it to a CSV file. Prerequisites Before you start, make sure you have: Intune admin permissions: An account with Intune permissions to read app and catalog data (such as Intune Administrator or a custom role with app read access). You will also need Microsoft Graph delegated scope: DeviceManagementApps.Read.All (you'll consent to this when connecting). An Intune Suite or Enterprise App Management add-on license: Enterprise App Management is part of the Intune Suite or available as a standalone add-on. Note: Microsoft has announced that Enterprise App Management will also be included in Microsoft 365 E5 licensing starting July 1, 2026 - check current licensing guidance to confirm availability for your tenant. Microsoft Graph PowerShell SDK (Beta module) installed: Note that the catalog API is currently in the beta endpoint which means cmdlet names and properties may change before reaching v1.0. If you don't have the beta module installed yet, run: Install-Module Microsoft.Graph.Beta.Devices.CorporateManagement -Scope CurrentUser -Force Step 1: Connect to Microsoft Graph First, authenticate to Microsoft Graph with the required scope: Connect-MgGraph -Scopes "DeviceManagementApps.Read.All" You'll get a browser prompt to sign in and consent. Once connected, you're ready to query the catalog. Step 2: Retrieve all Catalog apps Microsoft Graph exposes the Enterprise App Catalog through the /beta/deviceAppManagement/mobileAppCatalogPackages collection. The PowerShell cmdlet for it is: $catalogApps = Get-MgBetaDeviceAppManagementMobileAppCatalogPackage -All The -All parameter is important as it handles pagination automatically so you get every catalog package, not just the first page of results. 💡 Tip To see every property available on a catalog package object, pipe the first result to Format-List . $catalogApps | Select-Object -First 1 | Format-List * Step 3: Export to CSV Next, let's select the most useful fields and write them to a CSV file: $csvPath = "C:\Temp\IntuneCatalogApps.csv" New-Item -ItemType Directory -Path (Split-Path $csvPath) -Force | Out-Null $catalogApps | Select-Object ProductDisplayName, VersionDisplayName, PublisherDisplayName | Export-Csv -Path $csvPath -NoTypeInformation Open the CSV in Excel and you've got a clean, sortable list of every catalog package in the Intune Enterprise App Catalog. Putting it all together Here's the complete script you can save and run: # Connect to Microsoft Graph Connect-MgGraph -Scopes "DeviceManagementApps.Read.All" # Pull all Enterprise App Catalog packages $catalogApps = Get-MgBetaDeviceAppManagementMobileAppCatalogPackage -All # Export to CSV $csvPath = "C:\Temp\IntuneCatalogApps.csv" New-Item -ItemType Directory -Path (Split-Path $csvPath) -Force | Out-Null $catalogApps | Select-Object ProductDisplayName, VersionDisplayName, PublisherDisplayName | Export-Csv -Path $csvPath -NoTypeInformation Write-Host "Exported $($catalogApps.Count) catalog apps to $csvPath" -ForegroundColor Green # Disconnect when done Disconnect-MgGraph Sample output Your CSV will look something like this: Product Version Publisher Mozilla Firefox 137.0.1 Mozilla Corporation Google Chrome 135.0.6998.89 Google LLC Zoom Workplace 6.4.6 Zoom Video Communications, Inc. Adobe Acrobat Reader DC 25.001.20467 Adobe Inc. 7-Zip 24.09 Igor Pavlov Why this matters Having a complete list of what's in the catalog is useful for a few scenarios: App rationalization - Share the list with app owners and identify which apps you can stop manually packaging and switch to the catalog instead. Gap analysis - Compare the catalog against your current app portfolio to see what's missing. Change management - Track what's available over time as Microsoft continues to add new apps. Compliance and auditing - Document which catalog apps are available for your tenant. Wrapping up Enterprise App Management is one of the most impactful features in the Intune Suite. It takes the most tedious parts of endpoint management - app packaging and updates - and just handles it for you. And with a short Graph script, you can get full visibility into what's available. And to see how Enterprise App Management secures your app catalog, check out the companion post here: aka.ms/Intune/EAM-Security. Give the script a try and let us know what you think along with other Enterprise App Management topics you’d like to see by leaving a comment below or reaching out on X @IntuneSuppTeam. Want to learn more about Enterprise App Management? Check out the official documentation for the full details. Join our community! Discuss real-world scenarios, get expert guidance, connect with peers, and influence the future of Microsoft Security products. Learn more at https://aka.ms/JoinIntuneCommunity.2.1KViews0likes1CommentMicrosoft Graph: Private channel SharePoint site URL naming appears to have changed
📄 Question We are creating private channels using the Microsoft Graph API: POST https://graph.microsoft.com/v1.0/teams/{team-id}/channels With the following payload: { "@odata.type": "#microsoft.graph.channel", "displayName": "Project-Channel-001", "description": "Sample private channel for testing", "membershipType": "private", "members": [ { "@odata.type": "#microsoft.graph.aadUserConversationMember", "******@odata.bind": "https://graph.microsoft.com/v1.0/users/{user-id}", "roles": ["owner"] } ] } 🔍 Observed Behavior When the private channel is created, the associated SharePoint site is provisioned automatically (as expected). However, the URL format appears to have changed. Previously observed behavior: https://{tenant}.sharepoint.com/sites/ProjectTeamURL-Project-Channel-001 Current behavior: https://{tenant}.sharepoint.com/sites/ProjectTeamName-Project-Channel-001 ❗ Impact This change introduces several issues: Breaks deterministic URL generation logic Produces longer and less predictable URLs Introduces dependency on display name, which is mutable and may contain unexpected characters Impacts existing automation and integrations relying on the previous pattern ❓ Questions Has there been a recent change in how SharePoint site URLs are generated for private channels? Is this behavior intentional and documented, or a regression? Is there any way (via Graph or otherwise) to: Control the generated SharePoint site URL, or Retrieve the final site URL deterministically without relying on pattern assumptions? Is the previous {ParentTeamUrl}-{ChannelName} format still expected in some scenarios, or has it been deprecated? 🧪 Additional Notes This behavior is observed when creating channels via Microsoft Graph (v1.0) The issue is reproducible across multiple test scenarios 🙏 Any clarification from Microsoft or others encountering this would be appreciated.70Views0likes0CommentsUnable to retrieve all attachments from forwarded Outlook emails using Graph API
We have integrated Outlook with our system using Microsoft Graph API and subscribed to message events. Whenever we receive an event, we process the email message at our end. Currently, we are facing an issue related to attachments in forwarded email conversations. Scenario An email conversation contains multiple replies. Some of these replies contain attachments. When a user forwards the entire email thread, Outlook generates a forwarded email that includes the conversation history in the email body. Problem When we receive the forwarded email event and fetch the message details using the Microsoft Graph API, we observe the following: The forwarded email only contains the latest reply's attachment. Attachments from earlier replies in the thread are not included in the forwarded message attachments. In some cases: The first reply contains an attachment. Subsequent replies do not contain attachments. When the user forwards the email, the forwarded message JSON shows: hasAttachments: false But, the forwarded email body still contains the previous conversation that had attachments. Our Questions Is there a way to retrieve all attachments from the entire email thread when a conversation is forwarded? Can we retrieve these attachments using the current user's access token via Microsoft Graph API? If there is a way, please also let us know how we can identify forwarded emails using the Microsoft Graph API, so that we can apply this solution only for forwarded emails. Our Requirement We need a reliable solution that works in production to ensure that all attachments from the email conversation are retrieved, even when the email thread is forwarded. This issue is currently impacting our production system, so we would greatly appreciate any guidance on the correct approach. Thank you in advance for your support.104Views0likes0CommentsIs principalId Always a GUID in Microsoft Graph ??
{ "error": { "code": "Request_BadRequest", "message": "Invalid GUID:HR", "innerError": { "date": "2026-02-13T06:44:24", "request-id": "87678d90-1d94-4131-a705-4356ad3568a4", "client-request-id": "63569c7b-1dea-42d4-8d72-aa3668c78418" } } } We’re encountering an issue with the Microsoft Graph API response for directoryRole Recently, one of our Graph API calls started returning a response where the principalId value appears to be a custom string instead of the expected GUID. In our code, we loop through each id from the delta response, assuming it will always be a valid GUID. However, we are now getting errors because one of the returned principalId values does not match the expected format. Our questions: Is it possible for Microsoft Graph API to return a custom string instead of a GUID for principalId? Has anyone experienced similar behavior with delta queries for directoryRole or any other object? Are there any known scenarios where the principalId format differs from the standard GUID? Any insights would be appreciated.68Views0likes0CommentsResource not found while trying to access the available resource
I am attempting to automate CRUD operations on Microsoft Entra objects using the Microsoft Graph API. However, I am encountering a Resource not found error when accessing a resource programmatically, even though the same resource is accessible without issue when invoking the API endpoint via Postman.212Views0likes2CommentsAlias for Refinable Managed Property Not Working in Search Queries
Hi, The alias for the refinable managed property has worked as expected in sortProperties for the past year, but it has recently stopped working and now returns an error. Using the original managed property name (RefinableDateSingle01) continues to work as expected. The error is shown below, together with the trace ID. Unfortunately, we are unable to switch to using RefinableDateSingle01 in sortProperties as it does not meet our business requirements. We are currently facing challenges due to the large number of SharePoint sites, many of which we do not have permission to access. As a result, we can only confirm that the refinable managed property RefinableDateSingle01 and its associated alias are configured correctly on the SharePoint sites where we have full access. What is the root cause of this issue, and how can it be resolved? https://graph.microsoft.com/v1.0/search/query { "requests": [ { "entityTypes": [ "listItem" ], "query": { "queryString": "* AND SiteId:\"siteId\"" }, "from": 0, "size": 50, "sortProperties": [ { "name": "RefinableDateSingle01", // This works when I use the refinable managed property name (RefinableDateSingle01), but it does not work when I use the alias I defined for this property "isDescending": false } ] } ] } 500 Internal Server Error (When I used alias in sortProperties) { "error": { "code": "InternalServerError", "message": "The call failed, please try again.", "target": "", "details": [ { "code": "InternalServerError", "message": "The call failed, please try again.", "target": "", "details": [ { "code": "InternalServerError", "message": "The call failed, please try again.", "target": "", "details": [ { "code": "InternalServerError", "message": "The call failed, please try again.", "target": "", "details": [ { "code": "InternalServerError", "message": "The call failed, please try again.", "target": "", "details": [ { "code": "InternalServerError", "message": "The call failed, please try again.", "target": "", "details": [ { "code": "InternalServerError", "message": "The call failed, please try again.", "target": "", "details": [ { "code": "InternalServerError", "message": "The call failed, please try again.", "target": "", "details": [ { "code": "FanoutDownstreamContradiction", "message": "The call failed, please try again.", "target": "", "details": [ { "code": "TwoStepFanout_FirstStepFailed", "message": "The call failed, please try again.", "target": "", "serviceName": "Xap", "moduleName": "SubstrateSearch.FanoutV2.MultiDimensionSearchFanoutPluginV3", "contactTeam": "3sdri", "httpCode": 500 }, { "code": "FanoutDownstreamContradiction", "message": "The call failed, please try again.", "target": "", "serviceName": "FanoutService", "moduleName": "Fanout", "contactTeam": "3STenantSearchDevs", "httpCode": 500 } ], "serviceName": "FanoutService", "moduleName": "Fanout", "contactTeam": "3STenantSearchDevs", "httpCode": 500 } ], "moduleName": "SubstrateFanoutSearchWorkflow", "httpCode": 500 } ], "moduleName": "AscUserSearchFanoutWorkflowV2", "httpCode": 500 } ], "moduleName": "AscUserSearchFanoutWorkflowV2", "httpCode": 500 } ], "moduleName": "G21AscWorkflow", "httpCode": 500 } ], "moduleName": "TenantFileSearchFederationWorkflow_ASC", "httpCode": 500 } ], "moduleName": "TenantFileSearchFederationWorkflow", "httpCode": 500 } ], "moduleName": "FederationWorkflow", "httpCode": 500 } ], "moduleName": "TopLevelWorkflowBase", "httpCode": 500 }, "Instrumentation": { "TraceId": "57c005b9-07fc-453b-8c73-2650d90670e0" } }102Views0likes0Comments