Wednesday, January 7, 2026

Tracking Patient Data Corrections

When data are corrected, especially when the Patient themselves reported that the data was wrong, there needs to be breadcrumbs left behind. Correction is a form of updating of data, but with the special need to indicate that previous data was in error. Some corrections result in the previous data being removed, while others result in the previous data being replaced with corrected data.

Therefore the needs to be a special indication of a correction Provenance, as distinct from a general update Provenance, with the following goals:

  1. So that future uses understand the past might have seen different data
  2. So that requests for data, especially from outside (e.g. HIE), can see that previous data has been corrected.

The recipient can then find if they have a copy or used the previous data, and take appropriate action.

I have this documented in an IG that has both FHIR R4 and R6 details

- ci-build for FHIR R4 - http://build.fhir.org/ig/JohnMoehrke/correction/branches/main/index.html

- ci-build for FHIR R6 - https://build.fhir.org/ig/JohnMoehrke/correction/branches/R6/index.html

Use-cases

  • A patient contacts their provider to report that there is some data that is incorrect. An investigation is done, and the data are found to be in error. The data is corrected, and a Provenance resource is created to indicate the correction.
  • A Quality Improvement process finds that some data is incorrect. An investigation is done, and the data are found to be in error. The data is corrected, and a Provenance resource is created to indicate the correction.
    • One possibility for this detection is an AI system that reviews data and finds potential errors.

Profiling

Recommended use of a Profile on Provenance to indicate corrections:

  • activity.coding: will be code FIXDATA from v3-ActReason to indicate that this Provenance is for a data correction.
  • reason.coding: can use a set of codes often associated with a correction.
  • recorded: will be the time the correction was made.
  • target: points at the resource(s) that are corrected.
  • agent: indicates who made the correction.
    • the Patient would be indicated here for corrections requested by the patient.
  • entity: indicates the records corrected.
    • entity.role=#removal points at the record being replaced or removed (if applicable).
    • entity.role=#revision points at the record being revised (if applicable).
  • textual reason can be in why in R6, or in activity.text in R4/R5.
  • evidence such as the request for the correction can be in basedOn in R6, or in entity with role=#derivation in R4/R5.

Examples:

  • Provenance removal resource documenting the removal of incorrect food allergy observation. This example does not point at a resource that explains the correction, such as a DocumentReference.
  • Provenance replacement Provenance resource documenting the replacement of an immunization record that was in error. This example includes a reference to a DocumentReference that explains the correction.
  • Provenance AI corrected Provenance resource documenting the detection of an error in FHIR data by an AI system. The original Observation that is error is removed, and a new AllergyIntolerance resource is created to replace it.
  • Provenance AI corrected with replacement Provenance resource documenting the detection of an error in FHIR data by an AI system, where the Observation was used to record a food allergy, but the AI system detected that this should have been an AllergyIntolerance resource instead. Thus the Observation is removed, and the AllergyIntolerance is created.
  • Provenance patient correction Provenance resource documenting a patient requested correction of their birth date.
  • Provenance revision correction Provenance resource documenting the revision of a Condition resource with corrected onset date. In this case the original Condition is revised. Thus the old version history/1 is indicated as revised, and the new version history/2 is indicated as the current version.

changes from R4->R6 Provenance:

  • In R6 basedOn would be used to hold the evidence for the correction. Where in R4 would use entity with role=derivation, and in R5 with role=instantiates.
  • In R6 patient element exists. Same in R6. R4 didn't have patient, so other methods would be needed to find all Provenance pointing at a patient's data.
  • In R6 why exists. This does not exist in R4 or R5. Sometimes the activity.text would be used for this.
  • In R6 and R5 authorization is a codeableReference, where in R5 reason is used to hold the PurposeOfUse

Discovering Corrections

To find all corrections for a specific patient, one can search for Provenance resources with Provenance.activity with the FIXDATA code, and where the Provenance points at resources related to the patient.

FHIR R5 and R6

In FHIR R5 and R6 there is a patient element on Provenance, making it easy to find all Provenance resources related to a specific patient. One needs to simply filter on Provenance.activity with the FIXDATA code.

GET [base]/Provenance?patient=[patient-id]&activity=http://terminology.hl7.org/CodeSystem/v3-ActReason|FIXDATA

FHIR R4

In FHIR R4 one needs to search for Provenance resources that point at resources related to the patient. This can be done by searching for Provenance with Provenance.activity with FIXDATA and where the target or entity.what points at resources related to the patient. Since either target or entity.what may point at resources that contain a patient reference, these cannot be combined in a single standard FHIR search query. Both chained searches must be performed and results combined client-side to find all relevant Provenance resources:

GET [base]/Provenance?activity=http://terminology.hl7.org/CodeSystem/v3-ActReason|FIXDATA&target.patient=[patient-id]
GET [base]/Provenance?activity=http://terminology.hl7.org/CodeSystem/v3-ActReason|FIXDATA&entity.what.patient=[patient-id]

Proposed FHIR Document section

I could not find an existing FHIR Document section that covers this topic. For example, in an International Patient Summary (IPS) this could be an additional section. In this way as new Cross-Community requests are made corrections would be communicated. This section contains information about corrections made to the patient's data, including details of the original data, the corrected data, and the reason for the correction. This information is provided to ensure data integrity and transparency in the patient's health record. I propose a new section:

  • Section code: http://loinc.org#77472-9 (Information integrity attribute)
  • Section title: Corrections to Data
  • Section text: Text summary of the corrections made.
  • Section entries: Provenance resources documenting corrections made to the patient's data following the correction Profile on Provenance.

Note that loinc code 77472-9 is "Information integrity attribute", which seems appropriate for this section. I am open to other code suggestions.

Conclusion

This is just my view on this use-case. I expect there is experience that might help refine. Please let me know

Sunday, December 28, 2025

FHIR Consent backed by XACML enforcement

Imagine a scenario where my organization uses XACML for all of our policy. This is where HR policies are on what HR relevant data can be accessed by whom, etc. This same XACML system would also be used to protect Patient data, including imaging, EHR, and HIE access. Thus, the XACML system is very broad and deep. Thus, it is the one that we want to use to protect everything.

Along comes FHIR Consent and we feel that there is a need to have some representation of the Patient Consent in FHIR form, but the actual rules that are applied stay in XACML. So, what does the FHIR Consent look like?

Generally speaking, the FHIR Consent would be just a cross-reference between the Patient as known in FHIR with the XACML subject id as known in XACML. The FHIR Consent would not replicate any of the patient specific rules. 

So, lets imagine a Patient has indicated that they agree to permit all the normal clinical activities with Normal sensitivity data, but that they do not allow external access to Restricted sensitivity data. 

In FHIR Consent, this would be a set of Consent.provisions; but in my case I already have this in computable form in XACML.

Further, my organizational overriding policies are written in XACML.


To see what this looks like, I have written a simple Implementation Guide: The Consent points at the overriding policy and the patient specific policy as shown below. This is Profiled:

XACML Policies

For those more familiar with XACML, can you check my work? I don't pass off these two policy sets as perfect, but as small representative examples.

Using XACML leverages an existing standard for defining access control policies. XACML policies are XML documents that specify rules for granting or denying access to resources based on various attributes, such as user roles, resource types, and environmental conditions.

XACML Overriding Policy

The XACML Overriding Policy is a policy set that defines the overarching access control rules for FHIR resources. This policy is intended to be used in conjunction with patient-specific XACML policies referenced in FHIR Consent resources. The overriding policy ensures that certain organizational or regulatory requirements are consistently applied across all patient consents.

<!-- This XACML policy file defines an organizational governance layer that overrides patient consent preferences. Specifically:

Purpose: It demonstrates how an organization's data access rules take precedence over patient consent policies using XACML's "deny-overrides" combining algorithm.

Key Rules:

1. Emergency Access - Permits doctors to access data during life-safety emergencies, regardless of consent restrictions
2. Archived Data Denial - Strictly forbids access to archived records, overriding any patient permits
3. Administrative Staff Restriction - Denies administrative staff access to data tagged as "Restricted" (R), even if patient consent would allow it

Context: This sits within a larger governance framework where patient consent (referenced via PolicySetIdReference) is evaluated, but organizational policies can override patient preferences when necessary for clinical workflows, safety, or compliance reasons. A master container applies default-deny if neither permits nor denies are found.
-->

<Policy PolicyId="Org_Policy_7890_Workflow_Governance" 
        RuleCombiningAlgId="urn:oasis:names:tc:xacml:3.0:rule-combining-algorithm:deny-overrides" 
        xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17">
    
    <Description>
        Organizational Governance: Enforces workflow roles and clinical status.
        This policy overrides subject-level permits.
    </Description>

    <Target/> 
    
    <Rule RuleId="Emergency_Access_Permit" Effect="Permit">
        <Description>Allows doctors to access data regardless of tags during a life-safety event.</Description>
        <Condition>
            <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">Emergency</AttributeValue>
                <AttributeDesignator 
                    AttributeId="urn:example:names:clinical:access-context" 
                    Category="urn:oasis:names:tc:xacml:3.0:attribute-category:environment" 
                    DataType="http://www.w3.org/2001/XMLSchema#string" 
                    MustBePresent="true"/>
            </Apply>
        </Condition>
    </Rule>

    <Rule RuleId="Deny_Archived_Data_Access" Effect="Deny">
        <Description>Strictly forbids access if the record is in 'Archived' status, overriding user permits.</Description>
        <Target>
            <AnyOf>
                <AllOf>
                    <Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                        <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">Archived</AttributeValue>
                        <AttributeDesignator 
                            AttributeId="urn:example:names:resource:status" 
                            Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource" 
                            DataType="http://www.w3.org/2001/XMLSchema#string" 
                            MustBePresent="true"/>
                    </Match>
                </AllOf>
            </AnyOf>
        </Target>
    </Rule>

    <Rule RuleId="Deny_Non_Clinical_Staff_Restricted" Effect="Deny">
        <Description>Forbids administrative staff from seeing any data tagged as 'Restricted' (R).</Description>
        <Condition>
            <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:and">
                <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                    <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">Administrative</AttributeValue>
                    <AttributeDesignator AttributeId="urn:oasis:names:tc:xacml:2.0:subject:role" Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="true"/>
                </Apply>
                <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                    <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">R</AttributeValue>
                    <AttributeDesignator AttributeId="urn:example:med:names:resource:data-tag" Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="true"/>
                </Apply>
            </Apply>
        </Condition>
    </Rule>
</Policy>

XACML Patient Consent Policy


The XACML Patient Consent Policy is a policy document that defines the specific access control rules for an individual patient. This policy is referenced in the FHIR Consent resource and works in conjunction with the XACML Overriding Policy to determine access permissions for FHIR resources.

<!-- This XACML policy file encodes a patient consent for Patient ID 12345. Specifically:

Purpose: Defines the patient's preferences for data sharing based on sensitivity tags using Attribute-Based Access Control (ABAC).

Consent Rules:

1. Permits Normal Data - Allows sharing of data tagged as "Normal" (N) sensitivity
2. Denies Restricted Data - Blocks sharing of data tagged as "Restricted" (R) sensitivity

How it Works:

- Targets requests where the subject-id matches patient 12345
- Uses "deny-overrides" at the PolicySet level to ensure denials take precedence
- Contains two sub-policies: one for Normal data (permit-overrides) and one for Restricted data (deny-overrides)
- Evaluates the data-sensitivity attribute on resources to determine access
- This represents the patient's preferences layer in the consent framework, which works in conjunction with organizational policies (like xacml-overriding.xml) to make final access decisions.
-->

<PolicySet xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17"
           PolicySetId="consent-policy-set-12345"
           Version="1.0"
           PolicyCombiningAlgId="urn:oasis:names:tc:xacml:1.0:policy-combining-algorithm:deny-overrides">

    <Description>Consent Policy Set for Patient ID 12345</Description>
    <Target>
        <AnyOf>
            <AllOf>
                <Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                    <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">12345</AttributeValue>
                    <AttributeDesignator 
                        AttributeId="urn:oasis:names:tc:xacml:1.0:subject:subject-id" 
                        Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject" 
                        DataType="http://www.w3.org/2001/XMLSchema#string" 
                        MustBePresent="true"/>
                </Match>
            </AllOf>
        </AnyOf>
    </Target>

    <Policy PolicyId="consent-policy-12345-normal-data"
            RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:permit-overrides">

        <Description>Policy to permit sharing of Normal (N) data</Description>

        <Target>
            <AnyOf>
                <AllOf>
                    <Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                        <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">Normal</AttributeValue>
                        <AttributeDesignator AttributeId="data-sensitivity"
                                             Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource"
                                             DataType="http://www.w3.org/2001/XMLSchema#string"
                                             MustBePresent="true"/>
                    </Match>
                </AllOf>
            </AnyOf>
        </Target>

        <Rule RuleId="permit-normal-data" Effect="Permit">
            <Description>Permit access to Normal data</Description>
        </Rule>

    </Policy>

    <Policy PolicyId="consent-policy-12345-restricted-data"
            RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:deny-overrides">

        <Description>Policy to deny sharing of Restricted (R) data</Description>

        <Target>
            <AnyOf>
                <AllOf>
                    <Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                        <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">Restricted</AttributeValue>
                        <AttributeDesignator AttributeId="data-sensitivity"
                                             Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource"
                                             DataType="http://www.w3.org/2001/XMLSchema#string"
                                             MustBePresent="true"/>
                    </Match>
                </AllOf>    
            </AnyOf>
        </Target>
        <Rule RuleId="deny-restricted-data" Effect="Deny">
            <Description>Deny access to Restricted data</Description>
        </Rule>
    </Policy>
</PolicySet>