Retrieve the history of audited data changes
After auditing is enabled and data changes are made to those tables and columns being audited, you can retrieve the data change history.
Note
Audit data is not available using the Dataverse TDS (SQL) endpoint. More information: Use SQL to query data
Audit table
Data for auditing events are in the Auditing (Audit) table. In the Web API the audit EntityType is the resource for this data. The audit table is read-only.
The audit table provides the data for the View Audit Summary displayed in the Power Platform admin center. More information: Administrators Guide: View audit logging details
The calling user must have the prvReadAuditSummary
privilege to retrieve data from this table. More information: Example: Check whether a user has a privilege
The following table summarizes important columns in the audit table.
SchemaName LogicalName DisplayName |
Type | Description |
---|---|---|
Action action Event |
Choice | Options that represent the event that caused the change. More information: Actions |
AttributeMask attributemask Changed Field |
Memo | When the change represents a data change to a record, contains a comma separated list of numbers that correspond to the AttributeMetadata.ColumnNumber for the columns changed in the transaction for the action. Note: Rather than try to use this data, use the messages to retrieve change history. More information: Retrieve audit change history |
AuditId auditid Record Id |
Unique Identifier | The primary key for the audit table. |
CallingUserId callinguserid Calling User |
Lookup | The calling user when impersonation is used for the operation. Otherwise null. |
CreatedOn createdon Changed Date |
DateTime | When the audit record was created, which is when the user operation took place. |
ObjectId objectid Record |
Lookup | The unique identifier for the record that was audited. |
ObjectTypeCode objecttypecode Entity |
EntityName | The logical name of the table referred to by the objectid column. |
Operation operation Operation |
Choice | The operation that caused the audit record. One of 4 values: 1 = Create 2 = Update 3 = Delete 4 = Access |
UserId userid Changed By |
Lookup | The Id of the user who caused the change. |
Audit Actions
When this topic was written there were 76 options in the Action Choices/Options which correspond to events in the system. You can use these to filter for specific events.
The following groups categorize these events.
Table row events
These events capture changes to a record.
Value | Label | Message | Description |
---|---|---|---|
1 | Create | Create |
A record is created. |
2 | Update | Update |
A record is updated. |
3 | Delete | Delete |
A record is deleted. |
12 | Merge | Merge |
A record is merged with another. |
13 | Assign | Assign |
The ownerid column value for a user-owned table record is changed. |
41 | Set State | SetState |
The statecode column value for a record is changed. |
Record Sharing events
These events capture changes to record access when a record is shared.
Value | Label | Message | Description |
---|---|---|---|
14 | Share | GrantAccess |
A user is granted privileges to a record. |
48 | Modify Share | ModifyAccess |
The privileges granted to a user changes. |
49 | Unshare | RevokeAccess |
A user's access to a record is removed. |
Many-to-Many relationship events
These events capture changes when data changes for Many-to-Many relationships.
Value | Label | Message | Description |
---|---|---|---|
33 | Associate Entities | Associate |
One or more records are associated with another. |
34 | Disassociate Entities | Disassociate |
One or more records are disassociated from another. |
53 | Assign Role To Team | Associate |
A security role is assigned to a team. |
54 | Remove Role From Team | Disassociate |
A security role is removed from a team. |
55 | Assign Role To User | Associate |
A security role is assigned to a user. |
56 | Remove Role From User | Disassociate |
A security role is removed from a user. |
User Access Events
These options are used to capture the history of user access when user access auditing is enabled. The audit record for these events will have the operation
column value of 4.
Value | Label | Description |
---|---|---|
64 | User Access via Web | User is accessing Dataverse using a model-driven app. |
65 | User Access via Web Services | User is accessing Dataverse using web services using a client other than a model-driven app. |
112 | User Access Audit Started | User access audit began. |
113 | User Access Audit Stopped | User access audit ended. |
For an SDK for .NET sample showing the use of these action options, see Sample: Audit user access.
Metadata change events
These events capture changes to table and column definitions as well as changes to the organization table.
Value | Label | Description |
---|---|---|
100 | Delete Entity | User deleted a table. |
101 | Delete Attribute | User deleted a column. |
102 | Audit Change at Entity Level | User changed a table definition to enable or disable auditing. |
103 | Audit Change at Attribute Level | User changed a column definition to enable or disable auditing. |
104 | Audit Change at Org Level | A change was made to organization settings. |
Audit change events
These events capture changes to audit settings.
Value | Label | Description |
---|---|---|
105 | Entity Audit Started | A table was enabled for auditing. |
106 | Attribute Audit Started | A column was enabled for auditing. |
107 | Audit Enabled | Auditing was enabled for the organization. |
108 | Entity Audit Stopped | A table was disabled for auditing. |
109 | Attribute Audit Stopped | Auditing was disabled for the attribute. |
110 | Audit Disabled | A column was disabled for auditing. |
111 | Audit Log Deletion | An audit log was deleted. |
Security Role change events
Value | Label | Message | Description |
---|---|---|---|
57 | Add Privileges to Role | AddPrivilegesRole |
Privileges added to a role. |
58 | Remove Privileges From Role | RemovePrivilegeRole |
Privileges removed from a role. |
59 | Replace Privileges In Role | ReplacePrivilegesRole |
Privileges for a role are replaced. |
Other Actions
The remaining action options will generally refer to auditable operations that apply to specific solutions, such as Dynamics 365 Sales, Dynamics 365 Customer Service, and Dynamics 365 Marketing.
The labels for these actions should align with an SdkMessage.Name value that represents the action. The specific operation may be a combination of the action name and a table. For example, an option with a value of 10 and the label Close should correspond to the CloseIncident
or CloseQuote
messages.
Audit table relationships
The audit table has only two Many-to-One relationships with the systemuser
table:
Relationship | Audit Table Lookup | Description |
---|---|---|
lk_audit_userid |
userid |
Relates the user to all the audit records created because of changes they made. |
lk_audit_callinguserid |
callinguserid |
Relates the user to any of the audit records they created while impersonating another user. |
You can use these relationships to filter audit data records created for a specific user.
Note
The audit entity supports only one link entity in a query. Since only two relationships exist with the systemuser
table, this means you can include data from either the callinguserid
or userid
columns, but not both at the same time.
You cannot build queries using QueryExpression or FetchXml that join audit data with tables other than the two formal relationships that exist with the systemuser
table.
Audit EntityType definition
With the Web API, you will use the audit EntityType resource to read data from the audit
table. The following is the EntityType definition Web API $metadata service document without annotations:
<EntityType Name="audit" BaseType="mscrm.crmbaseentity">
<Key>
<PropertyRef Name="auditid" />
</Key>
<Property Name="operation" Type="Edm.Int32" />
<Property Name="attributemask" Type="Edm.String" Unicode="false" />
<Property Name="action" Type="Edm.Int32" />
<Property Name="useradditionalinfo" Type="Edm.String" Unicode="false" />
<Property Name="createdon" Type="Edm.DateTimeOffset" />
<Property Name="objecttypecode" Type="Edm.String" Unicode="false" />
<Property Name="_callinguserid_value" Type="Edm.Guid" />
<Property Name="_regardingobjectid_value" Type="Edm.Guid" />
<Property Name="_objectid_value" Type="Edm.Guid" />
<Property Name="_userid_value" Type="Edm.Guid" />
<Property Name="transactionid" Type="Edm.Guid" />
<Property Name="auditid" Type="Edm.Guid" />
<NavigationProperty Name="callinguserid" Type="mscrm.systemuser"
Nullable="false" Partner="lk_audit_callinguserid">
<ReferentialConstraint Property="_callinguserid_value"
ReferencedProperty="systemuserid" />
</NavigationProperty>
<NavigationProperty Name="userid" Type="mscrm.systemuser"
Nullable="false" Partner="lk_audit_userid">
<ReferentialConstraint Property="_userid_value"
ReferencedProperty="systemuserid" />
</NavigationProperty>
</EntityType>
More information: CSDL $metadata document
Note
The ChangeData Column is not included in the Web API audit EntityType. Rather than try to use this data, use the messages to retrieve change history. More information: Retrieve audit change history.
Example: Find Contact records deleted by a user
The following examples are queries showing audit history for contact records deleted by a specific user.
Both of the following queries return the same response.
The following one filters on the _userid_value
property of the audit record where the value matches <user id>
.
Request
GET [Organization URI]/api/data/v9.2/audits?$select=_objectid_value,objecttypecode,createdon,_userid_value&$orderby=createdon desc&$filter=operation eq 3 and objecttypecode eq 'contact' and _userid_value eq '<user id>' HTTP/1.1
Accept: application/json
OData-MaxVersion: 4.0
OData-Version: 4.0
If-None-Match: null
Prefer: odata.include-annotations="*"
The following one accesses the collection of audit records for a specific user with the lk_audit_userid
collection-valued navigation property from the systemuser
table where the systemuserid
value matches <user id>
Request
GET [Organization URI]/api/data/v9.2/systemusers(<user id>)/lk_audit_userid?$select=_objectid_value,objecttypecode,createdon,_userid_value&$orderby=createdon desc&$filter=operation eq 3 and objecttypecode eq 'contact' HTTP/1.1
Accept: application/json
OData-MaxVersion: 4.0
OData-Version: 4.0
If-None-Match: null
Prefer: odata.include-annotations="*"
Response
HTTP/1.1 200 OK
Preference-Applied: odata.include-annotations="*"
{
"@odata.context": "[Organization URI]/api/data/v9.2/$metadata#audits(_objectid_value,objecttypecode,createdon,_userid_value)",
"@Microsoft.Dynamics.CRM.totalrecordcount": -1,
"@Microsoft.Dynamics.CRM.totalrecordcountlimitexceeded": false,
"value": [
{
"_objectid_value@Microsoft.Dynamics.CRM.lookuplogicalname": "contact",
"_objectid_value": "0e76dc8a-41b5-ec11-983f-0022482bf046",
"objecttypecode@OData.Community.Display.V1.FormattedValue": "Contact",
"objecttypecode": "contact",
"createdon@OData.Community.Display.V1.FormattedValue": "5/12/2022 3:19 PM",
"createdon": "2022-05-12T22:19:12Z",
"_userid_value@Microsoft.Dynamics.CRM.lookuplogicalname": "systemuser",
"_userid_value@OData.Community.Display.V1.FormattedValue": "FirstName LastName",
"_userid_value": "4026be43-6b69-e111-8f65-78e7d1620f5e"
},
< Other results truncated for brevity>
]
}
Retrieve audit change history
There are three messages you can use to retrieve data changes that are audited.
Web API | SDK for .NET | Description |
---|---|---|
RetrieveAuditDetails Function | RetrieveAuditDetailsRequest Class | Retrieve the full audit details from an audit record. |
RetrieveAttributeChangeHistory Function | RetrieveAttributeChangeHistoryRequest Class | Retrieves the change history for an single column of an audited record. |
RetrieveRecordChangeHistory Function | RetrieveRecordChangeHistoryRequest Class | Retrieve all audited data changes for a specific record. |
To use these messages you must have the prvReadRecordAuditHistory
and prvReadAuditSummary
privileges. More information: Example: Check whether a user has a privilege
AuditDetail types
These messages provide additional details that depend on the type of action. These details are implemented using different types that are derived from a base AuditDetail
type as shown in the following table:
Web API | SDK for .NET | Description |
---|---|---|
AuditDetail ComplexType | AuditDetail Class | The base type for the derived classes. Provides access to the audit record. |
AttributeAuditDetail ComplexType | AttributeAuditDetail Class | Provides details when data changes occur for a record. Provides access to old values and new values. Returned by the following types of actions: - Table row events - Metadata change events - Audit change events |
RelationshipAuditDetail ComplexType | RelationshipAuditDetail Class | Provides details when records are associated or disassociated using Many-to-Many relationship. Provides the name of the relationship and a list of the records that the operation changed. Returned by Many-to-Many relationship events |
RolePrivilegeAuditDetail ComplexType | RolePrivilegeAuditDetail Class | Provides details when the definitions of Security Role (Role) records change. Provides information about the old and new role privileges associated to the role. Returned by Security Role change events |
ShareAuditDetail ComplexType | ShareAuditDetail Class | Provides details when a record is shared, unshared, or when the level of access to a shared record changes. Returned by Record Sharing events |
UserAccessAuditDetail ComplexType | UserAccessAuditDetail Class | Provides details to track user access auditing. Provides details on the interval and access time. Returned by User Access Events |
Important
At the time of this writing, the Web API types listed above that inherit from AuditDetail ComplexType do not return the
AuditRecord
navigation property value they should inherit fromAuditDetail
. The SDK for .NET classes return this data.Large column values included in
AttributeAuditDetail
OldValue
orNewValue
properties such as Email.Description or Annotation are limited (capped) to 5KB or ~5,000 characters in length. A capped column value can be recognized by three dots at the end of the text, for example "lorem ipsum, lorem ip…". Because the data is truncated, you cannot use the audit data to restore changes for these column values.
RetrieveAuditDetails Message
Use this message to retrieve the audit details for a single audit record.
RetrieveAuditDetails is a function bound to the audit table.
When you include the Prefer: odata.include-annotations="*"
request header you will get formatted values.
The example below shows the AttributeAuditDetail ComplexType returned when the parentaccountid
is set on an account
record.
Request
GET [Organization URI]/api/data/v9.2/audits(12869c65-d7d3-ec11-b656-281878f0eba9)/Microsoft.Dynamics.CRM.RetrieveAuditDetails HTTP/1.1
Accept: application/json
OData-MaxVersion: 4.0
OData-Version: 4.0
If-None-Match: null
Prefer: odata.include-annotations="*"
Response
HTTP/1.1 200 OK
OData-Version: 4.0
Preference-Applied: odata.include-annotations="*"
{
"@odata.context": "[Organization URI]/api/data/v9.2/$metadata#Microsoft.Dynamics.CRM.RetrieveAuditDetailsResponse",
"AuditDetail": {
"@odata.type": "#Microsoft.Dynamics.CRM.AttributeAuditDetail",
"InvalidNewValueAttributes": [],
"LocLabelLanguageCode": 0,
"DeletedAttributes": {
"Count": 0,
"Keys": [],
"Values": []
},
"OldValue": {
"@odata.type": "#Microsoft.Dynamics.CRM.account"
},
"NewValue": {
"@odata.type": "#Microsoft.Dynamics.CRM.account",
"_parentaccountid_value@OData.Community.Display.V1.FormattedValue": "A. Datum Corporation",
"_parentaccountid_value@Microsoft.Dynamics.CRM.associatednavigationproperty": "parentaccountid",
"_parentaccountid_value@Microsoft.Dynamics.CRM.lookuplogicalname": "account",
"_parentaccountid_value": "d249d106-38b5-ec11-983f-002248296cd0"
}
}
}
More information:
RetrieveAttributeChangeHistory Message
Use this message to retrieve a list of changes for a specific table column.
Use the PagingInfo
parameter to control the number of records to return and move forward or backward through the pages. For subsequent requests, set the PagingInfo.PagingCookie
property to the value returned by the AuditDetailCollection.PagingCookie
.
Changes for this message will always be AttributeAuditDetail
types.
This example returns a single audited change history for the description
column of an account
table record.
Request
GET [Organization URI]/api/data/v9.2/RetrieveAttributeChangeHistory(Target=@target,AttributeLogicalName=@attributeLogicalName,PagingInfo=@paginginfo)?
@target={ '@odata.id':'accounts(611e7713-68d7-4622-b552-85060af450bc)'}
&@attributeLogicalName='description'
&@paginginfo={
"PageNumber": 1,
"Count": 1,
"ReturnTotalRecordCount": true
}
Accept: application/json
OData-MaxVersion: 4.0
OData-Version: 4.0
If-None-Match: null
Response
HTTP/1.1 200 OK
{
"@odata.context": "[Organization URI]/api/data/v9.2/$metadata#Microsoft.Dynamics.CRM.RetrieveAttributeChangeHistoryResponse",
"AuditDetailCollection": {
"MoreRecords": true,
"PagingCookie": "<cookie page=\"1\"><cookieExtensions ContinuationToken=\"{"pageNumber":2,"continuationToken":"[{\\"compositeToken\\":{\\"token\\":null,\\"range\\":{\\"min\\":\\"3A800000000000000000000000000000\\",\\"max\\":\\"3B000000000000000000000000000000\\"}},\\"orderByItems\\":[{\\"item\\":\\"2022-05-13T22:06:46.6175613Z\\"}],\\"rid\\":\\"CVoNAJIidnNsmz0AAADwAw==\\",\\"skipCount\\":0,\\"filter\\":null}]"}\" /></cookie>",
,
"TotalRecordCount": 3,
"AuditDetails": [
{
"@odata.type": "#Microsoft.Dynamics.CRM.AttributeAuditDetail",
"InvalidNewValueAttributes": [],
"LocLabelLanguageCode": 0,
"DeletedAttributes": {
"Count": 0,
"Keys": [],
"Values": []
},
"OldValue": {
"@odata.type": "#Microsoft.Dynamics.CRM.account",
"description": "Old description value"
},
"NewValue": {
"@odata.type": "#Microsoft.Dynamics.CRM.account",
"description": "New description value"
}
}
]
}
}
More information:
RetrieveRecordChangeHistory Message
The RetrieveRecordChangeHistory
message shows the history of data changes for a given record indicated by the Target
parameter.
Use the PagingInfo
parameter to control the number of records to return and move forward or backward through the pages. For subsequent requests, set the PagingInfo.PagingCookie
property to the value returned by the AuditDetailCollection.PagingCookie
The results of this message are commonly seen as the AttributeAuditDetail
data displayed in model-driven apps when you select Related > Audit history. It shows the old values and the new values of the records, but it will also return RelationshipAuditDetail
and ShareAuditDetail
types.
This message can also be used to with the systemuser
and role
tables to return RolePrivilegeAuditDetail
and UserAccessAuditDetail
types.
The following example returns just the first two of four changes for an account record.
Request
GET [Organization URI]/api/data/v9.2/RetrieveRecordChangeHistory(Target=@target,PagingInfo=@paginginfo)?
@target={ '@odata.id':'accounts(611e7713-68d7-4622-b552-85060af450bc)'}
&@paginginfo={
"PageNumber": 1,
"Count": 2,
"ReturnTotalRecordCount": true
}
Accept: application/json
OData-MaxVersion: 4.0
OData-Version: 4.0
If-None-Match: null
Response
HTTP/1.1 200 OK
{
"@odata.context": "[Organization URI]/api/data/v9.2/$metadata#Microsoft.Dynamics.CRM.RetrieveRecordChangeHistoryResponse",
"AuditDetailCollection": {
"MoreRecords": true,
"PagingCookie": "<cookie page=\"1\"><cookieExtensions ContinuationToken=\"{"pageNumber":2,"continuationToken":"[{\\"compositeToken\\":{\\"token\\":null,\\"range\\":{\\"min\\":\\"38000000000000000000000000000000\\",\\"max\\":\\"38800000000000000000000000000000\\"}},\\"orderByItems\\":[{\\"item\\":\\"2022-05-13T22:06:27.8029732Z\\"}],\\"rid\\":\\"CVoNAJIidnPOnT0AAAAICA==\\",\\"skipCount\\":0,\\"filter\\":null}]"}\" /></cookie>",
"TotalRecordCount": 4,
"AuditDetails": [
{
"@odata.type": "#Microsoft.Dynamics.CRM.AttributeAuditDetail",
"InvalidNewValueAttributes": [],
"LocLabelLanguageCode": 0,
"DeletedAttributes": {
"Count": 0,
"Keys": [],
"Values": []
},
"OldValue": {
"@odata.type": "#Microsoft.Dynamics.CRM.account",
"description": "Old description value"
},
"NewValue": {
"@odata.type": "#Microsoft.Dynamics.CRM.account",
"description": "New description value"
}
},
{
"@odata.type": "#Microsoft.Dynamics.CRM.AttributeAuditDetail",
"InvalidNewValueAttributes": [],
"LocLabelLanguageCode": 0,
"DeletedAttributes": {
"Count": 0,
"Keys": [],
"Values": []
},
"OldValue": {
"@odata.type": "#Microsoft.Dynamics.CRM.account",
"_ownerid_value@OData.Community.Display.V1.FormattedValue": "FirstName LastName",
"_ownerid_value@Microsoft.Dynamics.CRM.associatednavigationproperty": "ownerid",
"_ownerid_value@Microsoft.Dynamics.CRM.lookuplogicalname": "systemuser",
"_ownerid_value": "4026be43-6b69-e111-8f65-78e7d1620f5e"
},
"NewValue": {
"@odata.type": "#Microsoft.Dynamics.CRM.account",
"_ownerid_value@OData.Community.Display.V1.FormattedValue": "TeamName",
"_ownerid_value@Microsoft.Dynamics.CRM.associatednavigationproperty": "ownerid",
"_ownerid_value@Microsoft.Dynamics.CRM.lookuplogicalname": "team",
"_ownerid_value": "39e0dbe4-131b-e111-ba7e-78e7d1620f5e"
}
}
]
}
}
More information:
- RetrieveRecordChangeHistory Function
- RetrieveRecordChangeHistoryResponse ComplexType
- PagingInfo ComplexType
- Use Web API functions
Note
The AuditDetail ComplexType values returned currently do not include the AuditRecord
property so there is no data about who made the change and when it was made.
See also
Auditing overview
Configure auditing
Delete audit data
Sample: Audit table data changes
Sample: Audit user access
Administrators Guide: Manage Dataverse auditing
Feedback
Submit and view feedback for