The short answer
In FHIR R4, AuditEvent is “a record of an event made for purposes of maintaining a security log”.1 Every system involved in an event should write one: the client, the server, a proxy in between. Provenance tracks the activity that created, revised, deleted or signed a version of a resource, and it is prepared by the application that initiates the change.2
So the question decides the resource. “Who touched patient 123’s record last Tuesday, from which system, and did it succeed?” is an AuditEvent question. “Where did version 4 of this Observation come from, who was it done for, and under which policy?” is a Provenance question. When an AI agent writes clinical data, you usually want both: the audit trail for security review, and provenance attached to the data itself.
Deedglass
A sealed record you can check offline
Record 1043
- What ran
- Medication order, Bed 12
- Approved by
- Dr. N. Shah
- Check
- Matches
- Time
- 09:12
- The request
- Not keptA fingerprint only: 77d2…c41a
Each record is linked to the one before
- Record 1041
- Record 1042
- Record 1043
Side by side
Both resources are at maturity level 3, status Trial Use, in R4.12
| AuditEvent | Provenance | |
|---|---|---|
| Purpose | Security log of an event1 | History of a resource version2 |
| Who writes it | Every actor involved in the event1 | The application initiating the change2 |
| Points at | Entities accessed (entity, optional) |
The version written (target, usually version-specific)2 |
| Covers reads | Yes | No; it’s about versions created, revised, deleted or signed |
| Records failures | Yes, outcome1 |
No; a failed write has no version |
| “On behalf of” | Through agents and the requestor flag1 |
agent.onBehalfOf2 |
| Policy | Not a core element | policy2 |
| Signature | Not a core element | signature on the target2 |
| Edited later? | Servers generally don’t accept updates or deletes1 | Can be persisted with the target |
The spec itself draws the line: a Provenance resource carries overlapping information, but it is a record-keeping assertion.1 AuditEvent is what was observed; Provenance is what the author says about its own work.
AuditEvent: the security log
AuditEvent’s required elements tell you what it is for: a type for the family of event, a recorded time, at least one agent, and a source, the system reporting the event.1 Each agent carries a required requestor flag that says whether it initiated the event.1
Two properties make it a log rather than a document:
- Several records for one event. The spec says all actors involved in an auditable event should record an AuditEvent, so one write often produces a record from the client and one from the server.1 Reconciling them is part of review.
- Append-only by convention. Servers that support AuditEvent would not generally accept updates or deletes on it, because that would undermine the audit record.1 Check that your server actually enforces this.
AuditEvent covers reads and searches as well as writes. That matters for privacy review: an agent that reads hundreds of charts without writing anything shows up only here.
Provenance: the history of a version
Provenance points at the version it describes through target, usually version-specific, and records occurred, recorded, the activity, the policy it followed, the agents with their type, role, who and onBehalfOf, the entity inputs, and an optional signature.2
For AI-generated data, agent and entity do most of the work. An agent row for the AI system, with onBehalfOf naming the clinician who asked, answers “who did this for whom”. An entity row for the source document, such as the transcript an AI scribe drafted from, answers “from what”.
A client can also send provenance with the write itself: with the X-Provenance header, the server picks up the Provenance and fills out the target.2 That keeps the two in step, since the server knows the version id the client doesn’t yet.
A worked example: an agent updates an Observation
An agent, acting for Dr. Rao, changes an Observation’s status to final. Here is what each resource should say.
The AuditEvent from the server, shortened:
Code Data in JSON, the format software uses to pass records and settings.
{
"resourceType": "AuditEvent",
"type": { "code": "rest" },
"subtype": [{ "code": "update" }],
"action": "U",
"recorded": "2026-10-26T09:14:03Z",
"outcome": "0",
"agent": [
{ "who": { "reference": "Device/notes-agent" }, "requestor": true },
{ "who": { "reference": "Practitioner/rao" }, "requestor": false }
],
"source": { "observer": { "display": "FHIR server" } },
"entity": [{ "what": { "reference": "Observation/456/_history/4" } }]
}
The Provenance from the agent’s application:
Code Data in JSON, the format software uses to pass records and settings.
{
"resourceType": "Provenance",
"target": [{ "reference": "Observation/456/_history/4" }],
"recorded": "2026-10-26T09:14:03Z",
"activity": { "coding": [{ "code": "UPDATE" }] },
"agent": [{
"type": { "coding": [{ "code": "author" }] },
"who": { "reference": "Device/notes-agent" },
"onBehalfOf": { "reference": "Practitioner/rao" }
}],
"policy": ["https://example.org/policy/notes-draft-review"]
}
The codes here are illustrative; use the value sets your implementation guide requires. What matters is the division of labour. The AuditEvent says the event happened, from the server’s point of view, with an outcome. The Provenance says who wrote version 4, for whom, and under which policy. If the update had failed, there would be an AuditEvent with a failing outcome and no Provenance, because no version was created.
Profiles that narrow them
Base FHIR leaves both resources loose. Two profiles tighten them for this use:
- IHE Basic Audit Log Patterns (BALP) 1.1.4 profiles AuditEvent for create, read, update, delete and query, each with a patient-specific variant.3 If you’re unsure how to fill an AuditEvent, start from BALP.
- HL7 AI Transparency on FHIR defines AI Provenance and AI Device profiles to mark data an AI system influenced. It went to its first ballot in January 2026, and no official version has been published yet.4 Use it as guidance and expect it to change.
ISO 27789’s minimum for health record audit trails, user, subject of care, function and time, maps neatly onto AuditEvent’s agent, entity, subtype and recorded.5
Common mistakes
- Using Provenance as the audit log. It doesn’t cover reads or failures, and the author writes it about itself.
- One agent row for an AI action. Record both the AI system and the person it acted for; otherwise the person disappears from the trail.
- Unversioned targets. A Provenance pointing at
Observation/456rather than a specific version says nothing once the resource changes again. - Assuming the write did what it said. Both records describe the request and its result code. Neither checks that the stored data matches what was meant; our FHIR write benchmark found 11 of 17 synthetic writes where it didn’t.
We build Deedglass, which exports its records of agent actions in both shapes: FHIR R4 AuditEvent shaped to the BALP profiles, and Provenance shaped to the AI Transparency ballot’s AI Provenance profile, with ids and digests instead of clinical content.6 One limit stated with it: the profile constraints are checked by our own offline checker, not a validator loaded with those packages.6 For the wider record design, see HIPAA audit controls and AI agents and the audit trail for AI in healthcare. To see checks on a real database in your browser, try the playground; to grade what an agent can reach on your machine, run npx deedglass scan (the grade, explained).
Written with AI assistance and checked against our claims register.