"Show me everything user Y did."

That is the most ordinary request in a HIPAA access review. Somebody in compliance asks it, an engineer runs a query against the audit log, and a list comes back. It has worked for as long as audit logs have existed.

Once AI agents are in the environment, that query returns a clean list that is wrong.

It does not error and it does not come back empty. What comes back is a plausible, well-formatted list of actions attributed to a clinician, some of which the clinician never performed. The query succeeds, and that is worse than if it had failed.

One actor field was enough, until it wasn't

Every audit standard in use today models a single actor per event. FHIR R5 AuditEvent does. IHE ATNA does. CloudTrail does. That model is correct as long as three identities are the same: the identity that authenticated, the entity that performed the action, and the person whose intent the action represents.

For a clinician clicking through a chart, those three are one human, and a single actor field captures all of it without losing anything.

Agents break the assumption. An agent can drive a browser inside a clinician's authenticated session, call an API under its own service credential, or run scheduled work with no human present at all. In each case the three identities separate. One field is left holding three answers, and nothing in the record says which one it took.

In behavioral health the stakes are higher than the general HIPAA case. 42 CFR Part 2 governs substance use disorder records and is stricter than standard HIPAA in the areas that matter here. An agent-mediated disclosure of a Part 2 record is still a disclosure, and the question of whose consent authorized it takes a person as its answer. If the audit trail cannot name that person separately from the machine, the system is not auditable.

Three roles instead of one

bh-audit-schema v2.0 splits the single actor into three: the authenticating identity (whose credentials were presented), the acting identity (which agent performed the action), and the authorizing identity (the human whose intent the action represents).

When a human acts directly, all three collapse into one person and the event carries a single agent the way a conventional audit record does. The common case stays simple. The attribution machinery only appears when the roles actually separate.

Two things keep this from being a documentation convention. Validation rejects an agent action with no identifiable authorizing human, so a non-attributed agent event cannot be emitted as a conforming event at all. And the authorizing identity is always a human, because an agent cannot bear authorizing accountability. When an agent spawns sub-agents, that identity stays the root human at every depth. An intermediate agent never becomes the authorizer, which is what stops accountability from being laundered through a chain.

Supervision gets recorded rather than assumed. An event carries whether the delegation was supervised, autonomous, or scheduled, so a reviewer can tell the difference between a human who was watching, a human who authorized and walked away, and work that ran with nobody there. Those three carry different risk and the record should not flatten them into "an agent did it."

The part I did not expect

HL7 has already recognized part of this problem. There is a published extension, auditevent-OnBehalfOf, that adds a delegation reference to AuditEvent.agent. Finding it was encouraging, because it confirms the community sees delegation as a real gap in base AuditEvent.

Then I worked an example through it and the extension turned out to relocate the problem rather than close it.

Take one action: an AI browser agent, operating inside a clinician's authenticated session, appends an addendum to a patient note. Same event, same clinician, same note. Only the agent array differs.

As a stock R5 AuditEvent, there is one agent and the clinician is the requestor. The agent that actually wrote the note does not appear anywhere in the record.

"agent": [
  {
    "who": "user_123",
    "role": [ { "text": "clinician" } ],
    "requestor": true
  }
]

With the extension applied, the delegation becomes visible, which is a real improvement. But the agent has taken the who slot, the clinician has been demoted into an extension, and requestor has moved with the agent.

"agent": [
  {
    "who": "agent_inst_4471",
    "requestor": true,
    "extension": [
      {
        "url": ".../StructureDefinition/auditevent-OnBehalfOf",
        "valueReference": "user_123"
      }
    ]
  }
]

Read that against the access review from the top of this post. A consumer filtering on requestor = true, which is the most standard access-review query in FHIR, is handed a browser agent instead of the accountable clinician. There is one who slot and three identities competing for it. The record goes from naming the human and losing the machine to naming the machine and demoting the human. Neither version answers whose credentials were presented, whether anyone was supervising, or which agent session this belonged to.

Against the profile, the three attribution roles are separate slices and requestor stays on the accountable human.

"agent": [
  { "type": "authenticating-identity", "who": "user_123",        "requestor": false },
  { "type": "acting-identity",         "who": "agent_inst_4471", "requestor": false },
  { "type": "authorizing-identity",    "who": "user_123",        "requestor": true  }
],
"extension": [
  { "url": "delegation-type",  "valueCode": "supervised" },
  { "url": "agent-session-id", "valueString": "agsess_01J9X0002" },
  { "url": "chain-depth",      "valuePositiveInt": 1 }
]

Abbreviated for legibility. In the real resource who is a Reference carrying an opaque identifier, the attribution roles are CodeableConcept codings on agent.type, and every extension carries its full canonical URL.

The same human occupies two slices here, authenticating and authorizing, and the profile does not collapse them. That redundancy is deliberate. A consumer filtering on the authorizing slice writes one query whether or not the two identities happen to coincide.

Someone else already landed on this shape

This is not a hypothetical failure I constructed to justify a profile. NHID-Clinical, a practitioner-led governance framework for healthcare voice AI at v1.3 since June 2026, emits FHIR R4 AuditEvent bundles and documents its agent structure openly: three agent slices carrying the AI voice agent as the requestor, the payer system as the destination, and the provider organization in the on-behalf-of position when an NPI is present.

Within the standard as written, that is a defensible mapping, and publishing an audit model in that much detail is more than most deployments do. It is also the exact shape described above, in R4, where agent.who and agent.requestor behave as they do in R5. An access review filtered on requestor = true across those bundles returns the voice agent. Here the accountable human is not demoted into an extension so much as absent from the record entirely, because the on-behalf-of slot holds an organization and an organization cannot hold intent.

When an independent effort, working carefully and documenting its choices, lands on the structure the analysis predicts, the result says something about the standard and nothing about the implementer.

Baynard, B. NHID-Clinical: A Behavioral Baseline for Healthcare Voice AI, v1.3, June 2026, CC BY 4.0. The agent structure described above is specified in the project's FHIR AuditEvent mapping document. The description reflects the framework as read in July 2026, and the project describes itself as practitioner-led rather than an accredited standard.

What a schema cannot do

A schema can represent attribution. It cannot detect a non-cooperating agent. To a target system, an agent driving a UI under a human's credentials is indistinguishable from that human. Only agent-side infrastructure knows a delegation exists and can emit the record. The UI-driving case is the sharpest version of this limit: because the event is structurally identical to genuine human action, the schema cannot conditionally require a delegation without also rejecting legitimate direct actions.

Which means a deployment's attribution guarantee is only as strong as its weakest emission path, and claims about it should be tier-specific. The specification defines three tiers. Enforced is a gateway, API proxy, or instrumented SDK that constructs and emits the event, where the agent cannot reach the target system without emission occurring. Instrumented is the agent harness emitting through hooks, cooperative but structural. Advisory is a rules file telling agents to self-report, which is an honor system in a text file.

Advisory templates are worth publishing as an adoption on-ramp, labeled for exactly what they are. They are not audit infrastructure.

Why the guarantee belongs in the schema

I have a concrete reason for putting attribution in validation rather than in guidance, and it comes out of running audit telemetry in production since April 2026.

Across the instrumented services reporting in, the services split cleanly on data classification. Several classify every event correctly. The two highest-volume services set no classification at all, falling back to an unclassified value, which is why 99.8% of events across the deployment are unclassified.

Same library. Same organization. Different integration decisions.

Classification is an optional argument on the emitter library. The field works when it is passed, and an optional argument is one that does not get passed.

A schema cannot force a producer to classify correctly, any more than it can force PHI out of a free-text field. What it can do is make classification a first-class, queryable field so that unclassified emission is visible and auditable instead of silently absent. Making the omission visible is the contribution. Two caveats on that number, because it would be easy to over-read: the telemetry is opt-in by design, so nothing here is a population claim about the field, and it records successful emissions without aggregating emission-time rejections, so any count taken from it is a floor on validated volume and not a failure rate.

Where this actually stands

The specification is released and tagged v2.0.0, and it is the default schema version, so adopters can pin it today. It validates against a corpus of seven positive and thirteen negative examples, and the reference translator produces structurally valid R5 AuditEvent resources for all seven, checked in continuous integration on every commit.

The agent attribution capability has not yet run against live agent traffic. That first deployment is the near-term work, and until it happens the capability is a prototype with a production base underneath it. Everything in the telemetry section above comes from human and service traffic, not agents. The integration decisions that turn out to matter will not be visible until real agent traffic hits it.

The profile's canonical URLs are identifiers, not published endpoints, and they do not currently resolve. Publishing this as a formal FHIR Implementation Guide with dereferenceable StructureDefinitions is future work, and it is the kind of artifact that should come out of community adoption rather than ahead of it.

Two gaps I would like to raise with the FHIR community. R5 outcome codes have no value for a correct authorization denial that is distinct from operational failure, so a system that correctly refuses an agent looks like a system that broke. The translator works around it today by mapping a denial to the serious-failure code and attaching a denied coding in outcome.detail, which preserves the distinction for any consumer that knows to look and loses it for every consumer that does not. A base-level code would be the durable fix. Separately, base AuditEvent carries no PHI-minimization discipline against agent-observed content, which becomes a live problem the moment agents that see screens start emitting what they saw.

The paper and the code

The full write-up is Agent Attribution for Healthcare Audit, twelve pages, with the FHIR R5 gap analysis, the regulatory mapping, and the emission tiers in detail. It carries a DOI: 10.5281/zenodo.21682867. A version of the work went to the HL7 AI Challenge this year.

The schema, the profile, the translator, and the example corpus are all in the bh-audit-schema repository under Apache 2.0. If you emit audit events from Python, bh-audit-logger v2.0 targets the v2.0 schema by default.

pip install bh-audit-logger

There is one specific thing I am looking for. I am putting together a proposal for a FHIR Connectathon track on agent attribution, and what would make it worth running is implementers willing to exercise the profile against their own agent traffic. If your team is building in this direction, I would like to hear from you before the proposal goes in.

If you are deploying agents against clinical systems, or you think the three-role split is wrong, I would genuinely like to hear it. The failure mode I care most about is a compliance officer being handed an access review that looks correct and is not. Find me on LinkedIn or open an issue on the repo.