NewPraxa is onboarding design partners for 2026.Read more

All posts

Designing an audit trail an auditor will accept

A log is written for the engineer who will debug the system next week. An audit record is written for a stranger who arrives two years later, does not trust you, and needs to reconstruct one action well enough to defend it or overturn it.

The Praxa Team

Every team building autonomous software eventually gets the same request, and never in the form they prepared for. Nobody asks whether the system works. They ask about one action, on one account, on one Tuesday in March: what happened, and who allowed it.

That is a reconstruction problem, not a monitoring problem. Monitoring says the service was healthy at 14:02. Reconstruction says that at 14:02 a specific actor, holding a specific grant of authority, read a specific version of a specific document, produced a specific output, wrote it to a named destination, and that the write can be undone. Most systems can answer two of those five. An autonomous system needs all five, because there is no person in the chair to interview afterwards.

What the auditor is actually asking

Internal compliance, a payer's investigations unit, a privacy officer handling a complaint — all of them ask the same unforgiving question: show me this one action, and prove that what you are showing me is what happened.

The word doing the work there is prove. A dashboard screenshot is not proof, and neither is narrative written by the vendor. What satisfies the question is a record a sceptical outsider can walk end to end without your help, pinned to artifacts they can verify themselves.

Anatomy of a replayable action
Fig. 01

Five anchors, one record. An action that cannot fill all five is an action the operator should not be permitted to take.

Five fields that are not optional

01 / What actedNot “the system”

Not a service account shared by eleven workflows, either. The record needs the individual operator's identity, the version of the configuration it was running, and the environment it ran in. “Praxa agent” is not an answer; “referral-intake operator, config 2026.08.3, prod-us-east-1” is. If you change the behaviour of the thing that acted and the record does not change with it, your trail describes a system that no longer exists.

02 / On whose authorityEvery autonomous action is a delegated action

Somewhere a named human approved a scope: this operator may work these queues, at this facility, until this date. The record has to name that delegation — who granted it, what it covers, and whether it was still valid at execution time. Authority is the field most logs skip entirely, and the first thing an auditor asks about, because it is the difference between an automated action and an unauthorised one.

03 / On what inputA pointer is not enough if the target can change

“Read the referral document” means nothing if that document can be replaced, re-scanned, or re-filed. The record needs a content hash of exactly the bytes the operator read, plus where it came from and when. Pull the document two years later: if the hash matches, that part of the conversation ends. If it does not, you have learned something more important than you wanted to.

04 / Producing what outputThe decision and the artifact, not a summary of both

The value written, the field it landed in, the prior value it replaced, and the receipt the accepting system returned. “Updated coverage” is worthless. coverage_status: pending → verified, written to a named destination with a transaction id, is evidence. Store the confidence score and the threshold in force beside it: “0.71 confident against a bar of 0.65” explains a decision that “the operator decided” never will.

05 / Reversible howThe field everyone forgets

For each action: reversible, by what mechanism, within what window? Some writes are trivially undone. Some — a submission already sent outside the organisation, a record already released — are not, and an honest record says so rather than leaving it to be discovered. This is the field that turns a trail into an operational document, and the only way to answer the question that follows every finding: and what did you do about it?

An audit trail that cannot be replayed is a diary. The test is not whether you wrote something down. It is whether a stranger can reconstruct the action from your record alone.

Design principle · Praxa operator platform

Why ordinary logging fails the test

Most teams believe they already have this, because they have logs. Application logging and audit recording look alike and are built for opposite purposes — and the difference only surfaces under pressure.

  • Logs are sampled. Observability pipelines drop what is expensive to keep, and a record that exists 99.9% of the time is missing for exactly the action under investigation.
  • Logs are mutable and retention-bound. If an engineer can edit it and it ages out in thirty days, it is not evidence. Audit records need append-only storage and a retention period set by the regulation, not the storage budget.
  • Logs describe the system, not the decision. POST /claims 200 proves the call succeeded. It says nothing about why the operator believed that value was right.
  • Logs use shared identities. When everything runs as one service account, the trail reports that every action was taken by the same actor — which reports nothing.
  • Logs have no schema. Freeform strings cannot be queried across two years, and a reconstruction that takes an afternoon to assemble by hand is one nobody performs until they are forced to, under deadline.

None of this argues against logs. It argues for a second, narrower stream with different guarantees: written synchronously with the action, never sampled, never mutated, schema-enforced, retained on the regulator's clock.

A record shape that survives contact

Here is one action with all five anchors present. No protected health information sits in the record itself: the input is referenced by hash, and the values written are status fields, not clinical content.

Audit record · one actionJSON
{
  "action_id": "act_01K7Q3F8N2",
  "occurred_at": "2026-03-17T14:02:11.418Z",

  "actor": {
    "operator": "referral-intake",
    "config_version": "2026.08.3",
    "deployment": "prod-us-east-1"
  },

  "authority": {
    "grant_id": "grn_4412",
    "granted_by": "rcm-operations-lead",
    "scope": "referral.write:facility-07",
    "valid_until": "2026-12-31T00:00:00Z"
  },

  "input": {
    "source": "fax-gateway",
    "artifact": "referral_intake.pdf",
    "sha256": "9f2a1c4e…c17d",
    "read_at": "2026-03-17T14:01:58.002Z"
  },

  "output": {
    "target": "scheduling-queue",
    "field": "coverage_status",
    "prior": "pending",
    "value": "verified",
    "receipt": "txn-8841-b",
    "confidence": 0.71,
    "threshold": 0.65
  },

  "reversal": {
    "reversible": true,
    "mechanism": "restore-prior-value",
    "window_hours": 72
  }
}
Illustrative record shape. Field names are what matters here, not the values — every value shown is fabricated for the example.

It is longer than a log line, and that is the point. The cost is a few hundred bytes per action and a schema to maintain. The return is that any single action can be explained without a meeting.

Build the trail first

The trail is not instrumentation you add once the system works. It is part of the system's contract with the people who let it run, and the constraint that follows is useful: if an operator cannot describe an action in these five fields, it should not be permitted to take the action.

That rule is irritating during development. It is also the entire reason anyone would let the thing run unattended.

Back to all posts

Related reading

[ 01 ]Get started

See the audit trail on one of your workflows.

Bring your most painful queue. We will show you the operator executing it end to end, and the record it leaves behind for every action.

Request a demo