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.
Use the msdyn_EmailClassificationCustomAction Dataverse action to analyze an existing email record and assign one of the active email categories configured for your organization. The action returns the matched category, its Dataverse ID, and an AI-generated explanation so that you can continue with routing, case creation, reporting, or other business logic.
The action updates the Email category (AI) and Classification Status fields on the email record.
Important
Don't invoke this action when built-in automatic email classification has already processed the email. Classifying the same email more than once can increase AI consumption, overwrite existing classification results, and cause downstream processes to execute multiple times.
Prerequisites
- Email classification is available and enabled in Dynamics 365 Customer Service.
- At least two active email categories are configured.
- Pay-as-you-go billing is enabled for the environment.
- The required Copilot, AI agent, and cross-geography permissions are enabled.
- The email record exists in Dataverse.
- The user invoking the action has permissions to read and update the email record and read the email classification configuration.
Request details
| Property | Value |
|---|---|
| Unique name | msdyn_EmailClassificationCustomAction |
| Binding | Global or unbound |
| Method | POST |
| Web API URL | https://<organization-url>/api/data/v9.2/msdyn_EmailClassificationCustomAction |
| Power Automate operation | Microsoft Dataverse - Perform an unbound action |
Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
emailId |
String | Yes | GUID of an existing Dataverse email record. Pass the row ID as text without braces. |
doRuleEvaluation |
Boolean | Yes | Determines whether the configured email classification conditions are evaluated. Use true to apply the conditions configured by the administrator. Use false only when the calling flow checks whether the email meets the required conditions before it calls the action. |
isSimulationMode |
Boolean | No | Enables implementation-specific simulation behavior. Use false for production classification. |
additionalInfo |
String | No | Contains a JSON-encoded subject and body for content-only simulation. Leave this value empty for production classification. |
For production classification, pass the email row ID, set doRuleEvaluation to true, set isSimulationMode to false, and leave additionalInfo empty.
Response properties
| Property | Type | Description |
|---|---|---|
emailclassificationResponse |
String | JSON-encoded classification result that contains the category, reason, and optional hierarchy details. |
matchedCategoryId |
String | GUID of the matched msdyn_emailclassificationcategory row. Use this value for deterministic branching and Dataverse lookups. |
status |
String | JSON-encoded object that contains an application-level code and message. |
Both status and emailclassificationResponse contain JSON stored as strings, not native JSON objects. Some prerequisite, skipped, and exception paths can return empty strings. Check that each value isn't empty before parsing it with the Power Automate json() expression.
Call the action from Power Automate
- Add a Dataverse trigger, or use an earlier flow step that provides the ID of an existing email record.
- Add the Microsoft Dataverse Perform an unbound action step.
- For Action name, select
msdyn_EmailClassificationCustomAction. - Map
emailIdto the email row ID, typicallyactivityid. - Set
doRuleEvaluationtotrue, unless the flow applies equivalent eligibility filtering. - Set
isSimulationModetofalse, and leaveadditionalInfoempty. - Confirm that
statusisn't empty, and then parse it as JSON. - Continue only when
status.codeis200andmatchedCategoryIdisn't empty. - Use
matchedCategoryIdfor category-based routing or other automation.
To parse the status, use the following expression. Replace Perform_an_unbound_action if your action has a different name.
json(body('Perform_an_unbound_action')?['status'])
To parse the classification result, use the following expression:
json(body('Perform_an_unbound_action')?['emailclassificationResponse'])
Important
The action updates the email row. If the flow is triggered when that row changes, filter the trigger columns or check Classification Status before calling the action. Otherwise, the action can retrigger the same flow.
Request example
POST https://<organization-url>/api/data/v9.2/msdyn_EmailClassificationCustomAction
Accept: application/json
Content-Type: application/json; charset=utf-8
OData-MaxVersion: 4.0
OData-Version: 4.0
{
"emailId": "<email-guid>",
"doRuleEvaluation": true,
"isSimulationMode": false
}
Successful response example
HTTP/1.1 200 OK
Content-Type: application/json
{
"@odata.context": "https://<organization-url>/api/data/v9.2/$metadata#Microsoft.Dynamics.CRM.msdyn_EmailClassificationCustomActionResponse",
"emailclassificationResponse": "{\"index\":1,\"category\":\"Marketing\",\"reason\":\"The email promotes a product or service.\",\"status\":null}",
"matchedCategoryId": "<category-guid>",
"status": "{\"code\":200,\"message\":\"Success\"}"
}
Classification response fields
The parsed emailclassificationResponse can contain the following fields.
| Field | Description | Usage guidance |
|---|---|---|
index |
Zero-based position of the selected category in the AI request. | Don't use this value as a durable identifier. |
category |
Name of the selected category. | Use for display. Category names can be edited. |
reason |
AI-generated explanation for the selection. | Use for display or diagnostics, not deterministic branching. |
selectedPath |
Optional hierarchy path when hierarchical classification is enabled. | Use only when the value is present. |
finalCategory |
Optional final hierarchy node selected by the service. | The action uses this value to resolve the Dataverse category. |
confidence |
Optional confidence information for hierarchical results. | Don't assume this value is always present. |
evidence |
Optional model evidence for hierarchical results. | Don't assume this value is always present. |
Classification status values
| Value | Label | Description |
|---|---|---|
0 |
Default | No completed classification outcome is recorded. |
1 |
Skipped | The email wasn't classified because a required condition wasn't met. |
2 |
Success | A category was matched and written to the email record. |
3 |
Failed | The classification attempt encountered an error. |
Handle action outcomes
A successful Power Automate action step doesn't always mean that classification succeeded. Classification is successful only when:
- The action step succeeds.
statusis present.- The parsed
status.codevalue is200. matchedCategoryIdisn't empty.
Use the following guidance for other outcomes:
| Scenario | Expected behavior | Recommended flow handling |
|---|---|---|
| An eligibility condition doesn't match. | Response strings can be empty, and Classification Status is Skipped. | End the flow normally or use a nonclassified path. |
| A required Copilot or cross-geography permission isn't available. | Response strings can be empty, and Classification Status is Skipped. | Log an environment or configuration issue. |
| Fewer than two active categories are configured. | The logical status can be empty, and Classification Status is Failed. | Notify an administrator to correct the category configuration. |
| The classification service returns an HTTP error. | status contains the service HTTP code and message. |
Use controlled retry and escalation policies. |
| No category is returned. | status.code is 500, and the message is No category returned. |
Log the failure and use a safe fallback. |
| No service response is received. | status.code is 500, and the message is No response from API. |
Log the failure and use a safe fallback. |
| The email ID is invalid or the calling user lacks a required Dataverse privilege. | The flow step can fail, or logical outputs can be empty. | Use Configure run after to capture and log the failure. |