Manage tenant term store with Microsoft Graph (preview)

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:

  1. termStore
  2. group
  3. set
  4. term
  5. 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:

DescriptionStatusTagsRelease
Microsoft Graph: Taxonomy service Graph APIsIn developmentMicrosoft GraphWebWorldwide (Standard Multi-Tenant)General AvailabilityMarch CY2021
Microsoft Graph roadmap for Taxonomy service APIs.

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

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s