AI audit logs.

When an AI system does something wrong, the first question is what exactly happened. An audit trail answers it in minutes: who asked, what the model saw, which tools ran, who approved, and which version of everything was live.

Engineering guide7 min readUpdated 19 October 2026

Short answer

An AI audit trail is a tamper-evident, append-only record of every run: who asked, which model and prompt version answered, what was retrieved, which tools ran with what arguments, who approved what, and what was returned. Structure it as linked trace events, follow OpenTelemetry’s GenAI conventions for field names, keep sensitive content in a separate controlled store, and test the trail by reconstructing a past run end to end.

  • Log runs as linked events: model calls, retrievals, tool calls, approvals and outputs.
  • Record versions of prompts, models, tools and indexes, or you cannot reproduce anything.
  • Keep message content in a separate store with its own access and retention rules.
  • Never sample audit events. Sample debugging traces if you must.
  • Chain event hashes so tampering or gaps are detectable.

What should an AI audit trail let you answer?

An AI audit trail should let someone who was not there reconstruct any run, weeks later, and answer five questions: what the system did, what it saw when it decided, who authorised it, what changed in other systems as a result, and which versions of prompts, models, tools and data were live at the time. If any answer needs an engineer’s memory, the trail is incomplete.

That makes an audit log different from ordinary application logging. Debug logs help engineers fix code and can be sampled, truncated or rotated quickly. Audit events are complete, protected against tampering, linked across systems and kept on a retention schedule the organisation chooses. The NIST AI RMF core asks for both sides: transparency and accountability risks examined and documented (Measure 2.8), and post-deployment monitoring that covers incident response, recovery and change management (Manage 4.1).

How should the trace be structured?

Structure each run as a trace of linked events, one per meaningful step, all carrying the same run identifier and a sequence number. The event types below cover most systems, including agents.

Event Key fields Content handling
run.start run ID, tenant, pseudonymous user ID, channel, app version, prompt version Request text stored by reference
model.call operation, provider, requested and returned model, token counts, finish reason, latency Input and output stored by reference, with hashes
retrieval index version, document IDs and versions, scores, filters applied Passage text by reference
tool.call tool name and version, arguments, idempotency key, status, external record ID Secrets redacted before logging
approval approver, decision, time, hash of exactly what was shown Rendered action stored by reference
output recipient, delivery channel, external message ID Delivered content stored by reference
run.end status, total cost, step count, escalation reason None

Use the OpenTelemetry GenAI semantic conventions for field names where they fit. As listed on 19 October 2026 they define gen_ai.operation.name values including chat, embeddings, retrieval, execute_tool and invoke_agent, plus attributes such as gen_ai.request.model and gen_ai.usage.input_tokens. They are marked as in development, and content attributes such as gen_ai.input.messages are opt-in because they may contain personal information. If your agents call MCP servers, the 2026-07-28 MCP revision documents how to pass trace context in request metadata, so server-side tool events can join the same trace.

A single tool event might look like this:

{
  "event": "tool.call",
  "run_id": "run_7f3c2a",
  "seq": 6,
  "ts": "2026-10-14T21:42:07.118Z",
  "tenant": "acme-accounting",
  "actor": {"type": "agent", "id": "gst-assistant", "prompt_version": "v14", "model": "claude-sonnet-5-5"},
  "on_behalf_of": "user_5521",
  "gen_ai.operation.name": "execute_tool",
  "tool": {"name": "send_client_email", "version": "3.2.0"},
  "args": {"client_id": "C-1093", "template": "gst_draft_ready", "attachment_doc_id": "doc_88a1"},
  "idempotency_key": "run_7f3c2a:6",
  "approval_ref": "appr_219",
  "result": {"status": "sent", "external_id": "msg_4b9e0d"},
  "content_ref": "cs://runs/run_7f3c2a/6",
  "prev_hash": "9c1e...e4",
  "hash": "51ab...07"
}

What should you record, hash or leave out?

Record identifiers and versions in full, keep content in a separate store, and never log secrets. The split keeps the audit log small, searchable and safe to share with investigators, while the content store holds the sensitive material under tighter control.

Data Audit log Content store Never logged
Prompt template, model and tool versions Full Not needed
User messages and model outputs Reference and hash Full, access-controlled
Retrieved passages Document IDs, versions, scores Text if documents may change later
Tool arguments Business identifiers in full Free-text arguments API keys, tokens, card numbers
What an approver saw Hash and reference The rendered action
Provider reasoning output Usually omitted Only if your provider returns it and you need it

Storing a hash of what an approver was shown matters more than it looks. When a reviewer later says the screen did not show the recipient, you can prove what it did show.

How do you make the log tamper-evident?

Make the log append-only and chain each event to the previous one with a cryptographic hash, so any edit, deletion or insertion breaks verification from that point on.

import hashlib
import json

GENESIS = "0" * 64

def _digest(prev_hash: str, event: dict) -> str:
    body = json.dumps(event, sort_keys=True, separators=(",", ":"))
    return hashlib.sha256((prev_hash + body).encode()).hexdigest()

def append_event(log: list, event: dict) -> None:
    prev = log[-1]["hash"] if log else GENESIS
    log.append({**event, "prev_hash": prev, "hash": _digest(prev, event)})

def verify(log: list) -> int:
    """Return the index of the first bad event, or -1 if the chain is intact."""
    prev = GENESIS
    for i, entry in enumerate(log):
        event = {k: v for k, v in entry.items() if k not in ("prev_hash", "hash")}
        if entry["prev_hash"] != prev or entry["hash"] != _digest(prev, event):
            return i
        prev = entry["hash"]
    return -1

Hash chaining detects tampering; it does not prevent it. Pair it with write-once storage in a separate account that application credentials cannot delete from, and record the latest chain hash somewhere independent each day.

What do New Zealand privacy rules mean for AI logs?

AI logs that contain personal information are subject to the Privacy Act 2020 like any other record. Three information privacy principles in the Act shape the design:

  • IPP 5, storage and security. Personal information must be protected by reasonable safeguards against loss and unauthorised access, use, modification or disclosure. Logs copied into every observability tool fail this quickly.
  • IPP 6, access. People can ask for personal information you hold about them, which includes what your AI system recorded. Index the content store by person so requests are answerable.
  • IPP 9, retention. Personal information must not be kept longer than required for the purposes it may lawfully be used for. A common pattern is longer retention for event metadata and hashes, shorter for message content.

Where the logs are stored is a separate decision from where the model runs; see data residency for AI systems.

How does an incident reconstruction work in practice?

Illustrative example: an accounting practice’s assistant emails a client a draft GST return that belongs to a different client. The audit trail turns a vague complaint into a sequence of facts.

  1. Find the run. The client forwards the email; its message ID matches external_id on a tool.call event, which gives the run ID.
  2. Replay the sequence. Ordering events by seq shows the request, two retrievals, a model call, an approval and the send.
  3. Inspect the retrieval. The second retrieval returned documents for two clients with near-identical trading names, and the model attached the wrong one.
  4. Inspect the approval. The approval hash matches a rendered summary that showed the recipient but not the attachment’s client name. The reviewer approved what they were shown.
  5. Check versions. Prompt version v14, deployed four days earlier, removed an instruction to confirm the client ID against the attachment.
  6. Assess and act. Personal information was disclosed, so the practice assesses serious harm and, if it applies, notifies the Privacy Commissioner. Fixes: a code check that attachment and recipient client IDs match, and an approval screen that shows both.

Without the trail, steps 3 to 5 are guesswork. Our AI incident response guide covers the wider process, and the AI incident log template is a place to record the outcome.

Where do AI audit trails fail?

They usually fail by omission, discovered during the first serious incident.

  • Missing versions. Without prompt, model, tool and index versions, you cannot say what code made the decision.
  • Sampled audit events. Sampling suits performance traces, not audit. Every run must be complete.
  • Approvals without context. Logging “approved” without what was shown makes the approval worthless as evidence.
  • Content everywhere. Full prompts in third-party tracing tools spread personal information beyond your controls.
  • Lost on failure. Events buffered in memory vanish when a worker crashes. Write before acting on irreversible steps.
  • Unlinked systems. Tool events that do not record the external record ID cannot be matched to what happened in the CRM or ledger.

Audit design is part of our AI governance advisory, and running the trail day to day, with monitoring of AI quality on top, is part of managed AI operations. Approval records connect directly to the controls in agent permissions and approvals.

  • Every run has a run ID, sequence numbers and linked events for each step
  • Prompt, model, tool and index versions are on every relevant event
  • Tool events record arguments, idempotency keys and external record IDs
  • Approval events store a hash of exactly what the approver saw
  • Message content lives in a separate store with its own access and retention
  • Secrets and payment data are redacted before any event is written
  • Audit events are never sampled
  • Events are hash-chained, stored write-once and verified on a schedule
  • Content can be found by person to answer IPP 6 requests
  • A past run has been reconstructed end to end as a drill

Sources

  1. OpenTelemetry GenAI semantic conventions, OpenTelemetry (GitHub), accessed 19 October 2026
  2. Model Context Protocol: Key Changes in 2026-07-28, Model Context Protocol, 28 July 2026
  3. Privacy Act 2020, New Zealand Legislation, 2020
  4. AI RMF Core, National Institute of Standards and Technology, 2023
  5. NotifyUs of a serious privacy breach, Office of the Privacy Commissioner, accessed 19 October 2026

Written by the Levy Street team in Wellington. Last updated 19 October 2026.

Questions people ask

What is the difference between an AI audit log and AI observability?

Observability data helps engineers debug and tune: latency, token counts, sampled traces, errors. An audit log is a complete record kept to answer questions about specific decisions later, often from people outside engineering. They can share a pipeline and field names, but audit events are never sampled, are protected against tampering, and have retention and access rules set by the organisation rather than by a tooling default.

Should AI audit logs store full prompts and responses?

Store them, but not in the main log. Put message content, retrieved text and outputs in a separate access-controlled store with its own retention period, and keep references and hashes in the audit log. That lets you prove what was said, honour access and deletion requests, and avoid copying personal information into every monitoring tool your engineers use.

How long should we keep AI audit logs?

As long as you need them for the purposes you collected them for, and no longer. In New Zealand, information privacy principle 9 of the Privacy Act 2020 says personal information must not be kept longer than required for its lawful purposes. Many teams keep event metadata and hashes longer than message content. Set periods with your legal or privacy adviser for your sector.

Can customers ask to see what an AI system recorded about them?

Yes. If your logs contain personal information about an identifiable person, that person can request access under information privacy principle 6 of the Privacy Act 2020. Design the content store so you can find everything linked to a person’s identifier, review it and release it, rather than searching raw log files when a request arrives.

Do we need OpenTelemetry for AI audit logs?

No, but its GenAI semantic conventions are a sensible default for field names, because many tracing tools already understand them and they cover model calls, tool execution, agent invocation and retrieval. The conventions are still marked as in development, so pin the version you follow and map it to your own audit schema rather than depending on every attribute staying the same.

Building something that has to hold up?

We design, evaluate and run AI systems in production. Tell us what you are building and where it hurts.