Context
Project Cortex uses AI for recognizing content types across systems then creates a knowledge network based on relationships among topics, content, and people (Viva Topics/SharePoint Syntex). This is my second article about Content services in Microsoft 365 and today I’m covering Content types APIs in Microsoft Graph (beta endpoint) which provide CRUD operations like creating/updating/deleting content types and their properties on tenant sites and hub sites. I recommend you to read my first article about Taxonomy APIs before continuing reading this one 😉 Now let’s jump into the content types world and see how it works together!

Permissions
To perform basic CRUD operations (read/write) on tenant sites and hub sites content types, you need at least the permission below in a Delegated (work or school account) or Application context:
Sites.ReadWrite.All
As a great use case here, you can grant an app read/write permissions to a specific site by using the brand new sites scoped permissions now available!
Permission required (Application context only):
Sites.Selected
Endpoint to grant app permissions to a specific site:
POST https://graph.microsoft.com/v1.0/sites/{siteId}/permissions
JSON request body:
{
"roles": [
"write"
],
"grantedToIdentities": [
{
"application": {
"id": "{AppId}",
"displayName": "{AppName}"
}
}
]
}
Note: {AppId} is the Application (client) ID and {AppName} the display name of your app in Azure AD App registrations blade. It seems that there is a problem with sp.full control (owner) role which never worked on my side after multiple tries (Invalid request).
With that method, you don’t need to grant the Sites.Read.All or Sites.ReadWrite.All permission to your dedicated Azure AD application which respect the least privilege principle. It’s a really great new add from Microsoft Graph team to refine app permissions!
Perform basic operations
First we can retrieve all content types from a specific site, you need to replace {siteId} in the query:
GET https://graph.microsoft.com/beta/sites/{siteId}/contentTypes
Note: you can execute the same operation for a specific list too, you need to replace the {listId} in the query:
GET https://graph.microsoft.com/beta/sites/{siteId}/lists/{listId}/contentTypes
Then we can retrieve a specific content type metadatas, you need to replace {siteId} and {contentTypeId} in the query:
GET https://graph.microsoft.com/beta/sites/{siteId}/contentTypes/{contentTypeId}
JSON response body:
{
"@odata.context": "https://graph.microsoft.com/beta/$metadata#sites('<SHAREPOINT_TENANT_ID>')/contentTypes/$entity",
"@odata.etag": "\"1\"",
"id": "0x01004DFD4872F860FD43AB3F75D69CF6141E",
"isBuiltIn": false,
"description": "Create a new list item.",
"group": "Custom Content Types",
"hidden": false,
"name": "Test Content Type",
"parentId": "0x01",
"readOnly": true,
"sealed": false,
"base": {
"id": "0x01",
"description": "Create a new list item.",
"group": "List Content Types",
"hidden": false,
"name": "Item",
"readOnly": false,
"sealed": false
}
}
Note: you can execute the same operation on a specific list with the same schema presented before.
You can query all the columns associated to a specific content type, you need to replace {siteId} and {contentTypeId} in the query:
GET https://graph.microsoft.com/beta/sites/{siteId}/contentTypes/{contentTypeId}/columns
JSON response body:
{
"@odata.context": "https://graph.microsoft.com/beta/$metadata#sites('<SHAREPOINT_TENANT_ID>')/contentTypes('0x01004DFD4872F860FD43AB3F75D69CF6141E')/columns",
"value": [
{
"@odata.etag": "\"0\"",
"columnGroup": "_Hidden",
"description": "",
"displayName": "Content Type",
"enforceUniqueValues": false,
"hidden": false,
"id": "c042a256-787d-4a6f-8a8a-cf6ab767f12d",
"indexed": false,
"isDeletable": false,
"isReorderable": false,
"isSealed": true,
"propagateChanges": false,
"name": "ContentType",
"readOnly": false,
"required": false,
"type": "unknownFutureValue"
},
{
"@odata.etag": "\"0\"",
"columnGroup": "_Hidden",
"description": "",
"displayName": "Title",
"enforceUniqueValues": false,
"hidden": false,
"id": "fa564e0f-0c70-4ab9-b863-0177e6ddd247",
"indexed": false,
"isDeletable": false,
"isReorderable": true,
"isSealed": false,
"propagateChanges": false,
"name": "Title",
"readOnly": false,
"required": true,
"type": "text",
"text": {
"allowMultipleLines": false,
"appendChangesToExistingText": false,
"linesForEditing": 0,
"maxLength": 255
},
"validation": {
"defaultLanguage": "en-US",
"descriptions": [
{
"languageTag": "en-US"
}
]
}
},
{
"@odata.etag": "\"0\"",
"columnGroup": "Custom Columns",
"description": "",
"displayName": "Test Col",
"enforceUniqueValues": false,
"hidden": false,
"id": "ed4892ba-d132-4f96-b157-e86d4f98d6be",
"indexed": false,
"isDeletable": true,
"isReorderable": true,
"isSealed": false,
"propagateChanges": false,
"name": "Test_x0020_Col",
"readOnly": false,
"required": true,
"type": "boolean",
"boolean": {},
"defaultValue": {
"value": "0"
}
}
]
}
Note: you can execute the same operation on a specific list with the same schema presented before.
Or you can query a specific column metadatas associated to a specific content type, you need to replace {siteId}, {contentTypeId} and {columnId} in the query:
GET https://graph.microsoft.com/beta/sites/{siteId}/contentTypes/{contentTypeId}/columns/{columnId}
JSON response body:
{
"@odata.context": "https://graph.microsoft.com/beta/$metadata#sites('<SHAREPOINT_TENANT_ID>')/contentTypes('0x01004DFD4872F860FD43AB3F75D69CF6141E')/columns/$entity",
"@odata.etag": "\"0\"",
"columnGroup": "Custom Columns",
"description": "",
"displayName": "Test Col",
"enforceUniqueValues": false,
"hidden": false,
"id": "ed4892ba-d132-4f96-b157-e86d4f98d6be",
"indexed": false,
"isDeletable": true,
"isReorderable": true,
"isSealed": false,
"propagateChanges": false,
"name": "Test_x0020_Col",
"readOnly": false,
"required": true,
"type": "boolean",
"boolean": {},
"defaultValue": {
"value": "0"
}
}
Note: you can execute the same operation on a specific list with the same schema presented before.
Now let’s see how to create a brand new content type in a specific site, you need to replace {siteId} in the query:
POST https://graph.microsoft.com/beta/sites/{siteId}/contentTypes
JSON request body:
{
"isBuiltIn": false,
"description": "This is a demo content type.",
"group": "Custom Content Types",
"hidden": false,
"name": "New Content Type",
"parentId": "0x01",
"readOnly": true,
"sealed": false,
"base": {
"id": "0x01",
"description": "Create a new list item.",
"group": "List Content Types",
"hidden": false,
"name": "Item",
"readOnly": false,
"sealed": false
}
}
Note: creating a new content type is not possible in a specific list context.
Finally we can delete a content type from a specific site, you need to replace {siteId} and {contentTypeId} in the query:
DELETE https://graph.microsoft.com/beta/sites/{siteId}/contentTypes/{contentTypeId}
Response status will output as below:
HTTP/1.1 204 No Content
Note: you can execute the same operation on a specific list with the same schema presented before.
Publish in hub sites
Microsoft Graph Content types APIs provide capabilities to manage content types publishing in hub sites (for example contentTypeHub site), here are some basic examples of use:
POST https://graph.microsoft.com/beta/sites/{siteId}/contentTypes/{contentTypeId}/publish
POST https://graph.microsoft.com/beta/sites/{siteId}/contentTypes/{contentTypeId}/unpublish
POST https://graph.microsoft.com/beta/sites/{siteId}/contentTypes/{contentTypeId}/isPublished
So it was an overview of basic operations with Microsoft Graph Content type APIs and like my previous article on Taxonomy APIs, another article is coming soon to cover advanced scenarios like hub sites management and associations (SharePoint Syntex) and use cases to understand better these great tools.
Common use cases
Like Taxonomy APIs, the common use cases here are for line of business applications which need to perform CRUD operations on tenant sites content types. With the content services APIs it’s now more easy to sync or migrate your third party content management systems metadatas to Microsoft 365 echosystem (for ex. Drupal, Joomla, WordPress, IBM, SAP, Talend, third party MDM, etc.) and I’m sure these APIs will evolve with the upcoming Project Cortex products.
Happy coding everyone!
Resources
https://developer.microsoft.com/en-us/graph
https://docs.microsoft.com/en-us/graph/api/resources/contenttype?view=graph-rest-beta
https://docs.microsoft.com/en-us/graph/api/site-post-contenttypes?view=graph-rest-beta
https://docs.microsoft.com/en-us/graph/api/contenttype-get?view=graph-rest-beta
https://docs.microsoft.com/en-us/graph/api/contenttype-update?view=graph-rest-beta
https://docs.microsoft.com/en-us/graph/api/contenttype-delete?view=graph-rest-beta