Why does Graph only return the Inbox when I query a group mailbox?
Because that’s the contract. The Mail API treats a Microsoft 365 group as a conversation surface, not a full mailbox. Point /messages at a group and you get Inbox-equivalent conversation items — Sent Items, Drafts, and anything users or transport rules dropped into custom folders simply aren’t projected. It isn’t a bug someone forgot to fix. User mailboxes get the full folder model; groups got a deliberately narrower one, and nobody has moved the line.
So what does the Import-Export API actually reach?
The folders the Mail API pretends don’t exist. Enumerate mailFolders on the group and you can walk Sent Items, Drafts, Deleted Items, Conversation History, and admin- or rule-created custom folders — then pull the items inside them. For compliance export, folder-level audits, or a migration where “we need Sent Items too” is a hard requirement, this is the first supported path that doesn’t involve bolting EWS back on or impersonating a member.
What permissions does it want?
App-only. A registered application with Mail.ReadWrite (read-only export genuinely does need the ReadWrite scope here — read that twice before you request it) or MailboxSettings.ReadWrite depending on what you’re touching, plus tenant admin consent. Delegated won’t do it; there’s no interactive user behind a group mailbox. Authenticate with a certificate, not a client secret you’ll forget to rotate.
Can I get attachments, or just metadata?
Both, but treat them as separate trips. Item properties — subject, sender, timestamps, hasAttachments — come back cheaply in the message list. Attachment content you fetch per item from the attachments endpoint, and that adds up fast. Pull metadata first, filter to what you actually need, then go back for bodies and attachments. Don’t page the whole folder with $expand=attachments and wonder why you’re throttled by lunch.
# Page a non-Inbox folder; metadata only, with 429 back-off
$items = [System.Collections.Generic.List[object]]::new()
$uri = "https://graph.microsoft.com/v1.0/groups/$groupId/mailFolders/$folderId/messages" +
"?`$top=50&`$select=id,subject,sentDateTime,from,hasAttachments"
do {
try {
$page = Invoke-MgGraphRequest -Method GET -Uri $uri -ErrorAction Stop
}
catch {
if ($_.Exception.Response.StatusCode.value__ -eq 429) {
$wait = [int]($_.Exception.Response.Headers['Retry-After'] ?? 30)
Write-Warning "Throttled. Backing off $wait s."
Start-Sleep -Seconds $wait
continue
}
throw
}
$items.AddRange($page.value)
$uri = $page.'@odata.nextLink'
} while ($uri)
Write-Host "Retrieved $($items.Count) items."
