SharePoint site creation via Microsoft Graph -- finally

SharePoint site creation via Microsoft Graph -- finally

SharePoint site creation via Microsoft Graph – finally

I have been waiting for this one for a long time. Years, actually. And if you have ever built a site provisioning solution on top of SharePoint Online, you know exactly what I mean.

At Ignite 2025, Microsoft quietly shipped something that many of us had given up hoping for: a proper Graph API endpoint to create SharePoint site collections. The Microsoft 365 Developer Blog announcement dropped on November 24th with the details. No more CSOM hacks. No more wrestling with the legacy SharePoint REST API. Just a clean POST request to /sites and you get a new site collection. That is it.

Why this matters

Until now, creating a SharePoint site programmatically meant one of two things:

  1. CSOM / PnP PowerShell – calling into the SharePoint-specific client object model, which requires the SharePoint admin URL and carries all the baggage of the legacy API surface.
  2. SharePoint REST API – using /_api/SPSiteManager/create, which works but lives completely outside of the Graph ecosystem.

Both approaches share a painful characteristic: they require Sites.FullControl.All. That is a tenant-wide, nuclear-level permission scope. Your app gets full control over every site in the tenant just because it needs to create new ones. Every security review I have ever been in flagged this immediately.

The new Graph endpoint fixes that. The create site API reference on Microsoft Learn has the full specification.

The new permission: Sites.Create.All

This is the real headline. Microsoft introduced a new permission scope called Sites.Create.All. It does exactly what the name says – it lets your app create sites and nothing else.

Here is the permission matrix:

Permission typeLeast privilegedHigher privileged alternative
Delegated (work or school account)Sites.Create.AllSites.FullControl.All
ApplicationSites.Create.AllSites.FullControl.All
Delegated (personal Microsoft account)Not supportedNot supported

The clever part: Sites.Create.All is designed to work with Sites.Selected. When your app creates a new site, it automatically receives Sites.Selected with FullControl on that specific site – and only that site. Your app can provision the site and then immediately configure it (add lists, apply templates, set permissions) without needing tenant-wide access.

For enterprise scenarios, this is a big deal. You can actually build a provisioning app that passes a security review without a three-hour argument.

Creating a site via Graph, step by step

1. Register your app and grant permissions

In the Azure portal (or via the Microsoft Entra admin center), add the following API permission to your app registration:

  • Microsoft Graph > Application permissions > Sites.Create.All

Grant admin consent. Yes, admin consent is still required – but the scope itself is far less dangerous than Sites.FullControl.All.

2. Acquire a token

Standard OAuth 2.0 client credentials flow. Nothing special here:

POST https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

client_id={client-id}
&scope=https://graph.microsoft.com/.default
&client_secret={client-secret}
&grant_type=client_credentials

3. Create a communication site

Send a POST request to the beta /sites endpoint:

POST https://graph.microsoft.com/beta/sites
Content-Type: application/json
Authorization: Bearer {access-token}

{
  "name": "Project Aurora",
  "webUrl": "https://contoso.sharepoint.com/sites/project-aurora",
  "locale": "en-US",
  "shareByEmailEnabled": false,
  "description": "Communication site for Project Aurora updates",
  "template": "sitepagepublishing",
  "ownerIdentityToResolve": {
    "email": "alex@contoso.com"
  }
}

The template property accepts two values:

Template valueSite type
sitepagepublishingCommunication site
stsTeam site (without a Microsoft 365 Group)

Important: There is no group template. Microsoft originally planned to support creating Group-connected team sites through this endpoint, but it did not work reliably and has been withdrawn. If you need a Group-connected team site, create the Microsoft 365 Group first (via POST /groups) and the site will be provisioned automatically.

4. Handle the response

If the request succeeds, you get back a 202 Accepted with a Location header:

HTTP/1.1 202 Accepted
Location: https://graph.microsoft.com/beta/sites/getOperationStatus(operationId='JXMnaHR0cHMlM0...')

Site creation is asynchronous. The Location header points to an operation status endpoint you can poll:

GET https://graph.microsoft.com/beta/sites/getOperationStatus(operationId='JXMnaHR0cHMlM0...')
Authorization: Bearer {access-token}

Be patient here. In my testing, it takes roughly five to ten minutes before the site is fully provisioned and discoverable. Do not assume it is instant.

5. Create a team site (no Group)

Same endpoint, different template:

POST https://graph.microsoft.com/beta/sites
Content-Type: application/json
Authorization: Bearer {access-token}

{
  "name": "Engineering Wiki",
  "webUrl": "https://contoso.sharepoint.com/sites/engineering-wiki",
  "locale": "en-US",
  "shareByEmailEnabled": true,
  "description": "Internal engineering knowledge base",
  "template": "sts",
  "ownerIdentityToResolve": {
    "email": "eng-lead@contoso.com"
  }
}

That is all there is to it. Two templates, one endpoint.

Comparison with the old approaches

Here is how the new Graph API stacks up against the old methods:

AspectGraph API (new)CSOM / PnP PowerShellSharePoint REST API
EndpointPOST /sitesSPSiteManager / New-PnPSite/_api/SPSiteManager/create
Minimum permissionSites.Create.AllSites.FullControl.AllSites.FullControl.All
Auth modelStandard Graph OAuth 2.0SharePoint-specific or GraphSharePoint-specific
Group-connected sitesNot supported (yet)SupportedSupported
Ecosystem integrationFull Graph ecosystemSharePoint-onlySharePoint-only
SDK supportGraph SDKs (C#, JS, Python, etc.)PnP librariesManual HTTP calls
StatusBetaGA (stable)GA (stable)

The Graph API wins on permissions and ecosystem integration. The legacy approaches still win on maturity and feature completeness. For new projects, I would start with the Graph API. For existing solutions that rely on Group-connected sites, stay with PnP PowerShell for now.

Use cases

A few scenarios that were painful before and are now much simpler:

Self-service site requests. Build an approval workflow in Power Automate or Logic Apps. User submits a request, manager approves, the flow calls the Graph API to provision the site. No admin intervention needed.

Automated project workspaces. When a new project is created in your project management system, automatically spin up a SharePoint site with a standardized structure. The Sites.Create.All + Sites.Selected combo means your integration only has access to the sites it created.

Tenant migrations. Migrating from one tenant to another? Script the site creation in bulk via the Graph API, then use the automatic Sites.Selected grant to populate content.

Dev/test environments. Spin up test sites, run your integration tests, tear them down. The lower permission model makes this much safer than before.

What does not work yet

A few things to be aware of:

  • Beta only. This endpoint lives under /beta. Microsoft explicitly states that beta APIs are subject to change and should not be used in production. Use at your own risk – though I suspect GA is coming soon.
  • No Group-connected team sites. As mentioned, the group template was pulled. You cannot create a team site with a linked Microsoft 365 Group through this API.
  • No Graph PowerShell cmdlet. There is no New-MgSite cmdlet yet. You have to use Invoke-MgGraphRequest with the raw endpoint. The SDK will catch up once the API metadata is processed.
  • No membership management. There is no Graph API to add site members through this endpoint. You will need to handle membership separately via SharePoint-specific APIs or by granting permissions through the /sites/{site-id}/permissions endpoint.
  • Provisioning delay. Sites take five to ten minutes to become fully available. Plan your automation accordingly – do not chain dependent operations immediately after creation.
  • Admin consent required. Even though Sites.Create.All is lower privilege, it still requires tenant admin consent. For ISVs distributing multi-tenant apps, this remains a friction point.

Copilot agents and self-service provisioning

One scenario I keep thinking about: Copilot agents. Now that they are a real thing in Microsoft 365, imagine this flow:

A user opens Teams and says to a custom Copilot agent: “I need a new project site for Project Aurora with standard document libraries and a project tracker.”

The agent:

  1. Validates the request against your naming conventions and governance policies.
  2. Calls the Graph API with Sites.Create.All to provision the site.
  3. Uses the automatic Sites.Selected grant to configure the site – creating lists, applying a site template, setting up navigation.
  4. Reports back to the user with the site URL.

No portal. No ticket. No waiting. The permission model makes this safe to automate end to end, because the agent only ever has access to the sites it creates.

I think Sites.Create.All was designed with exactly this kind of scenario in mind. This is honestly the angle that excites me most about the new API – not just scripting site creation, but enabling conversational, agent-driven provisioning where governance is baked in from the start. No other provisioning approach gives you the combination of least-privilege permissions and full Graph ecosystem access that makes this pattern safe at scale.

Where this leaves us

The SharePoint site creation API in Microsoft Graph has been one of the most requested features in the M365 developer community for years. It is finally here – in beta, with some rough edges, but it works. The Sites.Create.All permission alone is worth paying attention to, because it solves a real security problem that every enterprise provisioning solution had to work around.

I expect this to hit GA sometime in 2026. When it does, it will become the default way to provision sites programmatically. Start experimenting with the beta now so you are ready.


Read more

Enjoyed this post? Let's connect on LinkedIn:

Follow on LinkedIn →