Skip to main content

Microsoft Graph: Developer Blog

Microsoft Teams

30DaysMSGraph – Day 27 – Use case: Create a Team

List of all posts in the #30DaysMSGraph series

-Today’s post written by Nick Kramer

In Day 26 we concluded calling Microsoft Graph using Flow.  Today, we’ll show how to create teams and automate team lifecycles.

Introduction to Microsoft Teams

Microsoft Teams allows people to work together and get projects done by collaborating through chat, calls, and online meetings. Each team has members and owners, channels that contain chat messages, files (stored in SharePoint by default), apps, and tabs. Some of the apps and tabs you might use in a team include OneNote notebooks, Planner plans, and SharePoint lists.

Teams and Groups

Every team is part of a Group. (But not every group has a team!) Some of the information you think of as part of the team is actually part of the Group object, such as the name of the team/group and its members and owners. When you create a new team, you typically create a new group as well. (But it’s also possible to add a team to an existing group)

When creating teams and groups, you need the Group.ReadWrite.All scope. Both user delegated permissions and application permissions are supported.

Clone the Sample Application

To follow along with the code please clone the sample application from the contoso-airlines-teams-sample repo and follow the instructions in the file to build the project. The interesting code is in GraphService.cs.

In the Contoso Airlines sample, we create new teams every day for each flight that Contoso Airlines flies — and then archive the team after the flight is over.

Creating the team

Before we create a team, we need the IDs of the team members. Given the user’s universal principle name (UPN), we can retrieve the user’s data, including their ID:

When we add a user to a team, Graph needs to know the full URL to that user:

We repeat that for all the users in the flight crew:

Armed with that list of users, we can now create the group by providing the display name, mail nickname, description, visibility, owners, and members:

This generates a JSON payload of the form:

(Note the members@odata.bind syntax to provide references to existing resources, in this case users)

The next step is to create a team within the group:

We could pass an empty body to PUT /groups/{group.Id}/team, but we prefer to change some of the default settings – we don’t want guests to be able to create, update, or delete channels. Note also that sometimes when the team is created immediately after the group, that request can fail with a 404 because the necessary data hasn’t replicated to all data centers, so we retry in that case after a 10 second delay.

Note that the team has the same ID as the group:


Teams contain a list of channels, which contain the team’s chat messages. Channels also have a Files tab (typically linked to SharePoint), as well as any additional tabs you want to add to the team. Let’s create a channel for pilot talk:

When you create a channel with POST /teams/{teamId}/channels, Graph returns the created channel so you know its ID.


Let’s add a tab to channel that shows a map of the airport. Every tab has an associated app, for a map we can use the Website app. We know from documentation that the app ID for the Website app is “”. Graph will want that as a reference:

Using that, we can create the tab:

In the configuration section of the tab, there’s four properties that every tab has – the EntityID, the ContentURL, the WebsiteUrl, and the RemoveURL. At its core, a tab is an HTML rendering surface – and ContentURL is the HTML the render. WebsiteUrl is the HTML the show if the user clicks the “Go to website” button. RemoveUrl is the HTML shown when the user clicks the Remove button. And EntityId is a string that means something only to the app that powers the tab (in this case, the website app leaves it null) – again, see the docs for your favorite app.

Create a SharePoint list

Next, let’s create a SharePoint list of the problem passengers on the flight, and pin it to a tab. We’ll need some additional permission scopes in order to use SharePoint: Sites.ReadWrite.All and Sites.Manage.All. With those, we can get to the SharePoint site using the group:

(Again, there can be replication issues if you access the site to quickly after creating the group, so we put in some retries)

Once we have the site, we can create a new SharePoint List with 3 columns, Name, SeatNumber, and Notes:

That creates an empty list with those columns. Next we add some data to it:


Then we add a tab to the channel we created earlier:

And now we have a fully functioning team, with members, channels, and tabs.


With user delegated permissions, you can also add an app to the team. (We are working on application permissions) Let’s add the SurveyMonkey bot to the team. We need to know the app ID of the app we want, we can figure that out by running this in Graph Explorer:

where {} is a test team that you added SurveyMonkey to using Microsoft Teams. Look for the teamsAppId in the response.

Now, using that value, we install survey monkey to the new team:

Clone a team

Alternately, instead of creating a team from scratch, we can make a copy of an existing team. This is a good way to allow admins and end users to change the structure of the team you’re creating without having to update code. Clone Team also supports application permissions, so it’s a great way to get new teams with apps preinstalled.

When you clone a team, you get to pick which parts you want to clone. Your choices are apps, settings, channels, tabs, and members. We’ll choose “apps,settings,channels”:

Clone is a long-running operation, so we are actually creating a clone request and getting back a handle to check on its status. The critical part of the response is the Location header:

We query that operationUrl to see if the cloning is done. If it’s not, we wait 10 seconds and repeat.

Then, because we chose not to clone the membership, we need to add some people to the team:

And that gives us a fully functioning team, with members and apps.


Finally, after the flight is over and we are done with the team, we can archive it. This will put the team into a read-only state and hide it from our list of teams so our UI doesn’t get cluttered, but we can still read the contents of the team via the gear icon at the bottom of the screen:

Like clone, archiving is a long-running operation, so we will create an archiving request and monitor its status:

And like clone, we query that operationUrl to see if the archiving is done:

And there we go – we’ve shown how to create a new team, set it up with channels, tabs, and apps, and when the team has outlived its usefulness, how to archive it to make room for the next team.

Try it Out

Day 27 repo link


Join us tomorrow as we leverage webhooks for Microsoft Graph requests in Day 28.