Documentation
Getting Started
Data Grid
Modeling
Business Rules
Approvals
Users, Roles & Security
Integration & API
Installation
Migrating from MDS
Architecture

Audit Log

The audit log is the full trail of data changes and security events. It is append-only: entries are never edited, only deleted by the retention cleanup.

Access: Settings → Log ViewerAudit Log tab

The audit log with filters and expandable entries
The audit log with filters and expandable entries(click to enlarge)

Filtering

  • Search across records, users, fields and values
  • All models and All entities dropdowns. Authentication and user-management events use entity Authentication and model System
  • All actions, grouped into Data, Security, Permissions and Approval
  • A from and to date pair

Click Clear filters to reset everything at once.

Action groups

GroupActions
DataEdit, Create, Delete, Import, Purge
SecurityLogin, Failed login, Account locked, Logout, User created, User updated, User deleted, Welcome email, Password changed, Password set (forced), First setup
PermissionsPermission changed
ApprovalSubmitted for approval, Approved, Force approved, Rejected, Sent back

API user and API key events use the same security badges: API user created, API user updated, API user deleted, API key regenerated.

Reading an entry

Every row shows the time, the acting user's email address, a color-coded action badge, the entity, and a one-line summary. Click a row to expand it.

  • Data entries show a before-and-after table of every changed field
  • Permission entries show the role, the scope, and the exact flags that changed
  • Security entries show details such as the email address, the IP address, or the remaining login attempts

If a user is deleted, the numeric user ID appears instead of the email address.

Export

Click Export and choose JSON, CSV or XLSX. The export covers the filtered result set and includes the full field-level change data.


Technical reference

Database table: `AuditLog`

ColumnTypeDescription
IdBIGINTPrimary key, auto-increment
EntityIdINTForeign key → Entities.Id
EntityNameNVARCHAR(200)Entity name at the time of the action
ModelNameNVARCHAR(200)Model name at the time of the action
ActionNVARCHAR(50)See the action groups above
RecordCountINTNumber of rows affected
UserIdINTForeign key → Users.Id. Resolved to an email address in the UI
CommentNVARCHAR(1000)Optional save comment
DetailsNVARCHAR(MAX)JSON with the field-level change data
CreatedAtDATETIME2Timestamp in UTC

When an entry is written

The stored procedure responsible for each operation writes its own audit entry, inside the same transaction as the change. If the save rolls back, the audit entry rolls back with it — there are never phantom entries for changes that did not happen.

EventWritten by
Rows saved (new, edited or mixed)usp_Data_SaveChanges — one entry per save batch
Rows deletedusp_Data_Delete — one entry per batch
Data imported by the migration wizardThe import procedure — one entry per entity
Permissions changed in bulkusp_Permission_BulkSaveone entry per changed permission
A single permission upsertedusp_Permission_Upsert — one entry if anything changed
Login success or failure, account lockoutThe authentication handler in the API
User created, updated or deletedThe user-management routes
Password changed or resetThe password-change route
First user setup completedThe setup route

The `Details` JSON

For data actions, Details is an array with one element per affected row.

Create — every field value of the new row:

[{"code":"5","name":"Amsterdam","_savedId":130,"Region":"Noord-Holland","Country":"{NL} Netherlands"}]

Edit — only the fields that changed:

[{"code":"5","name":"Amsterdam","_savedId":130,"Region":"Zuid-Holland"}]

Delete — a full server-side snapshot of every deleted row, always saved automatically:

[{"code":"5","name":"Amsterdam","Region":"Noord-Holland","Country":"{NL} Netherlands"}]

Three keys are internal: code and name identify the row, and _savedId is the EntityRows.Id used by the audit panel to match entries to rows. _savedId is not displayed.

Domain attributes are resolved at write time. Instead of the raw ID 131, the log stores {NL} Netherlands, so the entry stays readable after the referenced record is changed or deleted.

Only changed fields are stored. The grid sends a changedFields array per row and the backend filters the details with it, so unchanged fields never clutter the trail.

Permission change entries

usp_Permission_BulkSave writes one entry per individual change, with Action = permission_change and RecordCount = 1. Here Details is an object, not an array:

{
  "scope": "entity",
  "modelId": 3,
  "modelName": "Customer",
  "entityId": 12,
  "entityName": "Branch",
  "roleId": 2,
  "roleName": "DataSteward",
  "changes": { "canRead": { "from": false, "to": true } }
}

Attribute scope adds attributeId and attributeName, and changes carries level. Only flags that actually changed appear.

Attribute-level level values are the effective levels, not the raw stored value. Setting an attribute to Inherit removes the permission row, so the log resolves what the inherited level actually is — from the entity permission, then the model permission for the same role — and records that. The entry reads "to": "read" rather than a meaningless "to": "none".

Batch saves

One save operation produces one entry, with RecordCount equal to the number of rows and one Details element per row. Action is:

  • create — every row is new
  • edit — every row already existed
  • create/edit — the batch contains both

Stored procedures

ProcedurePurpose
usp_AuditLog_WriteInserts an entry
usp_AuditLog_GetPaginatedPaginated retrieval for this screen
usp_AuditLog_GetByRowEntries for one row, used by the audit panel
usp_AuditLog_GetDashboardThe last 200 data actions, for the dashboard
usp_AuditLog_ResolveAttributesMaps uda_* column names to display names
usp_AuditLog_CleanupDeletes entries past the retention window

Automatic cleanup

usp_Maintenance_Cleanup reads auditRetentionDays (default 90) and calls usp_AuditLog_Cleanup:

DELETE FROM AuditLog
WHERE CreatedAt < DATEADD(DAY, -@RetentionDays, GETUTCDATE())

The cutoff moves forward one day for every day that passes, so the log always covers exactly the configured window. See Data Retention.

Ready to get started?

Start managing your master data with Primentra today.

View Pricing
Audit Log | Administration | Docs | Primentra