DataVerse 9.2 Event Structure¶
Overview¶
This document provides a comprehensive explanation of the JSON event structure emitted by Microsoft DataVerse 9.2 (formerly Dynamics 365 CRM). Understanding this structure is crucial for translating generic CRUD events into meaningful business events within the 7N20 platform.
Event Message Structure¶
DataVerse emits events through Azure Event Hub with a complex JSON structure. Here's a real-world example of a Create event for a custom entity (rqm_requestshortlist):
{
"BusinessUnitId": "55adc078-92be-e911-a837-000d3a2aabe6",
"CorrelationId": "fc65709c-7b89-460a-bafc-7193dd5f7c5b",
"Depth": 1,
"InitiatingUserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
"InitiatingUserAzureActiveDirectoryObjectId": "00662bfc-cb05-4266-8741-1de13f9db3f9",
"InitiatingUserId": "f88d0d38-940d-ed11-82e4-000d3a2fc6ad",
"InputParameters": [
{
"key": "Target",
"value": {
"__type": "Entity:http://schemas.microsoft.com/xrm/2011/Contracts",
"Attributes": [
{
"key": "rqm_candidateid",
"value": {
"__type": "EntityReference:http://schemas.microsoft.com/xrm/2011/Contracts",
"Id": "12ae6e67-6f3b-45ad-819e-dde77feb6d5a",
"LogicalName": "contact",
"Name": null
}
},
{
"key": "rqm_saleperhour",
"value": {
"__type": "Money:http://schemas.microsoft.com/xrm/2011/Contracts",
"Value": 8
}
},
{
"key": "statecode",
"value": {
"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
"Value": 0
}
}
],
"FormattedValues": [
{
"key": "rqm_saleperhour",
"value": "zł8,00"
},
{
"key": "statecode",
"value": "Active"
}
],
"Id": "ab89c994-065d-f011-bec2-000d3ac241ab",
"LogicalName": "rqm_requestshortlist"
}
}
],
"IsExecutingOffline": false,
"IsInTransaction": false,
"IsOfflinePlayback": false,
"IsolationMode": 1,
"MessageName": "Create",
"Mode": 1,
"OperationCreatedOn": "/Date(1752094336000+0000)/",
"OperationId": "b389c994-065d-f011-bec2-000d3ac241ab",
"OrganizationId": "2b1dd4ce-5af3-ef11-b013-6045bd9a7c61",
"OrganizationName": "unq2b1dd4ce5af3ef11b0136045bd9a7",
"OutputParameters": [
{
"key": "id",
"value": "ab89c994-065d-f011-bec2-000d3ac241ab"
}
],
"OwningExtension": {
"Id": "36d8066a-bc53-ee11-be6d-000d3a4571fd",
"LogicalName": "sdkmessageprocessingstep"
},
"ParentContext": {
"BusinessUnitId": "55adc078-92be-e911-a837-000d3a2aabe6",
"CorrelationId": "fc65709c-7b89-460a-bafc-7193dd5f7c5b",
"Depth": 1,
"InitiatingUserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
"InitiatingUserAzureActiveDirectoryObjectId": "00662bfc-cb05-4266-8741-1de13f9db3f9",
"InitiatingUserId": "f88d0d38-940d-ed11-82e4-000d3a2fc6ad",
"InputParameters": [...],
"MessageName": "Create",
"Mode": 1,
"OperationCreatedOn": "/Date(1752094336000+0000)/",
"OperationId": "b389c994-065d-f011-bec2-000d3ac241ab",
"OrganizationId": "2b1dd4ce-5af3-ef11-b013-6045bd9a7c61",
"OrganizationName": "unq2b1dd4ce5af3ef11b0136045bd9a7"
},
"PostEntityImages": [
{
"key": "PostImage",
"value": {
"Attributes": [
{
"key": "rqm_candidateid",
"value": {
"__type": "EntityReference:http://schemas.microsoft.com/xrm/2011/Contracts",
"Id": "12ae6e67-6f3b-45ad-819e-dde77feb6d5a",
"LogicalName": "contact",
"Name": "Andrzej Numerek"
}
},
{
"key": "rqm_title",
"value": "Andrzej Numerek is considered for request SR Type for client Nordea Poland"
}
],
"FormattedValues": [
{
"key": "createdon",
"value": "2025-07-09T22:52:14+02:00"
}
],
"Id": "ab89c994-065d-f011-bec2-000d3ac241ab",
"LogicalName": "rqm_requestshortlist"
}
}
],
"PreEntityImages": [],
"PrimaryEntityId": "ab89c994-065d-f011-bec2-000d3ac241ab",
"PrimaryEntityName": "rqm_requestshortlist",
"RequestId": "b41ea0f1-81da-4218-9558-c1bcd3cce484",
"SecondaryEntityName": "none",
"SharedVariables": [
{
"key": "IsAutoTransact",
"value": true
},
{
"key": "AcceptLang",
"value": "pl-PL,pl;q=0.9,en;q=0.8,en-US;q=0.7"
}
],
"Stage": 40,
"UserAzureActiveDirectoryObjectId": "00662bfc-cb05-4266-8741-1de13f9db3f9",
"UserId": "f88d0d38-940d-ed11-82e4-000d3a2fc6ad"
}
Key Event Properties¶
Core Properties¶
| Property | Type | Description | Example |
|---|---|---|---|
MessageName |
string | The CRUD operation type | "Create", "Update", "Delete", "Retrieve" |
PrimaryEntityName |
string | The logical name of the entity | "contact", "rqm_requestshortlist", "account" |
PrimaryEntityId |
GUID | Unique identifier of the entity | "ab89c994-065d-f011-bec2-000d3ac241ab" |
SecondaryEntityName |
string | Related entity name (for association events) | "none" for single entity operations |
BusinessUnitId |
GUID | Business unit where operation occurred | "55adc078-92be-e911-a837-000d3a2aabe6" |
OperationCreatedOn |
string | Timestamp of the operation | "/Date(1752094336000+0000)/" (epoch format) |
OperationId |
GUID | Unique identifier for this operation | "b389c994-065d-f011-bec2-000d3ac241ab" |
OrganizationId |
GUID | The DataVerse organization ID | "2b1dd4ce-5af3-ef11-b013-6045bd9a7c61" |
OrganizationName |
string | The organization name | "unq2b1dd4ce5af3ef11b0136045bd9a7" |
CorrelationId |
GUID | Unique ID for tracking related operations | "fc65709c-7b89-460a-bafc-7193dd5f7c5b" |
RequestId |
GUID | Unique ID for the HTTP request | "b41ea0f1-81da-4218-9558-c1bcd3cce484" |
UserId |
GUID | The user who initiated the operation | "f88d0d38-940d-ed11-82e4-000d3a2fc6ad" |
UserAzureActiveDirectoryObjectId |
GUID | Azure AD object ID of the user | "00662bfc-cb05-4266-8741-1de13f9db3f9" |
InitiatingUserId |
GUID | Same as UserId | "f88d0d38-940d-ed11-82e4-000d3a2fc6ad" |
InitiatingUserAzureActiveDirectoryObjectId |
GUID | Same as UserAzureActiveDirectoryObjectId | "00662bfc-cb05-4266-8741-1de13f9db3f9" |
InitiatingUserAgent |
string | Browser user agent string | "Mozilla/5.0 (Windows NT 10.0...)" |
Depth |
number | Plugin execution depth | 1 |
Mode |
number | Execution mode | 0=Synchronous, 1=Asynchronous |
Stage |
number | Pipeline stage | 10=PreValidation, 20=PreOperation, 40=PostOperation |
IsExecutingOffline |
boolean | Whether executing offline | false |
IsInTransaction |
boolean | Whether in a database transaction | false |
IsOfflinePlayback |
boolean | Whether offline playback | false |
IsolationMode |
number | Plugin isolation mode | 1=None, 2=Sandbox |
Execution Context (ParentContext)¶
The ParentContext object provides crucial information about the execution environment. It contains the same properties as the root level but represents the parent execution context:
{
"BusinessUnitId": "55adc078-92be-e911-a837-000d3a2aabe6",
"CorrelationId": "fc65709c-7b89-460a-bafc-7193dd5f7c5b",
"Depth": 1,
"InitiatingUserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)...",
"InitiatingUserAzureActiveDirectoryObjectId": "00662bfc-cb05-4266-8741-1de13f9db3f9",
"InitiatingUserId": "f88d0d38-940d-ed11-82e4-000d3a2fc6ad",
"MessageName": "Create",
"Mode": 1,
"OperationCreatedOn": "/Date(1752094336000+0000)/",
"OperationId": "b389c994-065d-f011-bec2-000d3ac241ab",
"OrganizationId": "2b1dd4ce-5af3-ef11-b013-6045bd9a7c61",
"OrganizationName": "unq2b1dd4ce5af3ef11b0136045bd9a7"
}
The ParentContext is particularly important when dealing with nested plugin executions or when one operation triggers another.
Pre and Post Entity Images¶
The most critical components for detecting changes are the entity images:
- PreEntityImages: Snapshot of the entity before the operation (empty array for Create operations)
- PostEntityImages: Snapshot of the entity after the operation
In the real-world structure, entity images use a more complex format with typed attributes:
{
"key": "PostImage",
"value": {
"Attributes": [
{
"key": "rqm_candidateid",
"value": {
"__type": "EntityReference:http://schemas.microsoft.com/xrm/2011/Contracts",
"Id": "12ae6e67-6f3b-45ad-819e-dde77feb6d5a",
"LogicalName": "contact",
"Name": "Andrzej Numerek"
}
},
{
"key": "rqm_saleperhour",
"value": {
"__type": "Money:http://schemas.microsoft.com/xrm/2011/Contracts",
"Value": 8.0000
}
},
{
"key": "statecode",
"value": {
"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
"Value": 0
}
}
],
"FormattedValues": [
{
"key": "rqm_saleperhour",
"value": "zł8,00"
},
{
"key": "statecode",
"value": "Active"
}
],
"Id": "ab89c994-065d-f011-bec2-000d3ac241ab",
"LogicalName": "rqm_requestshortlist"
}
}
Input Parameters¶
The InputParameters array contains the operation's input data, with the "Target" parameter being the most important:
{
"key": "Target",
"value": {
"__type": "Entity:http://schemas.microsoft.com/xrm/2011/Contracts",
"Attributes": [...],
"FormattedValues": [...],
"Id": "ab89c994-065d-f011-bec2-000d3ac241ab",
"LogicalName": "rqm_requestshortlist",
"RowVersion": "1152134471"
}
}
Translating to Business Events¶
Change Detection Pattern¶
To translate generic CRUD events into specific business events, compare pre and post images:
// Example: Detecting email change
public class ContactEmailChangedDetector
{
public bool HasEmailChanged(JObject preImage, JObject postImage)
{
var preEmail = preImage?["emailaddress1"]?.ToString();
var postEmail = postImage?["emailaddress1"]?.ToString();
return !string.Equals(preEmail, postEmail, StringComparison.OrdinalIgnoreCase);
}
}
Business Event Translation Example¶
Transform a generic Update event into a specific ContactEmailChanged business event:
// Input: Generic DataVerse Update Event
{
"MessageName": "Update",
"PrimaryEntityName": "contact",
"PreEntityImages": [{
"value": {
"emailaddress1": "old@email.com"
}
}],
"PostEntityImages": [{
"value": {
"emailaddress1": "new@email.com"
}
}]
}
// Output: Specific Business Event
{
"EventType": "ContactEmailChanged",
"ContactId": "12345678-1234-1234-1234-123456789012",
"OldEmail": "old@email.com",
"NewEmail": "new@email.com",
"ChangedBy": "87654321-4321-4321-4321-210987654321",
"ChangedAt": "2024-01-15T10:35:00.123Z",
"CorrelationId": "22222222-2222-2222-2222-222222222222"
}
Common Entity Attributes¶
Contact Entity¶
{
"contactid": "GUID",
"firstname": "string",
"lastname": "string",
"emailaddress1": "string",
"emailaddress2": "string",
"telephone1": "string",
"mobilephone": "string",
"jobtitle": "string",
"parentcustomerid": "GUID",
"ownerid": "GUID",
"statecode": 0,
"statuscode": 1
}
Account Entity¶
{
"accountid": "GUID",
"name": "string",
"emailaddress1": "string",
"telephone1": "string",
"websiteurl": "string",
"primarycontactid": "GUID",
"ownerid": "GUID",
"statecode": 0,
"statuscode": 1
}
Event Processing Considerations¶
1. Field Selection¶
Not all fields are included in pre/post images. Configure DataVerse plugins to include specific fields:
// Plugin registration step configuration
preImage.Attributes = new string[] {
"firstname",
"lastname",
"emailaddress1",
"telephone1"
};
2. Null vs Missing Values¶
- Null value: Field explicitly set to null
- Missing field: Field not included in the image (no change or not configured)
3. Data Types¶
DataVerse uses specific typed formats with __type annotations:
EntityReference (Lookup)¶
{
"__type": "EntityReference:http://schemas.microsoft.com/xrm/2011/Contracts",
"Id": "12ae6e67-6f3b-45ad-819e-dde77feb6d5a",
"LogicalName": "contact",
"Name": "Andrzej Numerek",
"KeyAttributes": [],
"RowVersion": null
}
Money¶
{
"__type": "Money:http://schemas.microsoft.com/xrm/2011/Contracts",
"Value": 8.0000
}
OptionSetValue¶
{
"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
"Value": 112020000
}
DateTime¶
DataVerse uses epoch format with timezone offset:
"/Date(1752094334000+0000)/"
4. Additional Properties¶
SharedVariables¶
Contains execution context information:
[
{"key": "IsAutoTransact", "value": true},
{"key": "AcceptLang", "value": "pl-PL,pl;q=0.9,en;q=0.8,en-US;q=0.7"},
{"key": "x-ms-app-name", "value": "CRMHub"},
{"key": "ChangedEntityTypes", "value": [
{"key": "rqm_request", "value": "Update"},
{"key": "fileattachment", "value": "Create"}
]}
]
OwningExtension¶
Identifies the plugin step that generated the event:
{
"Id": "36d8066a-bc53-ee11-be6d-000d3a4571fd",
"LogicalName": "sdkmessageprocessingstep",
"KeyAttributes": [],
"Name": null,
"RowVersion": null
}
Service Endpoint Configuration for Change Detection¶
To effectively capture and process DataVerse events for change detection, you must configure service endpoints and plugin registration steps correctly. This configuration determines what events are sent to your external systems and what data is included.
1. Service Endpoint Configuration¶
Create a service endpoint in DataVerse that points to your Azure Event Hub:
Connection String Format¶
Endpoint=sb://your-eventhub-namespace.servicebus.windows.net/;SharedAccessKeyName=your-policy-name;SharedAccessKey=your-key;EntityPath=your-eventhub-name
Service Endpoint Properties¶
| Property | Value | Description |
|---|---|---|
| Name | DataVerseEventEndpoint | Descriptive name for the endpoint |
| Namespace Address | sb://your-namespace.servicebus.windows.net/ |
Service Bus namespace URL |
| Contract | Queue | Use Queue contract for Event Hub |
| Path | your-eventhub-name | Event Hub name |
| Message Format | JSON | Use JSON format for structured data |
| User Information Sent | UserId | Include user context in events |
| Description | Sends DataVerse events to Event Hub | Optional description |
2. Plugin Registration Configuration¶
For effective change detection, register plugin steps on the PostOperation stage with appropriate entity images.
Required Plugin Step Configuration¶
<!-- Example Plugin Registration Tool XML -->
<Solution>
<PluginTypes>
<PluginType Name="Microsoft.Crm.Extensibility.InternalOperationPlugin"
FriendlyName="InternalOperationPlugin"
TypeName="Microsoft.Crm.Extensibility.InternalOperationPlugin"
WorkflowActivityGroupName=""
AssemblyName="Microsoft.Crm.Extensibility">
<Steps>
<!-- Contact Entity Events -->
<Step Name="ContactCreate"
MessageName="Create"
PrimaryEntityName="contact"
Stage="PostOperation"
Mode="Asynchronous"
Rank="1"
AsyncAutoDelete="false"
CustomConfiguration="MyServiceEndpointId">
<Images>
<Image Name="PostImage"
Type="PostImage"
Attributes="contactid,firstname,lastname,emailaddress1,telephone1,mobilephone,jobtitle,parentcustomerid,statecode,statuscode,modifiedon,modifiedby" />
</Images>
</Step>
<Step Name="ContactUpdate"
MessageName="Update"
PrimaryEntityName="contact"
Stage="PostOperation"
Mode="Asynchronous"
Rank="1"
AsyncAutoDelete="false"
CustomConfiguration="MyServiceEndpointId">
<Images>
<Image Name="PreImage"
Type="PreImage"
Attributes="contactid,firstname,lastname,emailaddress1,telephone1,mobilephone,jobtitle,parentcustomerid,statecode,statuscode" />
<Image Name="PostImage"
Type="PostImage"
Attributes="contactid,firstname,lastname,emailaddress1,telephone1,mobilephone,jobtitle,parentcustomerid,statecode,statuscode,modifiedon,modifiedby" />
</Images>
</Step>
<Step Name="ContactDelete"
MessageName="Delete"
PrimaryEntityName="contact"
Stage="PreOperation"
Mode="Asynchronous"
Rank="1"
AsyncAutoDelete="false"
CustomConfiguration="MyServiceEndpointId">
<Images>
<Image Name="PreImage"
Type="PreImage"
Attributes="contactid,firstname,lastname,emailaddress1,telephone1,mobilephone,jobtitle,parentcustomerid,statecode,statuscode" />
</Images>
</Step>
</Steps>
</PluginType>
</PluginTypes>
</Solution>
3. Critical Configuration Parameters¶
Stage Selection¶
| Stage | When to Use | Change Detection Capability |
|---|---|---|
| PreValidation (10) | Before validation | Limited - entity may change |
| PreOperation (20) | Before database operation | Good for deletes |
| PostOperation (40) | After database operation | Best for change detection |
Mode Selection¶
| Mode | Description | Recommended For |
|---|---|---|
| Synchronous (0) | Blocks the operation | Critical validations only |
| Asynchronous (1) | Non-blocking | Event publishing |
Entity Image Configuration¶
For Create Operations:
{
"PostImage": {
"Attributes": ["field1", "field2", "field3"],
"AllAttributes": false
}
}
For Update Operations (Required for Change Detection):
{
"PreImage": {
"Attributes": ["field1", "field2", "field3"],
"AllAttributes": false
},
"PostImage": {
"Attributes": ["field1", "field2", "field3", "modifiedon", "modifiedby"],
"AllAttributes": false
}
}
For Delete Operations:
{
"PreImage": {
"Attributes": ["field1", "field2", "field3"],
"AllAttributes": false
}
}
4. Best Practices for Change Detection¶
Field Selection Strategy¶
Only include fields you need to monitor for changes to reduce payload size and processing overhead:
// Example: Contact entity fields for business events
var contactFields = new[]
{
"contactid", // Required: Primary key
"firstname", // Business field
"lastname", // Business field
"emailaddress1", // Critical for email change events
"telephone1", // Critical for phone change events
"mobilephone", // Critical for mobile change events
"jobtitle", // Business field
"parentcustomerid", // Relationship field
"statecode", // Status tracking
"statuscode", // Status reason tracking
"modifiedon", // Audit field (PostImage only)
"modifiedby" // Audit field (PostImage only)
};
Entity-Specific Configurations¶
For Custom Entities (like rqm_requestshortlist):
{
"PreImage": {
"Attributes": [
"rqm_requestshortlistid",
"rqm_candidateid",
"rqm_requestid",
"rqm_salesstatus",
"rqm_saleperhour",
"rqm_costperhour",
"statecode",
"statuscode"
]
},
"PostImage": {
"Attributes": [
"rqm_requestshortlistid",
"rqm_candidateid",
"rqm_requestid",
"rqm_salesstatus",
"rqm_saleperhour",
"rqm_costperhour",
"rqm_title",
"statecode",
"statuscode",
"modifiedon",
"modifiedby"
]
}
}
5. Filtering Configuration¶
Use filtering conditions to reduce unnecessary event volume:
Message Filtering¶
<FilteringAttributes>emailaddress1,telephone1,statecode</FilteringAttributes>
Custom Configuration¶
Pass configuration data to identify the service endpoint:
<CustomConfiguration>MyServiceEndpointId</CustomConfiguration>
6. Monitoring and Troubleshooting¶
Plugin Trace Log¶
Enable plugin trace logs to debug event flow: - Navigate to Settings > System > System Jobs - Filter by Plugin Type = Microsoft.Crm.Extensibility.InternalOperationPlugin - Check execution details and any errors
Service Endpoint Monitoring¶
Monitor Event Hub metrics: - Incoming Messages: Verify events are being sent - Outgoing Messages: Verify events are being consumed - Throttled Requests: Check for rate limiting - Server Errors: Identify connectivity issues
7. PowerShell Configuration Script¶
# Example PowerShell script for service endpoint registration
$connectionString = "Endpoint=sb://your-namespace.servicebus.windows.net/;SharedAccessKeyName=your-policy;SharedAccessKey=your-key;EntityPath=your-hub"
# Create service endpoint
$serviceEndpoint = New-CrmRecord -EntityLogicalName "serviceendpoint" -Fields @{
"name" = "DataVerseEventEndpoint"
"namespaceaddress" = "sb://your-namespace.servicebus.windows.net/"
"contract" = "Queue"
"path" = "your-hub-name"
"messageformat" = "JSON"
"userintentionsent" = "UserId"
"connectionstring" = $connectionString
}
Write-Host "Service Endpoint Created: $($serviceEndpoint.Id)"
Microsoft Documentation References¶
For detailed information about DataVerse event structure and processing:
- DataVerse Event Framework - Core concepts and architecture
- Plugin Development - Creating and registering plugins
- Event Execution Pipeline - Understanding event flow
- Entity Images - Working with pre/post images
- Azure Service Bus Integration - Sending events to Azure
- Service Endpoints - Service endpoint registration
- Plugin Registration Tool - Tool for registering plugins and service endpoints
Implementation in 7N20¶
The 7N20 platform processes DataVerse events through a containerized .NET application that:
- Consumes events from Azure Event Hub
- Deserializes the JSON structure
- Compares pre/post images to detect changes
- Generates specific business events
- Publishes to business event topics
For implementation details, see: - DataVerse Event Transformation Guide - CRM Domain Business Events
Summary¶
Understanding the DataVerse 9.2 event structure enables: - Accurate change detection through pre/post image comparison - Translation of generic CRUD operations into domain-specific business events - Proper handling of DataVerse-specific data types and formats - Integration with the 7N20 event-driven architecture
The key to effective event translation is leveraging the pre and post entity images to identify specific field changes and emit targeted business events that downstream services can act upon.