Compliance & Traceability

Test Traceability for Medical Devices

Map FDA 21 CFR Part 820 and ISO 13485 requirements to TofuPilot features for device history records, test documentation, and audit trails.

JJulien Buteau
intermediate9 min readMarch 14, 2026

FDA 21 CFR Part 820 requires medical device manufacturers to maintain a Device History Record (DHR) for every unit produced. Capturing test results, serial numbers, measurements, and pass/fail determinations in an immutable record maps directly to FDA and ISO 13485 requirements.

Regulatory Requirements Overview

Medical device test traceability sits at the intersection of two frameworks. Both require documented evidence that each device was manufactured and tested according to approved procedures.

RegulationClauseRequirementWhere it maps
21 CFR 820.184DHRRecords demonstrating device manufactured per DMRTest records per serial number with procedure name
21 CFR 820.80Receiving, in-process, finished device acceptanceAcceptance activities with acceptance/rejection documentedPass/fail results with measurement limits
21 CFR 820.90Nonconforming productDocumented investigation of nonconformancesFailed runs with measurement-level detail
21 CFR 820.180General requirements (records)Records maintained for device lifetime or 2 yearsImmutable cloud storage with retention
21 CFR 820.250Statistical techniquesValid statistical techniques for process capabilityCpk, control charts, first pass yield
ISO 13485 7.5.3TraceabilityRecords per unit or batch through productionSerial tracking, unit history, sub-assembly links
ISO 13485 8.2.4Monitoring and measurementEvidence of conformity to acceptance criteriaMeasurements with units, limits, and pass/fail

This table maps requirements to where the evidence lives. It is not a compliance claim: your quality system, validation, and procedures are what satisfy the regulation, and the test records are one input to that. Software used in production also falls under 21 CFR 820.70(i), so the tooling itself needs validation for its intended use.

Structuring Tests for DHR Compliance

The DHR must include test results that prove each device was manufactured according to its Device Master Record (DMR). Structure your OpenHTF tests so each procedure maps to a documented test protocol.

test_medical_device.py
74 lines
# Medical device production test aligned with DHR requirementsimport openhtf as htffrom openhtf.util import unitsfrom tofupilot.openhtf import upload@htf.measures(    htf.Measurement("impedance_channel_1")        .in_range(minimum=980, maximum=1020)        .with_units(units.OHM)        .doc("Electrode impedance per IEC 60601-1, channel 1"),    htf.Measurement("impedance_channel_2")        .in_range(minimum=980, maximum=1020)        .with_units(units.OHM)        .doc("Electrode impedance per IEC 60601-1, channel 2"),    htf.Measurement("leakage_current")        .in_range(maximum=0.00001)        .with_units(units.AMPERE)        .doc("Patient leakage current, Type BF applied part per IEC 60601-1"),)def electrical_safety(test):    test.measurements.impedance_channel_1 = 1002.3    test.measurements.impedance_channel_2 = 997.1    test.measurements.leakage_current = 0.000003@htf.measures(    htf.Measurement("signal_accuracy_pct")        .in_range(minimum=99.0, maximum=101.0)        .doc("Measurement accuracy against NIST-traceable reference"),    htf.Measurement("noise_floor")        .in_range(maximum=0.000005)        .with_units(units.VOLT)        .doc("Input-referred noise, 0.05-150 Hz bandwidth"),    htf.Measurement("common_mode_rejection")        .in_range(minimum=100)        .doc("CMRR at 50/60 Hz, in dB"),)def signal_performance(test):    test.measurements.signal_accuracy_pct = 99.8    test.measurements.noise_floor = 0.0000021    test.measurements.common_mode_rejection = 112.4@htf.measures(    htf.Measurement("firmware_version")        .doc("Installed firmware version, must match DMR revision"),    htf.Measurement("self_test_result")        .in_range(minimum=1, maximum=1)        .doc("Device self-test pass (1) or fail (0)"),    htf.Measurement("battery_capacity_pct")        .in_range(minimum=95)        .doc("Battery capacity relative to rated"),)def system_validation(test):    test.measurements.firmware_version = "3.2.1-release"    test.measurements.self_test_result = 1    test.measurements.battery_capacity_pct = 98.7def main():    test = htf.Test(        electrical_safety,        signal_performance,        system_validation,        procedure_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",  # procedure UUID from the dashboard        part_number="MD-100",    )    test.add_output_callbacks(upload())    test.execute(lambda: "MD-2026-04-00192")if __name__ == "__main__":    main()

Install it with pip install "tofupilot[openhtf]". Each measurement references the applicable standard through .doc() and carries its acceptance limits, so the stored record shows what was required as well as what was measured.

Version the procedure whenever limits change. A DHR is only defensible if you can say which acceptance criteria applied to a given unit on the day it was tested, and that becomes impossible once limits have moved without a version marker.

Acceptance Activities (21 CFR 820.80)

The FDA requires documented acceptance activities at receiving, in-process, and finished device stages. Run separate procedures for each stage, all using the same serial number.

test_acceptance_stages.py
64 lines
# Three-stage acceptance testing for FDA 820.80 complianceimport openhtf as htffrom openhtf.util import unitsfrom tofupilot.openhtf import uploadRECEIVING_PROCEDURE_ID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"IN_PROCESS_PROCEDURE_ID = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"FINISHED_PROCEDURE_ID = "zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz"@htf.measures(    htf.Measurement("incoming_visual_pass")        .in_range(minimum=1, maximum=1),    htf.Measurement("incoming_dimension_check")        .in_range(minimum=24.9, maximum=25.1)        .with_units(units.MILLIMETRE),)def receiving_inspection(test):    test.measurements.incoming_visual_pass = 1    test.measurements.incoming_dimension_check = 25.02@htf.measures(    htf.Measurement("solder_paste_height")        .in_range(minimum=0.10, maximum=0.15)        .with_units(units.MILLIMETRE),    htf.Measurement("component_presence")        .in_range(minimum=1, maximum=1),)def in_process_inspection(test):    test.measurements.solder_paste_height = 0.12    test.measurements.component_presence = 1@htf.measures(    htf.Measurement("final_functional_test")        .in_range(minimum=1, maximum=1),    htf.Measurement("label_verification")        .in_range(minimum=1, maximum=1),)def finished_device_acceptance(test):    test.measurements.final_functional_test = 1    test.measurements.label_verification = 1def run_stage(phase, procedure_id, serial):    test = htf.Test(        phase,        procedure_id=procedure_id,        part_number="MD-100",    )    test.add_output_callbacks(upload())    test.execute(lambda: serial)def main():    serial = "MD-2026-04-00192"    run_stage(receiving_inspection, RECEIVING_PROCEDURE_ID, serial)    run_stage(in_process_inspection, IN_PROCESS_PROCEDURE_ID, serial)    run_stage(finished_device_acceptance, FINISHED_PROCEDURE_ID, serial)if __name__ == "__main__":    main()

The three stages run on separate procedures so each can be reported independently, while the shared serial number keeps them on one unit history. In production these are three scripts on three stations, days apart; they are shown together here only to make the pattern visible.

The unit page for this serial shows all three acceptance stages in order, which is what an inspector asks to see.

Nonconforming Product (21 CFR 820.90)

When a device fails testing, the FDA requires documented investigation. The record holds the exact measurement that triggered the failure, its value, and the limit it exceeded.

Failed runs stay visible in the unit's history alongside every retest, so the investigation, rework, and retest cycle is traceable. That covers the record-keeping side of 820.90. The investigation and disposition decisions themselves live in your CAPA system; the test data is evidence feeding it, not a substitute for it.

Component Traceability with Sub-Units

ISO 13485 clause 7.5.3 requires traceability of components used in each device. sub_units is a field on htf.Test(...) that links component serial numbers to the finished device.

test_component_traceability.py
30 lines
# Record component serials for ISO 13485 traceabilityimport openhtf as htffrom tofupilot.openhtf import upload@htf.measures(    htf.Measurement("assembly_complete")        .in_range(minimum=1, maximum=1),)def final_assembly(test):    test.measurements.assembly_complete = 1def main():    test = htf.Test(        final_assembly,        procedure_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",        part_number="MD-100",        sub_units=[            {"serial_number": "SENSOR-2026-08812"},            {"serial_number": "PCBA-2026-04410"},            {"serial_number": "BATT-2026-11023"},        ],    )    test.add_output_callbacks(upload())    test.execute(lambda: "MD-2026-04-00192")if __name__ == "__main__":    main()

The serials are hardcoded here for brevity. On a real line they come from barcode scans collected before the test is constructed, since sub_units is a constructor argument.

If a component supplier issues a recall, the unit hierarchy is what tells you which finished devices contain the affected parts, rather than quarantining everything built in a date range.

Audit Trail and Exports

The records support what FDA and ISO 13485 auditors ask for:

  • Device History Record. Every test run for a serial number, with procedure, station, operator, measurements, and result. This forms the test portion of the DHR.
  • Process capability. Cpk values and control charts for any measurement, supporting statistical process control per 21 CFR 820.250.
  • Failure analysis. Failure Pareto and measurement histograms to support CAPA investigations.
  • Record retention. Immutable storage, with historical records not alterable through the standard interface.

Export test data from the dashboard for inclusion in your formal DHR documentation package. Records include timestamps, station identifiers, and all measurement values with their acceptance criteria.

Confirm retention periods against your own device lifetime requirement, and keep an export path that does not depend on a single vendor. 820.180 obligations outlive any particular tool.

More Guides

Put this guide into practice