Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
All apps, including Enterprise AI apps, handle sensitive data that requires protection against data leaks, unauthorized access, and compliance violations. Microsoft Purview policies help organizations protect sensitive information. Your apps can integrate with the Microsoft Purview APIs to ensure Microsoft Purview policies support your apps security posture.
This article provides a walkthrough of how you can add the Microsoft Purview APIs to your existing Enterprise AI app to leverage your policies. At the end of this walkthrough, you will understand:
- When and how to call the Purview APIs for a known user for an activity they are performing in your app.
- Evaluate user prompts and AI responses against those policies.
- Enforce policy actions in your app such as blocking or keeping current with policy modifications in your tenant.
Note
Use Microsoft Purview APIs to send data into Microsoft Purview and support Purview policies associated with that data. There are no APIs available to extract data or analytics from Microsoft Purview.
Prerequisites
Before you begin, ensure you have:
- An Azure subscription with Microsoft Purview configured.
- An application registered in Microsoft Entra ID with the appropriate permissions.
- Basic familiarity with Microsoft Graph API calls.
- Access to the prompts and responses in your GenAI app or agent.
- For end-to-end testing, create policies in your Microsoft Purview portal. For more information on the available policies, see Use Microsoft Purview to manage data security & compliance for Entra-registered AI apps.
API integration overview
Your AI application performs two key API calls to support your Microsoft Purview policies:
Compute protection scopes: Determines which user activities (uploadText, downloadText, uploadFile, downloadFile) require policy evaluation for a specific user.Process content: Your app sends a content activity for policy evaluation and returns policy actions your app must enforce (such as blocking or detecting policy modifications).
The following sections provide step-by-step implementation guidance, including code examples and how to handle responses.
For a detailed walkthrough of a demo app that makes these API calls, see the Microsoft Reactor video.
Step 1: Compute protection scopes for the user
The first step is to identify which policies and restrictions apply to a specific user based on the activities they can perform in your AI application (such as uploading text prompts or downloading AI responses). This is called computing the protection scope of the user.
Protection scopes are an abstraction of policies in the tenant that apply to the user. For any given user and activity that user performs in your app, you want to compute the protection scope. The protection scope indicates the action that the app should take next, which can be evaluate and block, evaluate and don’t block, or no evaluation needed.
Note
We recommend that immediately after user authentication, your app calls Compute protection scopes. To call protectionScopes/compute you must have the user's Entra ID.
Here's an example of a request to protectionScopes/compute.
POST https://graph.microsoft.com/v1.0/users/7c1f8f10-cba8-4a8d-9449-db4b876d1ef70/dataSecurityAndGovernance/protectionScopes/compute
Content-type: application/json
{
"activities": "uploadText,downloadText",
"locations": [
{
"@odata.type": "microsoft.graph.policyLocationApplication",
"value": "83ef208a-0396-4893-9d4f-d36efbffc8bd"
}
]
}
In the above call to compute the protection scope, you must include the user activities that the user is performing in your app. Accepted user activities include:
- uploadText - users enter text prompts to send to the AI.
- downloadText - a text-based response from the AI.
- uploadFile - user submits a file as part of the prompt to the AI.
- downloadFile - a file returned as part of the response from AI.
For more information on user activities, see userActivityTypes values.
The call to compute protection scopes returns a collection of policyUserScopes. Here is an example of a response with 2 protection scopes.
HTTP/1.1 200 OK
Content-type: application/json
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(microsoft.graph.policyUserScope)",
"value": [
{
"activities": "uploadText,downloadText",
"executionMode": "evaluateOffline",
"locations": [
{
"@odata.type": "#microsoft.graph.policyLocationApplication",
"value": "83ef198a-0396-4893-9d4f-d36efbffc8bd"
}
],
"policyActions": []
},
{
"activities": "uploadText",
"executionMode": "evaluateInline",
"locations": [
{
"@odata.type": "#microsoft.graph.policyLocationApplication",
"value": "83ef198a-0396-4893-9d4f-d36efbffc8bd"
}
],
"policyActions": []
}
]
}
It's important that your app parses this response to determine which user activity (for example, uploadText) requires policy evaluation on the content submitted by or sent to the user.
If the returned policyUserScopes collection is empty: No policies apply to the user for the user activity. When no policies apply to this user for this activity, we recommend calling Content activity to log activities for audit compliance and anomaly detection. You can make this a configurable setting in your app.
If the policyUserScopes collection contains scopes:
When protection scopes are returned, your app needs to parse the response by inspecting the values of activities and executionMode for each protection scope. In the above example, 2 protection scopes are returned in the policyUserScopes collection.
executionMode helps you determine what restriction applies to a particular user for a user activity. The following list shows the valid values for executionMode:
evaluateOffline: means you can make an async call to evaluate the content against a policy when callingprocessContent.evaluateInline: means your app's main thread must block until the call returns fromprocessContent.
For more information, see executionMode values.
The activity indicates the user activity that the protection scope applies to. You may find that an activity is repeated in more than 1 protection scope. For example, notice in the above sample that uploadText is returned in both protection scopes. In this situation, your app must apply the more restrictive protection scope to that user activity.
Here's an example of how your app would parse the above returned policyUserScopes collection:
- By parsing the first protection scope, we see the following information:
uploadText(or prompts sent to the AI) anddownloadText(or responses from AI) must be evaluated offline.
- When parsing the second protection scope, we see the following information:
uploadText(or prompts sent to the AI) must be evaluated inline.
- For any of the other user activities (
uploadFile,downloadFile), no protection scopes apply to those user activities. Consider calling Content activity as described above.
The logic your app must implement for these different user activities are as follows:
| User activity | Action in your app |
|---|---|
uploadText |
Block the main thread when calling processContent. |
downloadText |
Make an async call when calling processContent. |
Important
Cache the ETag value: The call to protectionScopes/compute returns an ETag header which represents the current state of the protection scopes for that user. Your app must cache this value and send it with all calls to processContent.
Step 2: Process content
Next, based on the user's protection scope state, your app may need to call processContent.
As described earlier, any user activities where the executionMode was either evaluateInline or evaluateOffline are required to call processContent.
When you make the call, send the ETag value that your app cached from the call to protectionScopes/compute in Step 1 to determine if policy modifications were made in your tenant. You send the ETag value in the If-None-Match header.
Here is an example of a call to processContent.
POST https://graph.microsoft.com/v1.0/me/dataSecurityAndGovernance/processContent
Content-Type: application/json
{
"contentToProcess": {
"contentEntries": [
{
"@odata.type": "microsoft.graph.processConversationMetadata",
"identifier": "07785517-9081-4fe7-a9dc-85bcdf5e9075",
"content": {
"@odata.type": "microsoft.graph.textContent",
"data": "Write an acceptance letter for Alex Wilber with Credit card number 4532667785213500, ssn: 120-98-1437 at One Microsoft Way, Redmond, WA 98052"
},
"name":"PC Purview API Explorer message",
"correlationId": "d63eafd2-e3a9-4c1a-b726-a2e9b9d9580d",
"sequenceNumber": 0,
"isTruncated": false,
"createdDateTime": "2025-05-27T17:23:20",
"modifiedDateTime": "2025-05-27T17:23:20"
}
],
"activityMetadata": {
"activity": "uploadText"
},
"deviceMetadata": {
"deviceType": "Unmanaged",
"operatingSystemSpecifications": {
"operatingSystemPlatform": "Windows 11",
"operatingSystemVersion": "10.0.26100.0"
},
"ipAddress": "127.0.0.1"
},
"protectedAppMetadata": {
"name": "PC Purview API Explorer",
"version": "0.2",
"applicationLocation":{
"@odata.type": "microsoft.graph.policyLocationApplication",
"value": "83ef208a-0396-4893-9d4f-d36efbffc8bd"
}
},
"integratedAppMetadata": {
"name": "PC Purview API Explorer",
"version": "0.2"
}
}
}
Note
Chat implementation guidance:
- If you allow different chat threads or conversations in your AI application, ensure you use a unique
correlationIdfor each chat thread. - If you maintain conversation context in a given thread, increment
sequenceNumberfor each user message (for example, use 0, 1, 2, and so on).
Here is an example of a response from processContent.
HTTP/1.1 200 OK
Content-Type: application/json
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#microsoft.graph.processContentResponse",
"protectionScopeState": "modified",
"policyActions": [
{
"@odata.type": "#microsoft.graph.restrictAccessAction",
"action": "restrictAccess",
"restrictionAction": "block"
}
],
"processingErrors": []
}
In the above example, there are 2 actions your app must take:
- The
processContentResponsecontains theprotectionScopeStateproperty which is set tomodified.modifiedindicates that the policies in the tenant have changed. Because the policies have changed, your app must first callprotectionScopes/computeto get the new protection scopes for that user, which is described in Step 1 above. Ensure you cache the newETagvalue. - Because the
policyActionscollection is not empty, your app needs to step through eachactionto determine the appropriate action it must take. In this example,restrictAccessmeans that your app must block the user from the requested action. If thepolicyActionscollection in theprocessContentResponsewas empty, your app would continue with the requested activity. When building agents, the agent must also block before calling another agent if theactionis set torestrictAccess.
Important
If it's been 60 minutes since your last call to processContent, we recommend calling Compute protection scopes to detect if any policy changes made in the tenant now apply to the user. If there was a change that now applies to the user, the call to protectionScopes/compute returns a new ETag value which must be cached in your app and used when calling processContent.