Context
Taxonomy APIs actually in preview provide CRUD operations of the taxonomy service within Project Cortex. These APIs are available for third party developers on the Microsoft Graph beta endpoint with capacities like creating/updating/deleting a group, set, term, relation and their properties. Now let’s see how it works together!

Permissions
First, you need to make sure that current user has at least a Contributor role on the term group on which you want to perform CRUD operations:

If you don’t grant this permission to the current user, you’ll get an error like this when performing operations on terms:
{
"error": {
"code": "accessDenied",
"message": "The current user has insufficient permissions to perform this operation.",
"innerError": {
"date": "2021-03-06T18:06:19",
"request-id": "a55c82d5-c733-4d51-ab97-1a32f49ca95a",
"client-request-id": "a55c82d5-c733-4d51-ab97-1a32f49ca95a"
}
}
}
Second, all operations below are using the Delegated (work or school account) permission type to call the APIs simply because Application permission type is not supported (at the moment?) on the APIs. You’ll need admin consent for this permission and to add the right scope in your access token request as below:
https://graph.microsoft.com/TermStore.ReadWrite.All
Entities
At the moment, you can perform CRUD operations on 5 main entities:
- termStore
- group
- set
- term
- relation
Perform basic operations
First, we can query termStore top level entity which manage taxonomy for a Microsoft 365 tenant:
GET https://graph.microsoft.com/beta/termStore
JSON response:
{
"@odata.context": "https://graph.microsoft.com/beta/$metadata#termStore",
"defaultLanguageTag": "en-US",
"languageTags": [
"en-US",
"fr-FR"
]
}
Then we can list all groups in the tenant:
GET https://graph.microsoft.com/beta/termStore/groups
JSON response:
{
"@odata.context": "https://graph.microsoft.com/beta/$metadata#termStore/groups",
"value": [
{
"id": "6b4fa91a-e22e-4a22-b187-971db304f533",
"description": "",
"displayName": "Home Site",
"createdDateTime": "2021-03-04T14:51:20.673Z",
"scope": "global"
},
{
"id": "5e7d3365-8cc7-4c0f-97e1-88ef6865a4ee",
"description": "",
"displayName": "People",
"createdDateTime": "2020-11-21T11:54:01.067Z",
"scope": "global"
},
{
"id": "472f28a6-30db-401e-a042-0dd4a48c1121",
"description": "These term sets are used by the system itself.",
"displayName": "System",
"createdDateTime": "2020-11-21T11:35:18.207Z",
"scope": "system"
}
]
}
We can list all sets in a group too, you just need to replace the groupId in the query:
GET https://graph.microsoft.com/beta/termStore/groups/{groupId}/sets
JSON response:
{
"@odata.context": "https://graph.microsoft.com/beta/$metadata#termStore/groups('6b4fa91a-e22e-4a22-b187-971db304f533')/sets",
"value": [
{
"id": "adbd6857-b298-41b0-88c3-ab205545b4a3",
"description": "",
"createdDateTime": "2021-03-04T14:51:29.057Z",
"localizedNames": [
{
"name": "Topics",
"languageTag": "en-US"
}
]
}
]
}
Now let’s create a new term in my term set named Topics, you need to replace the setId in the query:
POST https://graph.microsoft.com/beta/termStore/sets/{setId}/children
JSON request body:
{
"labels": [
{
"languageTag" : "en-US",
"name" : "Dynamics 365",
"isDefault" : true
}
]
}
JSON response:
{
"@odata.context": "https://graph.microsoft.com/beta/$metadata#termStore/sets('adbd6857-b298-41b0-88c3-ab205545b4a3')/children/$entity",
"id": "93eceb56-e404-4c4c-a9fd-df4ebcdc651a",
"createdDateTime": "2021-03-06T19:00:47.953Z",
"lastModifiedDateTime": "2021-03-06T19:00:47.953Z",
"labels": [
{
"name": "Dynamics 365",
"isDefault": true,
"languageTag": "en-US"
}
],
"descriptions": []
}
Let’s update this term to add a translation in French language, you need to replace setId and termId in the query:
PATCH https://graph.microsoft.com/beta/termStore/sets/{setId}/terms/{termId}
JSON request body:
{
"labels": [
{
"languageTag": "en-US",
"name": "Dynamics 365",
"isDefault": true
},
{
"languageTag": "fr-FR",
"name": "Dynamics 365",
"isDefault": true
}
]
}
JSON response:
{
"@odata.context": "https://graph.microsoft.com/beta/$metadata#termStore/sets('adbd6857-b298-41b0-88c3-ab205545b4a3')/terms/$entity",
"id": "93eceb56-e404-4c4c-a9fd-df4ebcdc651a",
"createdDateTime": "2021-03-06T19:00:47.953Z",
"lastModifiedDateTime": "2021-03-06T19:01:56.953Z",
"labels": [
{
"name": "Dynamics 365",
"isDefault": true,
"languageTag": "en-US"
},
{
"name": "Dynamics 365",
"isDefault": true,
"languageTag": "fr-FR"
}
],
"descriptions": []
}
After the update, we can now read all terms in the Topics set, you need to replace setId in the query:
GET https://graph.microsoft.com/beta/termStore/sets/{setId}/children
JSON response:
{
"@odata.context": "https://graph.microsoft.com/beta/$metadata#termStore/sets('adbd6857-b298-41b0-88c3-ab205545b4a3')/children",
"value": [
{
"id": "93eceb56-e404-4c4c-a9fd-df4ebcdc651a",
"createdDateTime": "2021-03-06T19:00:47.953Z",
"lastModifiedDateTime": "2021-03-06T20:17:23.687Z",
"labels": [
{
"name": "Dynamics 365",
"isDefault": true,
"languageTag": "en-US"
},
{
"name": "Dynamics 365",
"isDefault": true,
"languageTag": "fr-FR"
}
],
"descriptions": []
},
{
"id": "831bc315-e61b-4299-bd23-e41db7e02490",
"createdDateTime": "2021-03-04T14:51:52.223Z",
"lastModifiedDateTime": "2021-03-07T11:24:24.117Z",
"labels": [
{
"name": "Graph",
"isDefault": true,
"languageTag": "en-US"
},
{
"name": "Graph",
"isDefault": true,
"languageTag": "fr-FR"
}
],
"descriptions": []
},
{
"id": "79c9d6e4-f2af-4ff7-9162-dd303021742c",
"createdDateTime": "2021-03-04T14:52:13.1Z",
"lastModifiedDateTime": "2021-03-07T11:24:32.47Z",
"labels": [
{
"name": "Power Platform",
"isDefault": true,
"languageTag": "en-US"
},
{
"name": "Power Platform",
"isDefault": true,
"languageTag": "fr-FR"
}
],
"descriptions": []
},
{
"id": "9592c007-ac6a-4721-9104-d05467a56350",
"createdDateTime": "2021-03-04T14:51:34.073Z",
"lastModifiedDateTime": "2021-03-07T11:24:38.853Z",
"labels": [
{
"name": "SharePoint",
"isDefault": true,
"languageTag": "en-US"
},
{
"name": "SharePoint",
"isDefault": true,
"languageTag": "fr-FR"
}
],
"descriptions": []
},
{
"id": "469e3fbc-b8a7-44fa-bdc6-35396d67a381",
"createdDateTime": "2021-03-04T14:51:42.017Z",
"lastModifiedDateTime": "2021-03-07T11:24:45.807Z",
"labels": [
{
"name": "Teams",
"isDefault": true,
"languageTag": "en-US"
},
{
"name": "Teams",
"isDefault": true,
"languageTag": "fr-FR"
}
],
"descriptions": []
},
{
"id": "734f93cd-0b7b-4b78-89ef-16892a51705b",
"createdDateTime": "2021-03-04T14:51:56.97Z",
"lastModifiedDateTime": "2021-03-06T20:17:23.76Z",
"labels": [
{
"name": "Viva",
"isDefault": true,
"languageTag": "en-US"
},
{
"name": "Viva",
"isDefault": true,
"languageTag": "fr-FR"
}
],
"descriptions": []
}
]
}
Finally we can delete the term, you need to replace setId and termId in the query:
DELETE https://graph.microsoft.com/beta/termStore/sets/{setId}/terms/{termId}
Response will output as follow:
HTTP/1.1 204 No Content
So it was just an overview of what Taxonomy APIs can do but there are more topics to cover like relationships, subtopics, hierarchy, etc. Another article is coming very soon to cover advanced topics and use cases with these APIs!
General availability
Taxonomy service APIs will go GA during this month (March) related to Microsoft Graph roadmap:
Description | Status | Tags | Release |
---|---|---|---|
Microsoft Graph: Taxonomy service Graph APIs | In development | Microsoft GraphWebWorldwide (Standard Multi-Tenant)General Availability | March CY2021 |
Common use cases
The common use cases here are for line of business applications which need to perform CRUD operations on the tenant term store and I’m pretty sure these APIs will evolve with the Project Cortex actual and upcoming products (Viva Topics, SharePoint Syntex, etc.) in the future… 😉
Happy coding everyone!
Resources
https://developer.microsoft.com/en-us/graph
https://docs.microsoft.com/en-us/graph/api/resources/termstore-store
https://docs.microsoft.com/en-us/graph/api/resources/termstore-group
https://docs.microsoft.com/en-us/graph/api/resources/termstore-set
https://docs.microsoft.com/en-us/graph/api/resources/termstore-term
https://docs.microsoft.com/en-us/graph/api/resources/termstore-relation