What the append-only audit log records, the full list of canonical action names, how gate overrides are snapshotted, and the reporting that does not exist yet.
4 min read·Last updated September 3, 2026
Every consequential action writes a row to AuditLog. The table is append-only: nothing in the product updates or deletes a row once written. It is the answer to “who verified this, and when” months after everyone has forgotten.
The tenant. Platform-level actions can override it.
actorUserId, actorType
Who did it and on which surface. Null for system actions.
action
A canonical dotted name from the list below.
entityType, entityId
What it was done to — Application, Document, Applicant, and so on.
before, after
JSON snapshots. Dates are serialised as ISO strings and decimals as strings, so a stored row never depends on a live object.
reason
Required for overrides and rejections. This is the field an inspection reads.
ip, userAgent
Request provenance.
createdAt
When.
An audit failure never breaks the user’s action.Audit writes are deliberately non-fatal — if the write fails it is logged loudly to the server console and the operation still completes. Losing a log row is bad; refusing to verify a passport because logging hiccuped is worse.
Stage moves are recorded twice, deliberately. The audit row gives you the flat “who, what, when” alongside everything else. ApplicationStageHistory gives you the structured detail:
fromStageId and toStageId — the exact transition.
byUserId — who moved it.
wasOverride — whether a blocking requirement was pushed past.
overrideReason — the written justification, minimum ten characters.
unmetSnapshot — the full set of unmet blocking requirements as they were at that moment, each with its label, type, refCode and detail string.
note — an optional free-text note on any move.
The snapshot is the important part.Fixing the document afterwards does not rewrite the record. Months later you can still answer precisely what was missing when someone decided to proceed, which is the question an inspection actually asks.
The dashboard surfaces the audit log in two places: a recent-activity feed of the last 18 rows with the action, entity and actor name, and a staff activity leaderboard derived from counting each officer’s audit rows.
There is no audit viewer and no audit export.The log is complete and queryable in the database, but the product gives you no way to browse, filter or export it — no date range, no action filter, no per-application history view, no CSV. The audit module is defined as read-only even for an Owner, and no endpoint serves it. If you need an inspection pack today, it comes out of the database directly. This is the largest gap between what the data model supports and what the UI exposes.
Until that lands, the two things worth doing are: keep override reasons genuinely descriptive, since they are the rows an inspector will read; and treat a rising application.stage_overridden count as a signal that your stage configuration needs fixing rather than as normal practice.