DEV Community

Alex Kane
Alex Kane

Posted on

n8n for HealthIT & EHR Interoperability SaaS: 5 Automations for ONC Information Blocking, FHIR R4, and HIPAA

Your EHR platform just processed a patient record request through a cloud automation workflow. The workflow vendor's servers are in Virginia. The patient's electronic health information briefly transited a third-party cloud service with its own terms of service.

That transit is the information blocking problem the ONC has been building toward since 2016.

The Clock That Moves Fastest in HealthIT

ONC information blocking (45 CFR §170.404): up to $1,000,000 per violation per day for health IT developers, health information exchanges, and health information networks. Not per incident. Per violation. Per day.

HIPAA breach notification (45 CFR §164.404): 60 days to notify affected individuals after discovery. If the breach affects 500+ residents of a state or jurisdiction, media notification is also required within 60 days. HHS notification: within 60 days for large breaches, annually for smaller ones.

21 CFR Part 11 §11.10(e): continuous, computer-generated audit trail — tamper-evident, attributable, and under your validated system control. Cloud iPaaS audit logs are not validated electronic records under Part 11.

Every one of these obligations assumes your automation infrastructure is auditable and under your control. If your workflow engine is a SaaS iPaaS — it is not.

The ONC Information Blocking Problem

The 21st Century Cures Act defines information blocking as any practice that is likely to interfere with, prevent, or materially discourage access, exchange, or use of electronic health information (EHI). The ONC's §170.404 rule applies the $1M/day civil money penalty to health IT developers, HIEs, and HINs — not just covered entities under HIPAA.

When your automation platform routes EHI through a third-party cloud vendor that has its own data retention, access controls, and terms of service, you introduce a dependency that could constitute interference. A vendor outage that delays patient data access is a potential §170.404 event.

Self-hosted n8n keeps EHI processing inside your system boundary — your BAAs, your audit trail, your validated controls. No third-party dependency between the patient's request and your FHIR server.

7 HealthIT & EHR Interoperability SaaS Compliance Tiers

These tiers determine which regulatory clocks govern your platform and which compliance flags your onboarding workflow must set:

Tier Primary Regs Fastest Clock
EHR_PLATFORM_VENDOR ONC 45 CFR §170 / HL7 FHIR R4 / HIPAA §164 / 21 CFR Part 11 / Cures Act §4004 $1M/day ONC violation
HEALTH_INFORMATION_EXCHANGE ONC §170.404 / HIPAA §164 / HL7 FHIR R4 / State HIE law / 42 CFR Part 2 $1M/day ONC violation
PATIENT_PORTAL_SAAS ONC §170.315(e)(1) / HIPAA §164.524 access right / ADA Title III / WCAG 2.1 AA 30 days HIPAA access
CLINICAL_DECISION_SUPPORT_SAAS FDA SaMD risk classification / ONC CDSS rule / HIPAA §164 / 21 CFR Part 820 QMS FDA 510(k) window
TELEHEALTH_PLATFORM HIPAA §164 / Ryan Haight Online Pharmacy Act / State medical licensing / FCC telehealth / DEA special registration 60 days HIPAA breach
DIGITAL_THERAPEUTICS_SAAS FDA SaMD / 21 CFR Part 11 / HIPAA §164 / ONC §170.404 / IRB 21 CFR Part 56 FDA response window
HEALTHIT_STARTUP ONC §170 / HIPAA §164 BAA / SOC2 Type II / HL7 FHIR R4 / State health data laws 60 days HIPAA breach

5 n8n Automations — Import-Ready JSON

1. Tier-Segmented HealthIT Customer Onboarding Drip

Sets compliance context on Day 0 based on customer tier. EHR vendors get ONC §170.404 + FHIR R4 API mandate brief. HIEs get information blocking + 42 CFR Part 2 note. Clinical AI platforms get SaMD classification note.

{
  "name": "HealthIT Tier-Segmented Onboarding Drip",
  "nodes": [
    {
      "id": "1",
      "name": "Customer Signed",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "parameters": {
        "sheetId": "YOUR_SHEET_ID",
        "range": "Customers!A:H",
        "event": "rowAdded"
      },
      "position": [
        100,
        200
      ]
    },
    {
      "id": "2",
      "name": "Classify Tier",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "const tier = $json.compliance_tier || 'HEALTHIT_STARTUP';\nconst tierMessages = {\n  EHR_PLATFORM_VENDOR: 'Your EHR platform is subject to ONC 45 CFR \u00a7170.404 information blocking prohibition \u2014 up to $1M/day per violation for health IT developers. HL7 FHIR R4 patient access API (\u00a7170.315(g)(10)), provider directory API, and drug formulary API are required. 21 CFR Part 11 \u00a711.10(e): continuous computer-generated audit trail required. Self-hosted n8n keeps EHI processing inside your system boundary \u2014 no third-party iPaaS dependency between patient request and FHIR server.',\n  HEALTH_INFORMATION_EXCHANGE: 'HIEs are health information networks under ONC \u00a7170.404 \u2014 information blocking applies directly at $1M/day. 42 CFR Part 2: substance use disorder records have stricter consent requirements than general HIPAA. HL7 FHIR R4 interoperability APIs required for participating in CMS data sharing programs. Self-hosted n8n eliminates SaaS iPaaS as a third-party in your EHI exchange chain.',\n  PATIENT_PORTAL_SAAS: 'HIPAA \u00a7164.524: patients have the right to access their PHI within 30 days of request. ONC \u00a7170.315(e)(1): patient access to health data via certified API. ADA Title III + WCAG 2.1 AA: portal must be accessible to patients with disabilities. Self-hosted n8n keeps portal automation audit logs under your validated control.',\n  CLINICAL_DECISION_SUPPORT_SAAS: 'FDA Software as a Medical Device (SaMD) guidance + 21st Century Cures Act CDSS rule: your decision support logic classification determines FDA oversight level. 21 CFR Part 11: electronic records and audit trails must meet validation requirements. HIPAA \u00a7164: PHI processed by CDSS is subject to BAA requirements with all cloud vendors.',\n  TELEHEALTH_PLATFORM: 'HIPAA \u00a7164: 60-day breach notification window. Ryan Haight Act / DEA special registration: prescribing via telehealth requires DEA registration in each patient state. State medical licensing: prescribers must be licensed in patient state. FCC connected care program: broadband subsidy eligibility tracking. Self-hosted n8n keeps telehealth session automation inside your HIPAA boundary.',\n  DIGITAL_THERAPEUTICS_SAAS: 'FDA SaMD classification: DTx products intended to treat, manage, or prevent a condition may be Class II devices requiring 510(k). 21 CFR Part 11: clinical trial data and patient engagement records must meet electronic records validation. IRB 21 CFR Part 56: human subjects research oversight. HIPAA \u00a7164: patient engagement data is PHI \u2014 BAA with all cloud automation vendors required.',\n  HEALTHIT_STARTUP: 'HIPAA \u00a7164: BAA required with every cloud vendor that processes PHI \u2014 including your automation platform. ONC \u00a7170.404: if you are a health IT developer, information blocking applies at $1M/day. SOC2 Type II is typically required by hospital and health system procurement. HL7 FHIR R4: interoperability mandate for CMS-regulated payers and providers.'\n};\nreturn [{ json: { ...($json), tier, onboarding_message: tierMessages[tier] || tierMessages.HEALTHIT_STARTUP } }];"
      },
      "position": [
        300,
        200
      ]
    },
    {
      "id": "3",
      "name": "Day 0 Welcome",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "operation": "send",
        "to": "={{ $json.email }}",
        "subject": "Welcome to {{ $json.platform_name }} \u2014 Your HealthIT Compliance Architecture Briefing",
        "message": "={{ '<p>Welcome, ' + $json.contact_name + ',</p><p>' + $json.onboarding_message + '</p><p>Your dedicated compliance success manager will reach out within 1 business day.</p>' }}"
      },
      "position": [
        500,
        200
      ]
    },
    {
      "id": "4",
      "name": "Wait 7 Days",
      "type": "n8n-nodes-base.wait",
      "parameters": {
        "unit": "days",
        "amount": 7
      },
      "position": [
        700,
        200
      ]
    },
    {
      "id": "5",
      "name": "Day 7 FHIR Setup",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "operation": "send",
        "to": "={{ $json.email }}",
        "subject": "Day 7 \u2014 FHIR R4 API setup + compliance deadline tracker",
        "message": "={{ '<p>Hi ' + $json.contact_name + ',</p><p>This week: three items for your compliance baseline:</p><ol><li>Verify HL7 FHIR R4 patient access API endpoint health monitoring is configured</li><li>Add your ONC \u00a7170.404 complaint response deadline to the compliance tracker</li><li>Confirm your 21 CFR Part 11 audit trail is capturing all workflow execution records</li></ol>' }}"
      },
      "position": [
        900,
        200
      ]
    },
    {
      "id": "6",
      "name": "Log to Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "parameters": {
        "operation": "append",
        "sheetId": "YOUR_ONBOARDING_LOG",
        "range": "Onboarding!A:E",
        "values": [
          [
            "={{ $json.email }}",
            "={{ $json.tier }}",
            "={{ $now }}",
            "drip_started",
            "={{ $json.platform_name }}"
          ]
        ]
      },
      "position": [
        1100,
        200
      ]
    }
  ],
  "connections": {
    "Customer Signed": {
      "main": [
        [
          {
            "node": "Classify Tier",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Tier": {
      "main": [
        [
          {
            "node": "Day 0 Welcome",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Day 0 Welcome": {
      "main": [
        [
          {
            "node": "Wait 7 Days",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 7 Days": {
      "main": [
        [
          {
            "node": "Day 7 FHIR Setup",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Day 7 FHIR Setup": {
      "main": [
        [
          {
            "node": "Log to Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

2. ONC / HIPAA / CMS / FHIR Compliance Deadline Tracker

Monitors 11 compliance deadline types. Routes by urgency. Notifies compliance officer, CISO, and legal before deadlines expire.

{
  "name": "HealthIT Compliance Deadline Tracker",
  "nodes": [
    {
      "id": "1",
      "name": "Daily 7AM Check",
      "type": "n8n-nodes-base.scheduleTrigger",
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 7 * * *"
            }
          ]
        }
      },
      "position": [
        100,
        200
      ]
    },
    {
      "id": "2",
      "name": "Read Deadlines",
      "type": "n8n-nodes-base.googleSheets",
      "parameters": {
        "operation": "getAll",
        "sheetId": "YOUR_COMPLIANCE_SHEET",
        "range": "Deadlines!A:F"
      },
      "position": [
        300,
        200
      ]
    },
    {
      "id": "3",
      "name": "Classify Urgency",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "const deadlineTypes = {\n  ONC_INFORMATION_BLOCKING_COMPLAINT: { reg: 'ONC 45 CFR \u00a7170.404', window: 'Civil money penalty $1M/day per violation', authority: 'ONC / HHS OIG' },\n  HIPAA_BREACH_INDIVIDUAL_NOTIFICATION: { reg: 'HIPAA 45 CFR \u00a7164.404', window: '60 calendar days from discovery', authority: 'HHS OCR' },\n  HIPAA_BREACH_HHS_NOTIFICATION_LARGE: { reg: 'HIPAA 45 CFR \u00a7164.408', window: '60 days (500+ affected: contemporaneous)', authority: 'HHS OCR Breach Portal' },\n  HIPAA_PATIENT_ACCESS_REQUEST: { reg: 'HIPAA 45 CFR \u00a7164.524', window: '30 days (one 30-day extension)', authority: 'HHS OCR' },\n  ONC_FHIR_API_COMPLIANCE: { reg: 'ONC 45 CFR \u00a7170.315(g)(10)', window: 'Continuous \u2014 API must be publicly available', authority: 'ONC / Certified HIT List' },\n  CMS_INTEROPERABILITY_AUDIT: { reg: 'CMS-9123-F / 42 CFR \u00a7422.119', window: 'Annual plan audit', authority: 'CMS' },\n  FDA_SAMD_510K_RESPONSE: { reg: 'FDA 21 CFR \u00a7807.87', window: '90 days standard review', authority: 'FDA CDRH' },\n  TWENTY_ONE_CFR_PART11_AUDIT: { reg: '21 CFR Part 11 \u00a711.10', window: 'Annual validation review', authority: 'FDA / sponsor' },\n  SOC2_TYPE2_RENEWAL: { reg: 'AICPA SOC2 TSC', window: 'Annual', authority: 'External auditor' },\n  HIPAA_RISK_ASSESSMENT: { reg: 'HIPAA 45 CFR \u00a7164.308(a)(1)', window: 'Periodic (at minimum annually)', authority: 'HHS OCR' },\n  STATE_HEALTH_DATA_BREACH: { reg: 'State breach notification law', window: 'Varies by state (30-90 days)', authority: 'State AG' }\n};\nconst items = $input.all();\nconst today = new Date();\nreturn items.map(item => {\n  const d = item.json;\n  const deadline = new Date(d.deadline_date);\n  const days = Math.ceil((deadline - today) / 86400000);\n  let urgency = 'NOTICE';\n  if (days < 0) urgency = 'OVERDUE';\n  else if (days <= 3) urgency = 'CRITICAL';\n  else if (days <= 7) urgency = 'URGENT';\n  else if (days <= 14) urgency = 'WARNING';\n  const meta = deadlineTypes[d.deadline_type] || {};\n  return { json: { ...d, days_remaining: days, urgency, ...meta } };\n}).filter(i => i.json.urgency !== 'NOTICE');"
      },
      "position": [
        500,
        200
      ]
    },
    {
      "id": "4",
      "name": "Route by Urgency",
      "type": "n8n-nodes-base.switch",
      "parameters": {
        "dataPropertyName": "urgency",
        "rules": {
          "rules": [
            {
              "value": "OVERDUE"
            },
            {
              "value": "CRITICAL"
            },
            {
              "value": "URGENT"
            },
            {
              "value": "WARNING"
            }
          ]
        }
      },
      "position": [
        700,
        200
      ]
    },
    {
      "id": "5",
      "name": "Slack CRITICAL",
      "type": "n8n-nodes-base.slack",
      "parameters": {
        "channel": "#compliance-critical",
        "text": "={{ ':rotating_light: ' + $json.urgency + ': ' + $json.deadline_type + ' \u2014 ' + $json.days_remaining + ' days remaining. Reg: ' + ($json.reg || '') + '. Authority: ' + ($json.authority || '') + '. Owner: ' + ($json.owner || 'UNASSIGNED') }}"
      },
      "position": [
        900,
        100
      ]
    },
    {
      "id": "6",
      "name": "Gmail CISO+Compliance",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "operation": "send",
        "to": "={{ $json.compliance_email }}",
        "subject": "={{ '[' + $json.urgency + '] ' + $json.deadline_type + ' \u2014 ' + $json.days_remaining + ' days' }}",
        "message": "={{ '<p>Deadline: ' + $json.deadline_date + '</p><p>Regulation: ' + ($json.reg || 'N/A') + '</p><p>Authority: ' + ($json.authority || 'N/A') + '</p><p>Window: ' + ($json.window || 'N/A') + '</p><p>Owner: ' + ($json.owner || 'UNASSIGNED') + '</p>' }}"
      },
      "position": [
        900,
        300
      ]
    }
  ],
  "connections": {
    "Daily 7AM Check": {
      "main": [
        [
          {
            "node": "Read Deadlines",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Deadlines": {
      "main": [
        [
          {
            "node": "Classify Urgency",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Urgency": {
      "main": [
        [
          {
            "node": "Route by Urgency",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Urgency": {
      "main": [
        [
          {
            "node": "Slack CRITICAL",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Slack CRITICAL",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Gmail CISO+Compliance",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Gmail CISO+Compliance",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

3. HL7 FHIR R4 API Health Monitor

Checks 5 FHIR API endpoints every 15 minutes. Each endpoint annotated with the ONC/CMS mandate it supports. Downtime = potential information blocking event.

{
  "name": "HL7 FHIR R4 API Health Monitor",
  "nodes": [
    {
      "id": "1",
      "name": "Every 15 Min",
      "type": "n8n-nodes-base.scheduleTrigger",
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "*/15 * * * *"
            }
          ]
        }
      },
      "position": [
        100,
        200
      ]
    },
    {
      "id": "2",
      "name": "FHIR Endpoints",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "const endpoints = [\n  { name: 'patient_access_api', url: 'https://fhir.yourplatform.com/R4/Patient/$everything', standard: 'ONC 45 CFR \u00a7170.315(g)(10) \u2014 Patient Access API (Cures Act mandate)', sla_minutes: 15, blocking_risk: true },\n  { name: 'provider_directory_api', url: 'https://fhir.yourplatform.com/R4/PractitionerRole', standard: 'CMS-9123-F \u2014 Provider Directory API for MA/Medicaid plans', sla_minutes: 30, blocking_risk: true },\n  { name: 'drug_formulary_api', url: 'https://fhir.yourplatform.com/R4/MedicationKnowledge', standard: 'CMS-9123-F \u2014 Drug Formulary API', sla_minutes: 30, blocking_risk: true },\n  { name: 'bulk_data_export_api', url: 'https://fhir.yourplatform.com/R4/$export', standard: 'ONC \u00a7170.315(g)(10) \u2014 Bulk FHIR export for population-level data access', sla_minutes: 60, blocking_risk: true },\n  { name: 'smart_on_fhir_auth', url: 'https://fhir.yourplatform.com/auth/token', standard: 'SMART on FHIR \u2014 OAuth 2.0 authorization for third-party app access', sla_minutes: 5, blocking_risk: true }\n];\nreturn endpoints.map(e => ({ json: e }));"
      },
      "position": [
        300,
        200
      ]
    },
    {
      "id": "3",
      "name": "HTTP Check Each",
      "type": "n8n-nodes-base.httpRequest",
      "parameters": {
        "url": "={{ $json.url }}",
        "method": "GET",
        "timeout": 10000,
        "continueOnFail": true
      },
      "position": [
        500,
        200
      ]
    },
    {
      "id": "4",
      "name": "Flag Failures",
      "type": "n8n-nodes-base.if",
      "parameters": {
        "conditions": {
          "conditions": [
            {
              "value1": "={{ $json.statusCode }}",
              "operation": "notEqual",
              "value2": 200
            }
          ]
        }
      },
      "position": [
        700,
        200
      ]
    },
    {
      "id": "5",
      "name": "Slack #fhir-incidents",
      "type": "n8n-nodes-base.slack",
      "parameters": {
        "channel": "#fhir-incidents",
        "text": "={{ ':red_circle: FHIR API DOWN: ' + $json.name + ' | Mandate: ' + $json.standard + ' | Status: ' + ($json.statusCode || 'TIMEOUT') + ' | Blocking Risk: ' + $json.blocking_risk + ' | Time: ' + $now }}"
      },
      "position": [
        900,
        100
      ]
    },
    {
      "id": "6",
      "name": "Log SLA Breach",
      "type": "n8n-nodes-base.googleSheets",
      "parameters": {
        "operation": "append",
        "sheetId": "YOUR_FHIR_SLA_LOG",
        "range": "FHIRIncidents!A:G",
        "values": [
          [
            "={{ $json.name }}",
            "={{ $json.standard }}",
            "={{ $json.statusCode || 'TIMEOUT' }}",
            "={{ $now }}",
            "DOWN",
            "={{ $json.sla_minutes }}",
            "={{ $json.blocking_risk }}"
          ]
        ]
      },
      "position": [
        900,
        300
      ]
    }
  ],
  "connections": {
    "Every 15 Min": {
      "main": [
        [
          {
            "node": "FHIR Endpoints",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "FHIR Endpoints": {
      "main": [
        [
          {
            "node": "HTTP Check Each",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Check Each": {
      "main": [
        [
          {
            "node": "Flag Failures",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Flag Failures": {
      "main": [
        [
          {
            "node": "Slack #fhir-incidents",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log SLA Breach",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

4. HealthIT Compliance Incident Pipeline

Receives compliance incidents via webhook. Classifies by type and fastest clock. Routes HIPAA breaches and ONC information blocking complaints to immediate notification channels.

{
  "name": "HealthIT Compliance Incident Pipeline",
  "nodes": [
    {
      "id": "1",
      "name": "Incident Webhook",
      "type": "n8n-nodes-base.webhook",
      "parameters": {
        "path": "healthit-incident",
        "method": "POST",
        "responseMode": "onReceived"
      },
      "position": [
        100,
        200
      ]
    },
    {
      "id": "2",
      "name": "Classify Incident",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "const types = {\n  INFORMATION_BLOCKING_COMPLAINT: { clock: 'CIVIL MONEY PENALTY $1M/DAY', authority: 'ONC / HHS OIG', reg: 'ONC 45 CFR \u00a7170.404', severity: 'P0', action: 'IMMEDIATE: Notify General Counsel + Compliance Officer. Preserve all records of the alleged interference. ONC complaint investigation can result in $1M/day civil money penalty per violation. Document factual basis for any applicable exception (45 CFR \u00a7171.200-\u00a7171.302).' },\n  HIPAA_BREACH_LARGE: { clock: '60 DAYS \u2014 MEDIA + HHS CONTEMPORANEOUS', authority: 'HHS OCR', reg: 'HIPAA 45 CFR \u00a7164.404/408', severity: 'P0', action: 'IMMEDIATE: Brief CISO + GC. 60-day individual notification window starts at discovery. 500+ residents of a state: media notification also required within 60 days. HHS Breach Portal notification: within 60 days. Engage breach response counsel.' },\n  HIPAA_BREACH_SMALL: { clock: '60 DAYS', authority: 'HHS OCR', reg: 'HIPAA 45 CFR \u00a7164.404', severity: 'P1', action: 'Notify affected individuals within 60 days. Log to annual breach report for HHS. Conduct root cause analysis. Update risk assessment.' },\n  FHIR_API_EXTENDED_OUTAGE: { clock: 'IMMEDIATE \u2014 potential \u00a7170.404 event', authority: 'ONC', reg: 'ONC 45 CFR \u00a7170.315(g)(10)', severity: 'P0', action: 'IMMEDIATE: FHIR Patient Access API downtime beyond SLA is a potential ONC information blocking event. Notify engineering + compliance. Document outage duration and cause. Extended outage triggers \u00a7170.404 review.' },\n  TWENTY_ONE_CFR_PART11_AUDIT_TRAIL_GAP: { clock: 'IMMEDIATE \u2014 FDA inspection risk', authority: 'FDA', reg: '21 CFR Part 11 \u00a711.10(e)', severity: 'P1', action: 'Audit trail gap in validated system: notify QA + compliance immediately. 21 CFR Part 11 requires continuous, computer-generated audit trail. Gap may constitute GMP deviation requiring CAPA. Document gap scope and remediation plan.' },\n  HIPAA_PATIENT_ACCESS_OVERDUE: { clock: 'OVERDUE \u2014 30-day window expired', authority: 'HHS OCR', reg: 'HIPAA 45 CFR \u00a7164.524', severity: 'P1', action: 'Patient access request deadline exceeded. 30-day window (one 30-day extension permitted). Fulfill request immediately. Document delay cause. HHS OCR complaints on patient access rights are increasing.' },\n  CMS_INTEROPERABILITY_AUDIT_TRIGGERED: { clock: 'PER CMS NOTIFICATION', authority: 'CMS', reg: 'CMS-9123-F 42 CFR \u00a7422.119', severity: 'P1', action: 'CMS audit triggered for interoperability rule compliance. Gather FHIR API availability reports, patient access logs, provider directory data currency records, drug formulary update frequency logs. Notify compliance + legal.' },\n  STATE_HEALTH_DATA_BREACH: { clock: 'VARIES BY STATE (30-90 DAYS)', authority: 'State Attorney General', reg: 'State health data breach law', severity: 'P1', action: 'State-specific breach notification law applies in addition to HIPAA. California CMIA: 15 days. Many states: 30-60 days. Notify state AG per applicable law. Some states: individual + regulator notification.' }\n};\nconst t = $json.incident_type;\nconst meta = types[t] || { clock: 'PER POLICY', authority: 'Compliance Officer', reg: 'Internal policy', severity: 'P3', action: 'Review and classify incident.' };\nreturn [{ json: { ...$json, ...meta, received_at: new Date().toISOString() } }];"
      },
      "position": [
        300,
        200
      ]
    },
    {
      "id": "3",
      "name": "P0 Immediate?",
      "type": "n8n-nodes-base.if",
      "parameters": {
        "conditions": {
          "conditions": [
            {
              "value1": "={{ $json.severity }}",
              "operation": "equal",
              "value2": "P0"
            }
          ]
        }
      },
      "position": [
        500,
        200
      ]
    },
    {
      "id": "4",
      "name": "Slack #incidents-p0",
      "type": "n8n-nodes-base.slack",
      "parameters": {
        "channel": "#incidents-p0",
        "text": "={{ ':sos: P0 HEALTHIT INCIDENT: ' + $json.incident_type + '\\nClock: ' + $json.clock + '\\nAuthority: ' + $json.authority + '\\nAction: ' + $json.action + '\\nReceived: ' + $json.received_at }}"
      },
      "position": [
        700,
        100
      ]
    },
    {
      "id": "5",
      "name": "Gmail CISO + GC",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "operation": "send",
        "to": "ciso@yourplatform.com",
        "bcc": "legal@yourplatform.com",
        "subject": "={{ '[P0 HEALTHIT INCIDENT] ' + $json.incident_type + ' \u2014 ' + $json.clock }}",
        "message": "={{ '<h2>P0 Incident: ' + $json.incident_type + '</h2><p><strong>Compliance Clock:</strong> ' + $json.clock + '</p><p><strong>Regulation:</strong> ' + $json.reg + '</p><p><strong>Notification Authority:</strong> ' + $json.authority + '</p><p><strong>Required Action:</strong> ' + $json.action + '</p><p><strong>Received At:</strong> ' + $json.received_at + '</p>' }}"
      },
      "position": [
        700,
        300
      ]
    },
    {
      "id": "6",
      "name": "Log All Incidents",
      "type": "n8n-nodes-base.googleSheets",
      "parameters": {
        "operation": "append",
        "sheetId": "YOUR_INCIDENT_LOG",
        "range": "Incidents!A:G",
        "values": [
          [
            "={{ $json.incident_type }}",
            "={{ $json.severity }}",
            "={{ $json.clock }}",
            "={{ $json.authority }}",
            "={{ $json.reg }}",
            "={{ $json.received_at }}",
            "OPEN"
          ]
        ]
      },
      "position": [
        900,
        200
      ]
    }
  ],
  "connections": {
    "Incident Webhook": {
      "main": [
        [
          {
            "node": "Classify Incident",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Incident": {
      "main": [
        [
          {
            "node": "P0 Immediate?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "P0 Immediate?": {
      "main": [
        [
          {
            "node": "Slack #incidents-p0",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Gmail CISO + GC",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail CISO + GC": {
      "main": [
        [
          {
            "node": "Log All Incidents",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

5. Weekly HealthIT SaaS KPI Briefing

Monday 8 AM. Pulls customer metrics, open compliance items, and FHIR API availability stats from Postgres. Sends HTML executive summary.

{
  "name": "HealthIT Weekly KPI Briefing",
  "nodes": [
    {
      "id": "1",
      "name": "Monday 8AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1"
            }
          ]
        }
      },
      "position": [
        100,
        200
      ]
    },
    {
      "id": "2",
      "name": "Query KPIs",
      "type": "n8n-nodes-base.postgres",
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT\n  COUNT(*) FILTER (WHERE tier='EHR_PLATFORM_VENDOR') AS ehr_accounts,\n  COUNT(*) FILTER (WHERE tier='HEALTH_INFORMATION_EXCHANGE') AS hie_accounts,\n  COUNT(*) FILTER (WHERE tier='PATIENT_PORTAL_SAAS') AS patient_portal_accounts,\n  COUNT(*) FILTER (WHERE tier='TELEHEALTH_PLATFORM') AS telehealth_accounts,\n  COUNT(*) FILTER (WHERE tier='DIGITAL_THERAPEUTICS_SAAS') AS dtx_accounts,\n  COUNT(*) FILTER (WHERE tier='HEALTHIT_STARTUP') AS startup_accounts,\n  SUM(mrr_usd) AS total_mrr,\n  COUNT(*) FILTER (WHERE hipaa_baa_signed=true) AS baa_signed,\n  COUNT(*) FILTER (WHERE fhir_r4_certified=true) AS fhir_certified,\n  COUNT(*) FILTER (WHERE open_hipaa_incidents > 0) AS accounts_hipaa_open,\n  COUNT(*) FILTER (WHERE open_onc_complaints > 0) AS accounts_onc_open,\n  AVG(fhir_api_uptime_7d) AS avg_fhir_uptime\nFROM healthit_accounts WHERE status='active'"
      },
      "position": [
        300,
        200
      ]
    },
    {
      "id": "3",
      "name": "Build HTML Report",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "const d = $json;\nconst html = `<h2>HealthIT Weekly KPI \u2014 ${new Date().toDateString()}</h2>\n<table border='1' cellpadding='6' style='border-collapse:collapse'>\n<tr><th>Metric</th><th>Value</th></tr>\n<tr><td>EHR Platform Accounts</td><td>${d.ehr_accounts}</td></tr>\n<tr><td>HIE Accounts</td><td>${d.hie_accounts}</td></tr>\n<tr><td>Patient Portal Accounts</td><td>${d.patient_portal_accounts}</td></tr>\n<tr><td>Telehealth Accounts</td><td>${d.telehealth_accounts}</td></tr>\n<tr><td>Digital Therapeutics Accounts</td><td>${d.dtx_accounts}</td></tr>\n<tr><td>Startup Accounts</td><td>${d.startup_accounts}</td></tr>\n<tr><td>Total MRR</td><td>$${Number(d.total_mrr||0).toLocaleString()}</td></tr>\n<tr><td>HIPAA BAA Signed</td><td>${d.baa_signed}</td></tr>\n<tr><td>FHIR R4 Certified</td><td>${d.fhir_certified}</td></tr>\n<tr><td>Avg FHIR API Uptime (7d)</td><td>${Number(d.avg_fhir_uptime||0).toFixed(2)}%</td></tr>\n<tr><td bgcolor='#ffe6e6'><strong>Accounts w/ Open HIPAA Incidents</strong></td><td><strong>${d.accounts_hipaa_open}</strong></td></tr>\n<tr><td bgcolor='#ffe6e6'><strong>Accounts w/ Open ONC Complaints</strong></td><td><strong>${d.accounts_onc_open}</strong></td></tr>\n</table>`;\nreturn [{ json: { ...d, html_report: html } }];"
      },
      "position": [
        500,
        200
      ]
    },
    {
      "id": "4",
      "name": "Gmail CEO + Compliance",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "operation": "send",
        "to": "ceo@yourplatform.com",
        "bcc": "compliance@yourplatform.com",
        "subject": "HealthIT Weekly KPI \u2014 {{ $now.format('MMM DD, YYYY') }}",
        "message": "={{ $json.html_report }}"
      },
      "position": [
        700,
        200
      ]
    },
    {
      "id": "5",
      "name": "Slack #go-to-market",
      "type": "n8n-nodes-base.slack",
      "parameters": {
        "channel": "#go-to-market",
        "text": "={{ 'Weekly HealthIT KPI: ' + $json.ehr_accounts + ' EHR / ' + $json.hie_accounts + ' HIE / ' + $json.telehealth_accounts + ' telehealth / MRR $' + Number($json.total_mrr||0).toLocaleString() + ' | HIPAA open: ' + $json.accounts_hipaa_open + ' | ONC complaints: ' + $json.accounts_onc_open + ' | FHIR uptime: ' + Number($json.avg_fhir_uptime||0).toFixed(2) + '%' }}"
      },
      "position": [
        900,
        200
      ]
    }
  ],
  "connections": {
    "Monday 8AM": {
      "main": [
        [
          {
            "node": "Query KPIs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Query KPIs": {
      "main": [
        [
          {
            "node": "Build HTML Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build HTML Report": {
      "main": [
        [
          {
            "node": "Gmail CEO + Compliance",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail CEO + Compliance": {
      "main": [
        [
          {
            "node": "Slack #go-to-market",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The BAA Chain Argument

HIPAA §164.314 requires a business associate agreement with every entity that creates, receives, maintains, or transmits PHI on your behalf. That includes your automation platform.

A SaaS iPaaS that processes PHI is a business associate. It has its own breach notification obligation under HIPAA. When it has an outage, delays your PHI processing, or retains logs you cannot audit — it is in your BAA chain and your patients' rights are implicated.

Self-hosted n8n is infrastructure, not a service. It does not create a business associate relationship with a third-party vendor. The workflow engine is under your control, your audit trail, and your BAA with the cloud provider you already use.

For EHR vendors specifically: the ONC §170.404 information blocking analysis applies to any practice that interferes with EHI access. A cloud iPaaS dependency that causes delays or limits auditability is exactly the kind of architecture ONC reviewers will scrutinize.

Get These Workflows

All 5 workflows above are included in the FlowKit n8n template library — import-ready JSON at stripeai.gumroad.com.

Individual templates from $12. Bundle: $97.

If you're building HealthIT or EHR SaaS and want to discuss compliance automation architecture — reach out at openstripeai@gmail.com.

Top comments (0)