๐ŸŽ New User? Get 20% off your first purchase with code NEWUSER20 ยท โšก Instant download ยท ๐Ÿ”’ Secure checkout Register Now โ†’
Menu

Categories

Manage Microsoft Teams with Graph API: Channels, Members, Messages (2026)

Manage Microsoft Teams with Graph API: Channels, Members, Messages (2026)
Microsoft Teams Graph API guide

Provisioning Teams by hand is repetitive. Provisioning 50 Teams for 50 new project workgroups by hand is a full day of work. The Microsoft Graph API and PowerShell SDK turn that into a script you run once. This guide is the practical reference: the scopes you need, the cmdlets that actually work in 2026, and the patterns for the four most common operations โ€” create a team, add channels, manage membership, and post messages.

Free PDF cheat sheet at the bottom condenses everything onto a single page.

Required scopes

For most Teams admin scripts:

  • Team.Create โ€” create new Teams
  • TeamSettings.ReadWrite.All โ€” modify team-level settings
  • TeamMember.ReadWrite.All โ€” manage members and owners
  • Channel.Create, Channel.Delete.All, ChannelSettings.ReadWrite.All โ€” channel ops
  • ChannelMessage.Send (delegated) or ChannelMessage.ReadWrite.All (application) โ€” post messages
  • Group.ReadWrite.All โ€” required because Teams sit on top of M365 groups

Posting messages as the app (not as a user) requires the Resource-Specific Consent (RSC) permission model โ€” a separate setup we cover at the end.

Team templates

Microsoft ships a few baseline templates: standard, educationClass, educationProfessionalLearningCommunity, plus any custom ones you have published. Most admin scripts use standard.

Create a team

$team = New-MgTeam -BodyParameter @{
    "template@odata.bind" = "https://graph.microsoft.com/v1.0/teamsTemplates('standard')"
    displayName  = "Project Atlas"
    description  = "Engineering for Project Atlas"
    visibility   = "Private"   # or Public
    members = @(
        @{
            "@odata.type"     = "#microsoft.graph.aadUserConversationMember"
            roles             = @("owner")
            "user@odata.bind" = "https://graph.microsoft.com/v1.0/users('alice@contoso.com')"
        }
    )
}

# $team.Id is the Group Id - the team uses the same Id as its underlying M365 group

Provisioning is asynchronous. The cmdlet returns immediately; the team is fully ready a few seconds later. Poll for readiness if your next step depends on it:

$ready = $false
1..10 | ForEach-Object {
    Start-Sleep -Seconds 3
    if (Get-MgTeam -TeamId $team.Id -ErrorAction SilentlyContinue) {
        $ready = $true; break
    }
}

Channels โ€” public, private, shared

# Standard public channel
New-MgTeamChannel -TeamId $team.Id -BodyParameter @{
    displayName    = "engineering"
    description    = "Engineering work"
    membershipType = "standard"
}

# Private channel
New-MgTeamChannel -TeamId $team.Id -BodyParameter @{
    displayName    = "leads-only"
    membershipType = "private"
    members = @(@{
        "@odata.type"     = "#microsoft.graph.aadUserConversationMember"
        roles             = @("owner")
        "user@odata.bind" = "https://graph.microsoft.com/v1.0/users('alice@contoso.com')"
    })
}

# Shared channel (cross-tenant)
New-MgTeamChannel -TeamId $team.Id -BodyParameter @{
    displayName    = "vendor-sync"
    membershipType = "shared"
}

Add and remove members and owners

$user = Get-MgUser -UserId "carol@contoso.com"

# Add as member
New-MgTeamMember -TeamId $team.Id -BodyParameter @{
    "@odata.type"     = "#microsoft.graph.aadUserConversationMember"
    roles             = @()
    "user@odata.bind" = "https://graph.microsoft.com/v1.0/users('$($user.Id)')"
}

# Promote to owner
$mem = Get-MgTeamMember -TeamId $team.Id |
    Where-Object { $_.AdditionalProperties.email -eq "carol@contoso.com" }
Update-MgTeamMember -TeamId $team.Id -ConversationMemberId $mem.Id -BodyParameter @{
    "@odata.type" = "#microsoft.graph.aadUserConversationMember"
    roles         = @("owner")
}

# Remove
Remove-MgTeamMember -TeamId $team.Id -ConversationMemberId $mem.Id

Post messages and replies

$channel = Get-MgTeamChannel -TeamId $team.Id |
    Where-Object DisplayName -eq "general"

# Post a message
$msg = New-MgTeamChannelMessage -TeamId $team.Id -ChannelId $channel.Id -BodyParameter @{
    body = @{
        contentType = "html"
        content     = "Welcome to Project Atlas โ€” kickoff meeting Friday 10:00 CEST."
    }
}

# Reply to it
New-MgTeamChannelMessageReply -TeamId $team.Id -ChannelId $channel.Id -ChatMessageId $msg.Id -BodyParameter @{
    body = @{ contentType = "text"; content = "Calendar invite sent." }
}

Posting as the application (no signed-in user) is restricted to specific scopes and requires Protected APIs approval from Microsoft for production use. Posting as a user (delegated) works without that step.

Add tabs and apps

# Add a Wiki tab
New-MgTeamChannelTab -TeamId $team.Id -ChannelId $channel.Id -BodyParameter @{
    displayName = "Project Wiki"
    "teamsApp@odata.bind" = "https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/com.microsoft.teamspace.tab.wiki"
}

# Add a website tab
New-MgTeamChannelTab -TeamId $team.Id -ChannelId $channel.Id -BodyParameter @{
    displayName = "Roadmap"
    "teamsApp@odata.bind" = "https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/com.microsoft.teamspace.tab.web"
    configuration = @{
        contentUrl = "https://confluence.contoso.com/atlas/roadmap"
        websiteUrl = "https://confluence.contoso.com/atlas/roadmap"
    }
}

Archive / unarchive / delete

# Archive (read-only, keeps content)
Invoke-MgGraphRequest -Method POST -Uri "/v1.0/teams/$($team.Id)/archive"

# Unarchive
Invoke-MgGraphRequest -Method POST -Uri "/v1.0/teams/$($team.Id)/unarchive"

# Delete (hard - same as deleting the underlying M365 group)
Remove-MgGroup -GroupId $team.Id

Cheat sheet

Every cmdlet, scope, and snippet on a single PDF: Teams Graph API Cheat Sheet.

FAQ

Why is my new team missing from the Teams client?

Either the user has not refreshed the Teams client (it polls every few minutes), or the team was created with no members yet. Add at least one member; the team appears for them on next refresh.

Can I post a message as a bot or service account?

Yes โ€” but posting as the application (no user) requires the Teams "Protected APIs" approval from Microsoft for any production scenario, and even then is rate-limited. Posting as a service account user with delegated scopes is the lower-friction route.

What is the difference between standard, private, and shared channels?

Standard = visible to all team members. Private = visible only to channel members (subset of team). Shared = can include guests from outside the team or even outside the tenant.

How do I find the Team ID without going to the Teams client?

Get-MgTeam -All lists every team. Or Get-MgGroup -Filter "displayName eq 'Project Atlas'" โ€” the Group ID is the Team ID.

Can I provision a team from a custom template?

Yes โ€” first publish the template via the Teams admin center or PowerShell, then bind to its template ID instead of standard in the create call.

How do I add a guest user from another tenant?

First invite them to the directory with New-MgInvitation, then add the resulting user object to the team like any other member.

Are Teams operations rate-limited?

Yes โ€” Teams APIs are particularly aggressive about throttling. Add randomized 1-3 second delays between team-create operations when bulk-provisioning more than ~20 teams.

Related reading

Share this article:
Dargslan Editorial Team (Dargslan)
About the Author

Dargslan Editorial Team (Dargslan)

Collective of Software Developers, System Administrators, DevOps Engineers, and IT Authors

Dargslan is an independent technology publishing collective formed by experienced software developers, system administrators, and IT specialists.

The Dargslan editorial team works collaboratively to create practical, hands-on technology books focused on real-world use cases. Each publication is developed, reviewed, and...

Programming Languages Linux Administration Web Development Cybersecurity Networking

Stay Updated

Subscribe to our newsletter for the latest tutorials, tips, and exclusive offers.