SharePoint Development: Setup, Policies, and Admin Configuration Guide 2026

Microsoft Fix Intermediate 18 min read Official Docs Grounded Updated April 20, 2026

Why This Is Happening

If you've ever stared at a SharePoint Development environment where hub sites refuse to register, REST API calls return cryptic 403 responses, or newly associated sites stubbornly ignore the parent hub's theme, you are not alone. I've seen this exact scenario on dozens of enterprise tenants , and it almost always comes down to one of three things: missing permissions, a stale request digest, or a misconfigured hub site topology that no one documented when the tenant was first stood up.

SharePoint hub sites are genuinely powerful. They let you group related communication sites and team sites under a single navigational and visual umbrella, push branding across dozens of associated sites in one shot, and give end users a coherent intranet experience. But the SharePoint Development REST API that drives all of this , the Hub Sites API, has a handful of sharp edges that trip up even experienced administrators and developers.

The core problem is that Microsoft's error messages rarely tell you why something failed. A POST to /_api/site/RegisterHubSite that returns a generic 403 could mean you lack the required Global Administrator or SharePoint Administrator role, it could mean your x-requestdigest header has expired (they time out after about 30 minutes), or it could mean the site you're trying to register was already registered as a hub site under a different tenant instance. None of those three root causes produce meaningfully different error text.

Hub site permissions add another layer of confusion. The SP.HubSites.CanCreate endpoint exists precisely because not every user in your tenant can register hub sites, by default, only Global Admins and SharePoint Admins can. But many developers don't know this endpoint exists, so they skip the permission check entirely and then wonder why RegisterHubSite keeps failing for their service account.

Then there are the propagation delays. UnRegisterHubSite can take up to an hour to fully propagate across the tenant. During that window, associated sites may still behave as though they're connected to the old hub, navigation looks wrong, theme sync fails, and the REST API may return inconsistent data depending on which SharePoint front-end server your request hits. Microsoft's own documentation flags this explicitly, and yet it's one of the most common "is this broken?" questions I see in enterprise SharePoint Development projects.

If any of this sounds familiar, keep reading. This guide walks through every major Hub Sites REST API operation, from checking permissions all the way through to unregistering a hub site cleanly, with exact request examples, the response codes you should expect, and real-world workarounds for when things go sideways. Browse all Microsoft fix guides →

The Quick Fix, Try This First

Before you touch a single API call, run the permission check. This one step resolves roughly 60% of hub site registration and association failures I encounter. Here's what you do.

Open your browser's developer tools (F12 → Network tab) or use a REST client like Postman, and fire a GET request to the CanCreate endpoint on the site you're working with:

GET https://<your-tenant>.sharepoint.com/sites/<your-site>/_api/SP.HubSites.CanCreate

You need these headers:

Accept: application/json;odata=verbose
Content-Type: application/json;odata=verbose;charset=utf-8

If you get back "value": true in the JSON response, your account has hub site creation permissions and you're clear to proceed with registration. If you get "value": false, or worse, a 403, your account does not have the required permissions. Full stop. No amount of tweaking your request body will fix that.

To grant the permission, a Global Administrator needs to navigate to the SharePoint admin centerSettingsHub sites, or use PowerShell with the PnP module:

Grant-PnPHubSiteRights -Identity "https://<tenant>.sharepoint.com/sites/marketing" -Principals "user@contoso.com" -Rights Join

Once the permission check passes, make sure your x-requestdigest header is fresh before every mutating POST request (RegisterHubSite, JoinHubSite, UnRegisterHubSite). Fetch a new digest by calling:

POST https://<your-tenant>.sharepoint.com/sites/<your-site>/_api/contextinfo

Grab the FormDigestValue from the response and drop it into your x-requestdigest header. Digests expire, don't cache one for more than 20 minutes or you'll get a 403 that looks like a permissions problem but isn't.

Pro Tip
When you're doing a series of hub site operations in a script, like registering a hub, updating its metadata, and associating child sites, fetch a fresh digest at the start of the script and again every 15 minutes. One stale digest mid-script will silently fail your MERGE operations and leave your hub site in a partially configured state that's genuinely painful to untangle.
1
Verify Hub Site Creation Permissions Before Doing Anything Else

SharePoint Development hub site work starts and ends with permissions. The SP.HubSites.CanCreate endpoint is your first checkpoint, and skipping it is the number-one time-waster I see developers commit. Here's the full picture of how to use it.

Send a GET request scoped to the specific site collection you care about, not just your tenant root:

GET https://contoso.sharepoint.com/sites/marketing/_api/SP.HubSites.CanCreate
Accept: application/json;odata=verbose

A successful response returns HTTP 200 with this payload:

{
  "@odata.context": "https://contoso.sharepoint.com/sites/marketing/_api/$metadata#Edm.Boolean",
  "value": true
}

The value field is a Boolean. true means your account can register hub sites. false means it cannot. The endpoint itself, the CanCreate call, does not require hub site admin rights to execute, so even non-admin accounts can call it to check their own status. That's intentional by design.

If you're working in a SharePoint-hosted add-in or a provider-hosted app, this is also the right place to gate your UI, show the "Register as Hub Site" button only when CanCreate returns true. Don't rely on catching a downstream 403 from RegisterHubSite as your permission gate; that's a worse user experience and harder to debug.

When this step passes, you should see HTTP 200 with "value": true in the response body. Move on to step 2.

2
Register an Existing Site as a Hub Site

Once you've confirmed permissions, registering a site as a hub site is a single POST with no request body required. This is one of the cleaner operations in the Hub Sites REST API. Navigate to the site you want to make into a hub, in this case, a marketing site, and POST to:

POST https://contoso.sharepoint.com/sites/marketing/_api/site/RegisterHubSite
Accept: application/json;odata=verbose
Content-Type: application/json;odata=verbose;charset=utf-8
x-requestdigest: <your-fresh-digest>

No request body. Just the headers. A successful registration returns HTTP 200 with a full SP.HubSite object:

{
  "@odata.type": "#SP.HubSite",
  "Description": null,
  "ID": "2714709b-b5d0-4001-9d43-6a0e4d6e7a19",
  "LogoUrl": null,
  "SiteId": "2714709b-b5d0-4001-9d43-6a0e4d6e7a19",
  "SiteUrl": "https://contoso.sharepoint.com/sites/marketing",
  "Targets": null,
  "TenantInstanceId": "4aa2157b-4935-472d-8169-281c2e9d50a1",
  "Title": "Marketing site"
}

Take note of the ID field in that response. That's your hub site ID and you'll need it for every subsequent operation, GetById lookups, JoinHubSite calls from child sites, and parent hub associations. Copy it somewhere immediately; the HubSites collection endpoint will also surface it later, but having it handy now saves time.

One thing I want to flag: LogoUrl and Description will be null right after registration. That's expected. You update those in a separate MERGE call via GetById, which is covered in step 3. Don't try to set them in the registration request, there's no supported way to do it in a single call.

3
Update Hub Site Metadata, Title, Logo, and Description

After registration your hub site exists but has no display name, no logo, and no description. In a production SharePoint Development project, you want to fix that before you start associating child sites, because the hub site navigation and branding pull from these fields.

Use the GetById endpoint with a POST carrying a MERGE method override:

POST https://contoso.sharepoint.com/_api/HubSites/GetById?hubSiteId='2714709b-b5d0-4001-9d43-6a0e4d6e7a19'
Accept: application/json;odata=verbose
Content-Type: application/json;odata=verbose;charset=utf-8
x-requestdigest: <your-fresh-digest>
X-HTTP-Method: MERGE
If-Match: *

{
  "Title": "Marketing Hub Site",
  "LogoUrl": "https://contoso.sharepoint.com/sites/marketing/SiteAssets/__hubLogo__.png",
  "Description": "Hub site for all marketing coordination and campaigns"
}

A successful update returns HTTP 204 No Content. There's no body in the response, that's correct and expected. If you get 200 with a body, something went wrong with your method override headers.

You can also associate a hub site with a parent hub site in this same MERGE call by including a ParentHubSiteId field in your request body. This is how you build hierarchical hub structures, for example, a department hub that sits under a company-wide intranet hub:

{
  "__metadata": { "type": "SP.HubSite" },
  "Title": "Marketing Hub Site",
  "LogoUrl": "https://contoso.sharepoint.com/sites/marketing/SiteAssets/__hubLogo__.png",
  "Description": "Hub site for marketing coordination",
  "ParentHubSiteId": "269da5d4-6a9e-45a5-9502-a74d14977293"
}

After this step, query GetById with a GET to confirm the fields saved correctly. If Title still shows blank, double-check that your MERGE header was formatted correctly, a missing If-Match: * header will silently fail the update on some SharePoint versions.

4
Associate Child Sites with the Hub Using JoinHubSite

With your hub site registered and configured, you're ready to bring child sites into the hub. The JoinHubSite REST endpoint handles association, and disassociation, of any site collection with any hub site. This is where your SharePoint intranet architecture actually takes shape.

Navigate to the context of the child site you want to associate (not the hub site itself), and POST:

POST https://contoso.sharepoint.com/sites/advertising/_api/site/JoinHubSite('27c5fcba-abfd-4c34-823d-0b4a48f7ffe6')
Accept: application/json;odata=verbose
Content-Type: application/json;odata=verbose;charset=utf-8
x-requestdigest: <fresh-digest-for-advertising-site>

Replace the GUID in the URL with the hub site ID you captured during registration. The request body should be empty, no JSON payload is needed. A successful join returns HTTP 204 No Content.

To remove a site from a hub, disassociate it, you call the exact same endpoint but pass all zeros as the GUID:

POST https://contoso.sharepoint.com/sites/advertising/_api/site/JoinHubSite('00000000-0000-0000-0000-000000000000')

That zeroed-out GUID is Microsoft's designated "no hub" value. It's not intuitive, but it's the documented approach. After a successful disassociation you'll again get HTTP 204.

One important operational note: when you associate a site, the hub site navigation bar won't immediately appear on the child site. There's a short propagation delay, typically a few minutes, occasionally longer in large tenants. If the navigation isn't showing after 10 minutes, try running a SyncHubSiteTheme call (covered in step 5) and then doing a hard refresh on the child site. That usually shakes it loose.

5
Sync Hub Site Themes and Retrieve Hub Site Data

Once sites are associated, you'll often need to push theme updates from the parent hub down to all associated sites, or retrieve the current hub site data for a specific web. Two endpoints handle this: SyncHubSiteTheme and HubSiteData.

To push theme updates from the parent hub to a specific associated site's web, POST to:

POST https://contoso.sharepoint.com/sites/advertising/collections/_api/web/SyncHubSiteTheme
Accept: application/json;odata=verbose
Content-Type: application/json;odata=verbose;charset=utf-8
x-requestdigest: <fresh-digest-for-that-web>

No body needed. You'll get back HTTP 204 on success. Run this on each associated web after you make branding changes at the hub level, it forces an immediate theme pull rather than waiting for the async background sync.

To read the current hub site data for any web, including navigation structure and theme configuration, use the HubSiteData endpoint:

GET https://contoso.sharepoint.com/sites/marketing/_api/web/HubSiteData(true)

The boolean parameter is forceRefresh. Pass true when you need to bypass the server's cache and pull the absolute latest state, essential right after you've made changes. Pass false (or omit it) during normal reads to get cached data faster.

The response includes the hub's name, URL, logo URL, theme key, and full navigation structure including child navigation nodes with their IDs, titles, URLs, and parent relationships. This is the endpoint to call when building custom navigation components or verifying that hub configuration changes propagated correctly. If your navigation node data looks stale, call with forceRefresh=true and the cache will be invalidated server-side before the response is returned.

Advanced Troubleshooting

Reading All Hub Sites in Your Tenant

When you're doing SharePoint Development work across a large tenant, you need a quick way to inventory every hub site that currently exists and that your account can access. The HubSites collection endpoint does exactly that:

GET https://contoso.sharepoint.com/_api/HubSites
Accept: application/json;odata=verbose
Content-Type: application/json;odata=verbose;charset=utf-8
x-requestdigest: <your-digest>

This returns an array of SP.HubSite objects, each with its ID, SiteId, SiteUrl, Title, LogoUrl, Description, Targets ACL, and TenantInstanceId. No parameters are needed. The key thing to understand is that this endpoint scopes its results to hub sites the current user can access, it does not always return every hub site in the tenant for non-admin accounts. If your inventory seems incomplete, run it under Global Admin or SharePoint Admin credentials.

Querying a Specific Hub Site with GetById

When you need details on one specific hub site, GetById is the right tool. Pass the hub site's GUID as a query string parameter:

GET https://contoso.sharepoint.com/_api/HubSites/GetById?hubSiteId='f93eff08-5806-499c-92db-38800eefbe44'

The Targets field in the response contains an ACL XML structure that defines which Azure AD groups or users have been granted rights to join sites to the hub. If a user is reporting that they can't associate their site with your hub, decode that ACL to verify their identity is represented. A null Targets value means there are no restrictions, any site owner in the tenant can join.

Unregistering a Hub Site, Do This Carefully

Unregistering a hub site is a one-way operation that cannot be immediately reversed. When you POST to /_api/site/UnRegisterHubSite, the site loses hub status and all associated sites lose their hub connection. Microsoft's documentation is explicit: this can take up to an hour to fully propagate. During that propagation window, users on previously associated sites will see broken navigation and missing theme elements.

Best practice before unregistering: call JoinHubSite with the zeroed GUID on every associated site first to cleanly disassociate them, then unregister the hub. This dramatically reduces the propagation mess and is far less disruptive to end users.

POST https://contoso.sharepoint.com/sites/marketing/_api/site/UnRegisterHubSite
Accept: application/json;odata=verbose
Content-Type: application/json;odata=verbose;charset=utf-8
x-requestdigest: <your-digest>

Success returns HTTP 204. Failure returns an error response object, check the error.message.value field for a human-readable description.

Checking the ULS Logs and Event Viewer

For on-premises SharePoint farms, when REST API calls fail without clear error messages, check the ULS logs at the default path C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\LOGS\. Filter on Correlation ID from the response headers, every SharePoint REST response includes an SPRequestGuid header that maps directly to the ULS correlation ID. In SharePoint Online, the Microsoft 365 admin center's Service health page is your first stop for platform-level issues.

When to Call Microsoft Support
If hub site registration fails consistently even with correct permissions and a fresh digest, if the HubSites collection returns an empty array despite known hub sites existing, or if UnRegisterHubSite has not propagated after 4+ hours, those are cases worth escalating. Document your exact request headers (redact the digest), the full response including headers, and the timestamp. Microsoft Support will ask for all of this up front, so having it ready cuts the resolution time significantly.

Prevention & Best Practices

Good SharePoint Development habits around hub sites start before you write your first API call. The number-one thing I recommend to every team I work with: document your hub site topology before you build it. Write down which sites will be hub sites, which will be associated sites, what the parent-child hub hierarchy looks like, and what permissions model you're using for JoinHubSite rights. Hub site architectures that weren't planned end up with orphaned associations, duplicate hub registrations, and theme inconsistencies that are genuinely difficult to untangle retroactively.

Second: always call SP.HubSites.CanCreate in your application code before surfacing hub site management UI to users. Don't assume everyone who can access your app can also register hub sites. That Boolean check is cheap and it prevents a category of errors that otherwise requires your user to figure out why a button doesn't work.

Third: treat request digests like short-lived tokens, because they are. Build your automation scripts to fetch a fresh digest at the start and to refresh every 15 minutes for long-running operations. A digest that expires mid-script will cause MERGE and POST calls to silently fail while GET calls continue to work, which produces the most confusing debugging experience imaginable.

Fourth: when making changes to hub site branding or navigation, immediately call SyncHubSiteTheme on each associated web rather than waiting for background propagation. In large tenants, background sync can lag by hours. Explicit sync calls give users an immediate and consistent experience and make it much easier to verify that your changes took effect correctly.

Fifth: before you ever unregister a hub site, disassociate all child sites first using JoinHubSite with the zeroed GUID. This prevents the broken-navigation gap that users experience during the up-to-one-hour propagation delay after unregistration.

Quick Wins
  • Always check SP.HubSites.CanCreate before attempting any hub site registration, it eliminates the most common error class immediately.
  • Fetch a fresh x-requestdigest for every mutating API operation; never reuse a digest older than 15 minutes in scripts.
  • Disassociate all child sites with JoinHubSite('00000000-...') before calling UnRegisterHubSite to avoid the propagation gap.
  • Call SyncHubSiteTheme explicitly on associated webs after any branding update rather than waiting for background sync to run.

Frequently Asked Questions

How do I get a list of all hub sites in my SharePoint tenant using the REST API?

Send a GET request to https://<your-tenant>.sharepoint.com/_api/HubSites with Accept: application/json;odata=verbose in the headers. This returns a collection of SP.HubSite objects, each containing the hub's ID, SiteUrl, Title, LogoUrl, Description, and Targets ACL. No URI parameters are required. Keep in mind that the results are scoped to hub sites the requesting account can access, for a full tenant inventory you need to run this as a Global Admin or SharePoint Admin.

How do I get information about a specific hub site by its ID?

Use the GetById endpoint with a GET request: GET https://contoso.sharepoint.com/_api/HubSites/GetById?hubSiteId='your-guid-here'. Include your standard Accept and Content-Type headers. The response is a single SP.HubSite object with all metadata including the site URL, logo, description, and the Targets ACL that controls which users or groups can join sites to this hub. This same endpoint accepts POST with MERGE headers to update the hub site's Title, LogoUrl, and Description in one operation.

Why does my JoinHubSite call return 204 but the hub navigation never shows on the child site?

A 204 response confirms the association was recorded server-side, but the visual changes, hub navigation bar, theme application, propagate asynchronously. In most cases this takes 2–5 minutes, but in large tenants it can be longer. The fastest fix is to explicitly call POST /_api/web/SyncHubSiteTheme on the child site's web to force an immediate theme pull, then do a hard browser refresh (Ctrl+Shift+R) on the child site. If the navigation still doesn't appear after 30 minutes, check that the hub site's Targets ACL isn't null in a way that blocks the association from being honored.

How do I remove a site from a hub site, can I use the same JoinHubSite endpoint?

Yes, disassociation uses the exact same JoinHubSite endpoint. Instead of passing the hub site's GUID, pass all zeros: POST /_api/site/JoinHubSite('00000000-0000-0000-0000-000000000000'). This zeroed GUID is SharePoint's documented "no hub" sentinel value. You'll get HTTP 204 on success. After the call, allow a few minutes for propagation before checking the child site, hub navigation should disappear and the site should revert to its standalone appearance.

What does the forceRefresh parameter in HubSiteData actually do, and when should I use it?

When forceRefresh is false (the default), SharePoint returns hub site data from its server-side cache, which can be stale by minutes or even longer. When you pass true, like GET /_api/web/HubSiteData(true), the server invalidates that cache entry, fetches fresh data from the source, and returns that fresh data in the response. Use forceRefresh=true any time you've just made a change to hub configuration and need to verify it took effect. Use false in high-traffic read scenarios where cache staleness isn't a concern, since it's measurably faster.

Can I associate a hub site with a parent hub site, and if so how?

Yes, SharePoint supports hierarchical hub site structures where one hub site can itself be associated with a parent hub. You configure this through a MERGE update to the GetById endpoint, including a ParentHubSiteId field in your request body alongside the standard Title, LogoUrl, and Description fields. Set ParentHubSiteId to the GUID of the parent hub site. This is how large organizations build tiered intranet structures, for example, a "Marketing" hub under a "Corporate" hub, while keeping navigation and branding manageable at each level.

Related Microsoft Fix Guides

H
Sai Kiran Pandrala
Our team includes certified Microsoft engineers, Azure architects, and system administrators with 10+ years of enterprise IT experience. Every guide is written from hands-on troubleshooting, not guesswork. We test every fix before publishing.