Add custom data to resources using extensions

Microsoft Graph provides a single API endpoint to access rich people-centric data and insights through resources such as user and message. You can also extend Microsoft Graph by adding custom properties to resource instances without requiring an external data store.

This article describes how Microsoft Graph supports extending its resources, the options available to add custom properties, and when to use them.

Important

Do not use extensions to store sensitive personally identifiable information, such as account credentials, government identification numbers, cardholder data, financial account data, healthcare information, or sensitive background information.

The extensions mentioned in this article are not similar to custom security attributes. To understand their differences, see How do custom security attributes compare with extensions?

Why add custom data to Microsoft Graph?

  • As an ISV developer, you might decide to keep your app lightweight and store app-specific user profile data in Microsoft Graph by extending the user resource.
  • Alternatively, you might want to retain your app's existing user profile store, and add an app-specific identifier to the user resource.
  • As an enterprise developer, the in-house applications that you build might rely on your organization's HR-specific data. Integration within multiple applications can be simplified by storing this custom data in Microsoft Graph.

Custom data options in Microsoft Graph

Microsoft Graph offers four types of extensions for adding custom data.

  • Extension attributes
  • Directory (Microsoft Entra ID) extensions
  • Schema extensions
  • Open extensions

Extension attributes

Microsoft Entra ID offers a set of 15 extension attributes with predefined names on the user and device resources. These properties were initially custom attributes provided in on-premises Active Directory (AD) and Microsoft Exchange. However, they can now be used for more than syncing on-premises AD and Microsoft Exchange data to Microsoft Entra ID through Microsoft Graph.

Developer experience

You can use the 15 extension attributes to store String values on user or device resource instances, through the onPremisesExtensionAttributes and extensionAttributes properties respectively. You can assign the values while creating a new resource instance or while updating an existing resource instance. You can also filter by the values.

Add or update data in extension attributes

The following example shows how to store data in extensionAttribute1 and delete existing data from extensionAttribute13 through an update operation with a PATCH method.

PATCH https://graph.microsoft.com/v1.0/users/071cc716-8147-4397-a5ba-b2105951cc0b

{
    "onPremisesExtensionAttributes": {
        "extensionAttribute1": "skypeId.adeleVance",
        "extensionAttribute13": null
    }
}

The request returns a 204 No Content response object.

Read the extension attributes

Request
GET https://graph.microsoft.com/v1.0/users?$select=id,displayName,onPremisesExtensionAttributes
Response
{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users(id,displayName,onPremisesExtensionAttributes)",
    "value": [
        {
            "id": "071cc716-8147-4397-a5ba-b2105951cc0b",
            "displayName": "Adele Vance",
            "onPremisesExtensionAttributes": {
                "extensionAttribute1": "Contractor",
                "extensionAttribute2": "50",
                "extensionAttribute3": null,
                "extensionAttribute4": "1478354",
                "extensionAttribute5": "10239390",
                "extensionAttribute6": null,
                "extensionAttribute7": null,
                "extensionAttribute8": null,
                "extensionAttribute9": null,
                "extensionAttribute10": "11",
                "extensionAttribute11": null,
                "extensionAttribute12": "/o=ExchangeLabs/ou=Exchange Administrative Group (FYDIBOHF47SPDLT)/cn=Recipients/cn=5ee781fc7egc7aa0b9394bddb44e7f04-Adele Vance",
                "extensionAttribute13": null,
                "extensionAttribute14": null,
                "extensionAttribute15": null
            }
        }
    ]
}

Considerations for using extension attribute properties

The onPremisesExtensionAttributes object can be updated only for objects that aren't synced from on-premises AD.

The 15 extension attributes are already predefined in Microsoft Graph and their property names can't be changed. Therefore, you can't use custom names such as SkypeId for the extension attributes. Your organization must therefore track the extension attribute properties in use to avoid inadvertently overwriting their data.

Directory (Microsoft Entra ID) extensions

Directory extensions provide developers with a strongly typed, discoverable and filterable extension experience for directory objects.

Directory extensions are first registered on an application through the Create extensionProperty operation and must be explicitly targeted to specific and supported directory objects. After a user or an admin has consented to the application in the tenant, the extension properties become immediately accessible in the tenant. All authorized applications in the tenant can read and write data on any extension properties defined on an instance of the target directory object.

For the list of resource types that can be specified as target objects for a directory extension, see Comparison of extension types.

Developer experience

Directory extension definitions are managed through the extensionProperty resource and its associated methods. The data is managed through the REST API requests that you use to manage the resource instance.

Define the directory extension

Before you can add a directory extension to a resource instance, you must first define the directory extension.

Request

In the following request, 30a5435a-1871-485c-8c7b-65f69e287e7b is the object ID of the application that owns the directory extension. You can create directory extensions that store a collection of values.

POST https://graph.microsoft.com/v1.0/applications/30a5435a-1871-485c-8c7b-65f69e287e7b/extensionProperties

{
    "name": "jobGroupTracker",
    "dataType": "String",
    "targetObjects": [
        "User"
    ]
}
Response

A directory extension property named extension_b7d8e648520f41d3b9c0fdeb91768a0a_jobGroupTracker is created with an extension name that follows the following naming convention: extension_{appId-without-hyphens}_{extensionProperty-name}.

HTTP/1.1 201 Created
Content-type: application/json

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#applications('30a5435a-1871-485c-8c7b-65f69e287e7b')/extensionProperties/$entity",
    "id": "4e3dbc8f-ca32-41b4-825a-346215d7d20f",
    "deletedDateTime": null,
    "appDisplayName": "HR-sync-app",
    "dataType": "String",
    "isMultiValued": false,
    "isSyncedFromOnPremises": false,
    "name": "extension_b7d8e648520f41d3b9c0fdeb91768a0a_jobGroupTracker",
    "targetObjects": [
        "User"
    ]
}

Add a directory extension property to a target object

After defining the directory extension, you can now add it to an instance of a target object type. You can store data in the directory extension when creating a new instance of the target object or when updating an existing object. The following example shows how to store data in the directory extension when creating a new user object.

POST https://graph.microsoft.com/v1.0/users

{
    "accountEnabled": true,
    "displayName": "Adele Vance",
    "mailNickname": "AdeleV",
    "userPrincipalName": "AdeleV@contoso.com",
    "passwordProfile": {
        "forceChangePasswordNextSignIn": false,
        "password": "xWwvJ]6NMw+bWH-d"
    },
    "extension_b7d8e648520f41d3b9c0fdeb91768a0a_jobGroupTracker": "JobGroupN"
}

The request returns a 201 Created response code and a user object in the response body.

Retrieve a directory extension

The following example shows how the directory extensions and associated data are presented on a resource instance. The extension property is returned by default through the beta endpoint, but only on $select through the v1.0 endpoint.

Request

GET https://graph.microsoft.com/beta/users?$select=id,displayName,extension_b7d8e648520f41d3b9c0fdeb91768a0a_jobGroupTracker,extension_b7d8e648520f41d3b9c0fdeb91768a0a_permanent_pensionable

Response

HTTP/1.1 200 OK
Content-type: application/json

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users(id,displayName,extension_b7d8e648520f41d3b9c0fdeb91768a0a_jobGroupTracker,extension_b7d8e648520f41d3b9c0fdeb91768a0a_permanent_pensionable)",
    "value": [
        {
            "id": "63384f56-42d2-4aa7-b1d6-b10c78f143a2",
            "displayName": "Adele Vance",
            "extension_b7d8e648520f41d3b9c0fdeb91768a0a_jobGroupTracker": "E4",
            "extension_b7d8e648520f41d3b9c0fdeb91768a0a_permanent_pensionable": true
        }
    ]
}

Update or delete directory extensions

To update or delete the value of the directory extension for a resource instance, use the PATCH method. To delete the extension property and its associated value, set its value to null.

The following request updates the value of one directory extension and deletes another extension property.

PATCH https://graph.microsoft.com/v1.0/users/63384f56-42d2-4aa7-b1d6-b10c78f143a2

{
    "extension_b7d8e648520f41d3b9c0fdeb91768a0a_permanent_pensionable": null,
    "extension_b7d8e648520f41d3b9c0fdeb91768a0a_jobGroupTracker": "E4"
}

The request returns a 204 No Content response code.

Considerations for using directory extensions

If you accidentally delete a directory extension definition, any data stored in the associated property becomes undiscoverable. To recover the data, create a new directory extension definition with the same name as the deleted definition, on the same owner app.

When a definition object is deleted before the corresponding extension property is updated to null, the property counts against the 100-limit for the object.

When the definition is deleted before data in the associated extension property is deleted, there's no way to know the existence of the extension property via Microsoft Graph - even though the undiscoverable property counts against the 100-limit.

Deleting an owner app in the home tenant makes the associated directory extensions and their data undiscoverable. When you restore an owner app, it restores the directory extension definitions but doesn't make the directory extension properties or their data immediately discoverable; because restoring an app doesn't automatically restore the associated service principal in the tenant. To make the directory extension properties and their data discoverable, either create a new service principal or restore the deleted service principal. NO changes are made to other tenants where the app has been consented to.

Schema extensions

Microsoft Graph schema extensions are conceptually similar to directory extensions. First, you define your schema extension. Then, use it to extend supported resource instances with strongly typed custom properties. In addition, you can control the status of your schema extension and let it be discoverable by other apps.

For the list of resource types that support schema extensions, see Comparison of extension types.

Developer experience

When creating a schema extension definition, you must provide a unique name for its id. There are two naming options:

  • If you already have a vanity .com,.net, .gov, .edu or a .org domain that's verified with your tenant, you can use the domain name along with the schema name to define a unique name, in this format {domainName}_{schemaName}. For example, if your vanity domain is contoso.com, you can define an id of contoso_mySchema. This option is highly recommended.
  • Alternatively, you can set the id to a schema name (without a domain name prefix). For example, mySchema. Microsoft Graph assigns a string ID for you based on the supplied name, in this format: ext{8-random-alphanumeric-chars}_{schema-name}. For example, extkvbmkofy_mySchema.

The id is the name of the complex type that stores your data on the extended resource instance.

Once you register a schema extension, it's available to be used by all applications in the same tenant as the associated owner application (when in the InDevelopment state) or by all applications in any tenant (when in the Available state). Like directory extensions, authorized apps have the ability to read and write data on any extensions defined on the target object.

You manage the schema extension definitions and the data in the corresponding schema extension property using separate sets of API operations. To manage the schema extension data on the extended resource instance, use the same REST request that you use to manage the resource instance.

  • Use POST to store data in the schema extension property when you're creating a new user.
  • Use PATCH to either store data in the schema extension property or update or delete the stored data.
    • To delete data from a property, set its value to null.
    • To delete data from all properties, set every property to null. If all properties are null, the schema extension object is also deleted.
    • To update any property, you must specify all properties in the request body. Otherwise, Microsoft Graph updates the unspecified properties to null.
  • Use GET to read the schema extension properties for all users or individual users in the tenant.

Define a schema extension

Request
POST https://graph.microsoft.com/v1.0/schemaExtensions

{
    "id": "graphLearnCourses",
    "description": "Graph Learn training courses extensions",
    "targetTypes": [
        "user"
    ],
    "properties": [
        {
            "name": "courseId",
            "type": "Integer"
        },
        {
            "name": "courseName",
            "type": "String"
        },
        {
            "name": "courseType",
            "type": "String"
        }
    ]
}
Response
{
    "@odata.context": "https://graph.microsoft.com/beta/$metadata#schemaExtensions/$entity",
    "id": "extkmpdyld2_graphLearnCourses",
    "description": "Graph Learn training courses extensions",
    "targetTypes": [
        "user"
    ],
    "status": "InDevelopment",
    "properties": [
        {
            "name": "courseId",
            "type": "Integer"
        },
        {
            "name": "courseName",
            "type": "String"
        },
        {
            "name": "courseType",
            "type": "String"
        }
    ]
}

Add a schema extension to a resource instance

After defining the schema extension, you can now add the extension property to an instance of a target object type. You can store data in the schema extension when creating a new instance of the target object or when updating an existing object. The following example shows how to store data in the schema extension property when creating a new user object.

POST https://graph.microsoft.com/beta/users

{
    "accountEnabled": true,
    "displayName": "Adele Vance",
    "mailNickname": "AdeleV",
    "userPrincipalName": "AdeleV@contoso.com",
    "passwordProfile": {
        "forceChangePasswordNextSignIn": false,
        "password": "xWwvJ]6NMw+bWH-d"
    },
    "extkmpdyld2_graphLearnCourses": {
        "courseId": 100,
        "courseName": "Explore Microsoft Graph",
        "courseType": "Online"
    }
}

The request returns a 201 Created response code and a schemaExtension object in the response body

Update or delete a schema extension property

Use the PATCH operation to update a schema extension or delete an existing schema extension. To delete the extension property and its associated value from the resource instance, set its value to null.

The following example deletes the value of the courseId property and updates the courseType property. To delete the extkmpdyld2_graphLearnCourses extension property in its entirety, set its value to null.

PATCH https://graph.microsoft.com/beta/users/0668e673-908b-44ea-861d-0661297e1a3e

{
    "extkmpdyld2_graphLearnCourses": {
        "courseType": "Instructor-led",
        "courseId": null
    }
}

The request returns a 204 No Content response object.

Retrieve the schema extension property

To read the schema extension properties on a resource instance, specify the extension name in a $select request.

Request
GET https://graph.microsoft.com/beta/users/0668e673-908b-44ea-861d-0661297e1a3e?$select=id,displayName,extkmpdyld2_graphLearnCourses
Response
HTTP/1.1 200 OK
Content-type: application/json

{
    "@odata.context": "https://graph.microsoft.com/beta/$metadata#users(id,displayName,extkmpdyld2_graphLearnCourses)/$entity",
    "id": "63384f56-42d2-4aa7-b1d6-b10c78f143a2",
    "displayName": "Adele Vance",
    "extkmpdyld2_graphLearnCourses": {
        "@odata.type": "#microsoft.graph.ComplexExtensionValue",
        "courseType": "Instructor-led",
        "courseName": "Explore Microsoft Graph",
        "courseId": null
    }
}

Considerations for using schema extensions

A schema extension must have an owner app. Ownership of the schema extension can't be reassigned to another app.

Deleting a schema extension definition without setting the schema extension to null makes the property and its associated user data undiscoverable.

Deleting an owner app in the home tenant doesn't delete the associated schema extension definition or the property and the data it stores. The schema extension property can still be read, deleted, or updated for users. However, the schema extension definition can't be updated.

Open extensions

Microsoft Graph open extensions are open types that offer a simple and flexible way to add untyped data directly to a resource instance. These extensions aren't strongly typed, discoverable, or filterable.

For the list of resource types that support Microsoft Graph open extensions, see Comparison of extension types.

Developer experience

Open extensions, together with their data, are accessible through the extensions navigation property of the resource instance. They allow you to group related properties for easier access and management.

You define and manage open extensions on the fly on resource instances. They're considered unique for each object, and you don't need to apply a universally consistent pattern for all objects. For example, in the same tenant:

  • The user object for Adele can have an open extension named socialSettings that has three properties: linkedInProfile, skypeId, and xboxGamertag.
  • The user object for Bruno can have no open extension property.
  • The user object for Alex can have an open extension named socialSettings with five properties: theme, color, language, font, and fontSize.

Create an open extension

The following example shows an open extension definition with three properties and how the custom properties and associated data are presented on a resource instance.

POST https://graph.microsoft.com/v1.0/users/3fbd929d-8c56-4462-851e-0eb9a7b3a2a5/extensions

{
    "@odata.type": "#microsoft.graph.openTypeExtension",
    "extensionName": "com.contoso.socialSettings",
    "skypeId": "skypeId.AdeleV",
    "linkedInProfile": "www.linkedin.com/in/testlinkedinprofile",
    "xboxGamerTag": "AwesomeAdele",
    "id": "com.contoso.socialSettings"
}

The request returns a 201 Created response code and an openTypeExtension object in the response body.

Update an existing open extension

To update an open extension, you must specify all its properties in the request body. Otherwise, the unspecified properties are updated to null and deleted from the open extension.

The following request specifies only the linkedInProfile and xboxGamerTag properties. The value of the xboxGamerTag property is being updated while the linkedInProfile property remains the same. This request also deletes the unspecified skypeId property.

PATCH https://graph.microsoft.com/v1.0/users/3fbd929d-8c56-4462-851e-0eb9a7b3a2a5/extensions/com.contoso.socialSettings

{
    "xboxGamerTag": "FierceAdele",
    "linkedInProfile": "www.linkedin.com/in/testlinkedinprofile"
}

This request returns a 204 No Content response code.

Retrieve the open extensions

GET https://graph.microsoft.com/v1.0/users/3fbd929d-8c56-4462-851e-0eb9a7b3a2a5/extensions/com.contoso.socialSettings

{
    "@odata.context": "https://graph.microsoft.com/beta/$metadata#users('3fbd929d-8c56-4462-851e-0eb9a7b3a2a5')/extensions/$entity",
    "@odata.type": "#microsoft.graph.openTypeExtension",
    "xboxGamerTag": "FierceAdele",
    "linkedInProfile": "www.linkedin.com/in/testlinkedinprofile",
    "id": "com.contoso.socialSettings"
}

Considerations for using open extensions

Deleting a creator app doesn't affect the open extension and the data it stores.

Comparison of extension types

The following table compares the extension types, which should help you decide which option is most appropriate for your scenario.

Capability Extension attributes 1-15 Directory extensions Schema extensions Open extensions
Supported resource types user
device
user
group
administrativeUnit
application
device
organization
user
group
administrativeUnit
contact
device
event (both user and group calendars)
message
organization
post
user
group
contact
device
event1 (both user and group calendars)
message
organization
post
todoTask
todoTaskList
Strongly typed No Yes Yes No
Filterable Yes Yes Yes No
Can store a collection No Yes No No
Tied to an "owner" application No Yes Yes No
Managed via Microsoft Graph
Exchange admin center
Microsoft Graph Microsoft Graph Microsoft Graph
Sync data from on-premises to extensions using AD connect Yes, for users Yes No No
Create dynamic membership rules using custom extension properties and data Yes Yes No No
Usable for customizing token claims Yes Yes (1, 2) No No
Available in Azure AD B2C Yes Yes Yes Yes
Limits
  • 15 predefined attributes per user or device resource instance
  • 100 extension values per resource instance
  • Maximum of five definitions per owner app
  • 100 extension values per resource instance (directory objects only)
  • Two open extensions per creator app per resource instance2
  • Max. of 2 Kb per open extension2
  • For Outlook resources, each open extension is stored in a MAPI named property3
  • Note

    1 Due to an existing service limitation, delegates cannot create open extension-appended events in shared mailbox calendars. Attempts to do so will result in an ErrorAccessDenied response.

    2 These limits on open extensions apply to the following directory resources: user, group, device, and organization.

    3 Each open extension is stored in a MAPI named property, which are a limited resource in a user's mailbox. This limit applies to the following Outlook resources: message, event, and contact

    You can manage all extensions when you're signed in with a work or school account. Additionally, you can manage open extensions for the following resources when signed-in with a personal Microsoft account: event, post, group, message, contact, and user.

    Permissions and privileges

    The same privileges that your app requires to read from or write to a resource instance are also required to manage any extensions data on that resource instance. For example, in a delegated scenario, an app can only update any user's extension data if it's granted the User.ReadWrite.All permission and the signed-in user has a supported Microsoft Entra administrator role.