MedTech and digital health SaaS vendors face a uniquely dense regulatory stack. If your software touches medical device operations, patient data, or consumer health metrics, you're simultaneously subject to multiple frameworks — each with distinct notification clocks and reporting chains.
The fastest clock in this space: the FDA 5-day expedited MDR (21 CFR §803.53) — applies when a device malfunction would likely cause or contribute to death or serious injury if it recurred. Miss it and you risk a Warning Letter that cascades into 510(k) clearance delays for your next product line.
Here are five self-hosted n8n workflows covering the full MedTech compliance stack.
Customer Tiers
| Tier Constant | Segment |
|---|---|
ENTERPRISE_MEDICAL_DEVICE_MANUFACTURER |
Large Class II/III MDMs (device + software combined) |
MID_MARKET_MEDICAL_DEVICE_SAAS |
Mid-size SaMD companies (Class I/II software-only) |
DIGITAL_HEALTH_PLATFORM |
Consumer health apps (wearables, mental health, fitness) |
CLINICAL_DECISION_SUPPORT_SAAS |
CDS software vendors (AUC tools, CPOE order-entry) |
TELEHEALTH_SAAS_VENDOR |
Telemedicine platform operators |
REMOTE_PATIENT_MONITORING_VENDOR |
RPM device + software vendors |
HEALTH_DATA_ANALYTICS_SAAS |
Population health analytics, de-identified data platforms |
Compliance flags injected at onboarding:
-
FDA_510K_CLEARED— 510(k)-cleared SaMD under 21 CFR §807.92 -
FDA_PMA_REQUIRED— Class III device requiring Premarket Approval -
HIPAA_BAA_REQUIRED— Handles PHI as a Business Associate (45 CFR §164.308) -
FTC_HEALTH_BREACH_NOTIFIABLE— Non-HIPAA consumer health app (16 CFR Part 318) -
MDR_EU_APPLICABLE— EU sales subject to EU MDR 2017/745 -
FDA_SAMD_GUIDANCE— Software as a Medical Device (IMDRF N23/N46/N56) -
CURES_ACT_COMPLIANCE— 21st Century Cures Act Information Blocking (45 CFR §171)
Workflow 1: MedTech SaaS Customer Onboarding Drip
Runs Monday at 8 AM. Pulls customers in pending, day3, and day7 onboarding stages. The Code node builds tier-specific emails: DIGITAL_HEALTH_PLATFORM customers get FTC Health Breach Rule notices on Day 0; HIPAA_BAA_REQUIRED customers get a BAA signature link; FDA_510K_CLEARED customers receive MDR clock activation instructions. CURES_ACT_COMPLIANCE customers get a §171.103 OIG $1M/violation perimeter note on Day 3.
{
"name": "MedTech SaaS Customer Onboarding Drip",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 8,
"triggerAtMinute": 0,
"weekdays": [
"monday"
]
}
]
}
},
"id": "t1",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
250,
300
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT id, company_name, customer_tier, primary_email, compliance_flags, onboarding_step FROM medtech_customers WHERE onboarding_step IN ('pending','day3','day7') AND created_at <= NOW() - (CASE onboarding_step WHEN 'pending' THEN INTERVAL '0 days' WHEN 'day3' THEN INTERVAL '3 days' WHEN 'day7' THEN INTERVAL '7 days' END)"
},
"id": "db1",
"name": "Get Onboarding Customers",
"type": "n8n-nodes-base.postgres",
"position": [
450,
300
]
},
{
"parameters": {
"jsCode": "const tier = $json.customer_tier;\nconst flags = ($json.compliance_flags || '').split(',');\nconst step = $json.onboarding_step;\nlet subject, body;\nif (step === 'pending') {\n subject = `Welcome to FlowKit \u2014 Your MedTech Automation Starter Kit`;\n body = `Hi ${$json.company_name} team,\\n\\nWelcome! Your compliance automation templates are active.\\n\\n`;\n if (flags.includes('HIPAA_BAA_REQUIRED')) body += `ACTION REQUIRED: Sign your BAA before activating PHI workflows: [Sign BAA]\\n\\n`;\n if (flags.includes('FDA_510K_CLEARED')) body += `Your FDA-cleared device workflows include MDR malfunction tracking (21 CFR \\u00a7803.50, 30-day clock).\\n\\n`;\n if (flags.includes('FTC_HEALTH_BREACH_NOTIFIABLE')) body += `FTC Health Breach Notification Rule (16 CFR Part 318) timeline automation is pre-configured.\\n\\n`;\n if (flags.includes('MDR_EU_APPLICABLE')) body += `EU MDR 2017/745 Art.87 serious incident 15-day clock is active.\\n\\n`;\n body += `Start here: [QuickStart Guide] | [Schedule Onboarding Call]\\n\\nAlex Kane, FlowKit`;\n} else if (step === 'day3') {\n subject = `Day 3: Connect Your EHR/EMR Integration (${$json.company_name})`;\n body = `Hi team,\\n\\nDay 3 \u2014 ready to connect your EHR or device data pipeline?\\n\\n`;\n if (flags.includes('FDA_510K_CLEARED') || flags.includes('FDA_PMA_REQUIRED')) body += `SaMD workflows log to Postgres for FDA 21 CFR \\u00a711 audit trail (tamper-evident, \\u00a711.10(e)).\\n\\n`;\n if (flags.includes('MDR_EU_APPLICABLE')) body += `EU MDR Art.10(9) PMCF workflow is pre-configured for post-market surveillance.\\n\\n`;\n if (flags.includes('CURES_ACT_COMPLIANCE')) body += `21st Century Cures Act information blocking workflows active \u2014 \\u00a7171.103 OIG $1M/violation perimeter.\\n\\n`;\n body += `Questions? Reply here or [Book 30 min] \u2014 Alex`;\n} else {\n subject = `Day 7: Your Regulatory Deadline Tracker Is Active`;\n body = `Hi team,\\n\\nWeek 1 complete. Active regulatory clocks:\\n\\u2022 FDA MDR malfunction: 30-day (21 CFR \\u00a7803.50)\\n\\u2022 FDA MDR death expedited: 5-day (\\u00a7803.53)\\n\\u2022 HIPAA breach: 60-day (45 CFR \\u00a7164.404)\\n\\u2022 EU MDR serious incident: 15-day (Art.87)\\n\\u2022 FTC Health Breach: 60-day (16 CFR \\u00a7318.5)\\n\\nCompliance dashboard: [Open Dashboard]\\n\\nAlex Kane, FlowKit`;\n}\nreturn [{ json: { ...$json, email_subject: subject, email_body: body } }];"
},
"id": "c1",
"name": "Build Tier Email",
"type": "n8n-nodes-base.code",
"position": [
650,
300
]
},
{
"parameters": {
"sendTo": "={{ $json.primary_email }}",
"subject": "={{ $json.email_subject }}",
"message": "={{ $json.email_body }}",
"options": {}
},
"id": "e1",
"name": "Send Email",
"type": "n8n-nodes-base.gmail",
"position": [
850,
300
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "UPDATE medtech_customers SET onboarding_step = CASE onboarding_step WHEN 'pending' THEN 'day3' WHEN 'day3' THEN 'day7' WHEN 'day7' THEN 'complete' END, updated_at = NOW() WHERE id = {{ $json.id }}"
},
"id": "db2",
"name": "Advance Step",
"type": "n8n-nodes-base.postgres",
"position": [
1050,
300
]
}
],
"connections": {
"Schedule Trigger": {
"main": [
[
{
"node": "Get Onboarding Customers",
"type": "main",
"index": 0
}
]
]
},
"Get Onboarding Customers": {
"main": [
[
{
"node": "Build Tier Email",
"type": "main",
"index": 0
}
]
]
},
"Build Tier Email": {
"main": [
[
{
"node": "Send Email",
"type": "main",
"index": 0
}
]
]
},
"Send Email": {
"main": [
[
{
"node": "Advance Step",
"type": "main",
"index": 0
}
]
]
}
}
}
Workflow 2: FDA MDR / HIPAA / EU MDR / FTC Regulatory Deadline Tracker
Runs daily at 7:30 AM. Tracks 12 deadline types:
| Deadline Type | Clock | Regulation |
|---|---|---|
FDA_MDR_DEATH_EXPEDITED_5DAY |
5 days | 21 CFR §803.53 — fastest clock in MedTech |
FDA_MDR_MALFUNCTION_REPORT_30DAY |
30 days | 21 CFR §803.50 |
FDA_MDR_SERIOUS_INJURY_30DAY |
30 days | 21 CFR §803.50 |
FDA_510K_DEFICIENCY_RESPONSE_90DAY |
90 days | 21 CFR §807.87(k) |
FDA_UDI_GUDID_SUBMISSION |
Before distribution | 21 CFR §830.300 |
HIPAA_BREACH_NOTIFICATION_60DAY |
60 days | 45 CFR §164.404 |
FTC_HEALTH_BREACH_NOTIFICATION_60DAY |
60 days + media | 16 CFR §318.5 |
EU_MDR_SERIOUS_INCIDENT_15DAY |
15 days | EU MDR Art.87 |
EU_MDR_PMSR_ANNUAL |
Annual | EU MDR Art.85/86 |
HIPAA_RISK_ASSESSMENT_ANNUAL |
Annual | 45 CFR §164.308(a)(1) |
ISO_13485_SURVEILLANCE_AUDIT |
Per cert schedule | ISO 13485:2016 |
SOC2_TYPE2_RENEWAL |
Annual | Customer procurement |
The FDA_MDR_DEATH_EXPEDITED_5DAY urgency threshold triggers a CRITICAL Slack alert at day 0 of creation, escalating to VP Regulatory at 3 days remaining — because this clock starts running the moment you become aware, not when you report.
{
"name": "FDA MDR / HIPAA / EU MDR Regulatory Deadline Tracker",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 7,
"triggerAtMinute": 30
}
]
}
},
"id": "t2",
"name": "Daily 7:30 AM",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
250,
300
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT id, deadline_type, deadline_date, owner_email, customer_id, notes FROM medtech_regulatory_deadlines WHERE status = 'open' AND deadline_date <= NOW() + INTERVAL '120 days' ORDER BY deadline_date ASC"
},
"id": "db3",
"name": "Get Deadlines",
"type": "n8n-nodes-base.postgres",
"position": [
450,
300
]
},
{
"parameters": {
"jsCode": "const d = new Date($json.deadline_date);\nconst now = new Date();\nconst days = Math.ceil((d - now) / 86400000);\nconst type = $json.deadline_type;\nconst regulatoryNote = {\n 'FDA_MDR_DEATH_EXPEDITED_5DAY': '21 CFR \u00a7803.53 \u2014 5-day expedited MDR. FASTEST CLOCK IN MEDTECH.',\n 'FDA_MDR_MALFUNCTION_REPORT_30DAY': '21 CFR \u00a7803.50 \u2014 30-day malfunction MDR. Device failure report to FDA.',\n 'FDA_MDR_SERIOUS_INJURY_30DAY': '21 CFR \u00a7803.50 \u2014 30-day serious injury MDR.',\n 'FDA_510K_DEFICIENCY_RESPONSE_90DAY': '21 CFR \u00a7807.87(k) \u2014 90-day 510(k) deficiency response. No extension without HSR.',\n 'FDA_UDI_GUDID_SUBMISSION': '21 CFR \u00a7830.300 \u2014 GUDID submission required before commercial distribution.',\n 'HIPAA_BREACH_NOTIFICATION_60DAY': '45 CFR \u00a7164.404 \u2014 60-day breach notification to HHS OCR + affected individuals.',\n 'FTC_HEALTH_BREACH_NOTIFICATION_60DAY': '16 CFR \u00a7318.5 \u2014 FTC Health Breach Notification. Media notice if >500 state residents.',\n 'EU_MDR_SERIOUS_INCIDENT_15DAY': 'EU MDR Art.87 \u2014 15-day serious incident report to competent authority.',\n 'EU_MDR_PMSR_ANNUAL': 'EU MDR Art.85 \u2014 Annual PMSR (Class IIa). Art.86 PSUR for Class IIb/III.',\n 'HIPAA_RISK_ASSESSMENT_ANNUAL': '45 CFR \u00a7164.308(a)(1) \u2014 Annual security risk assessment. OCR audit item #1.',\n 'ISO_13485_SURVEILLANCE_AUDIT': 'ISO 13485:2016 \u2014 Surveillance audit by certification body.',\n 'SOC2_TYPE2_RENEWAL': 'SOC 2 Type II \u2014 Annual audit. Customer procurement requirement.'\n}[type] || type;\nlet urgency;\nif (days < 0) urgency = 'OVERDUE';\nelse if (days <= 5) urgency = 'CRITICAL';\nelse if (days <= 14) urgency = 'URGENT';\nelse if (days <= 30) urgency = 'WARNING';\nelse if (days <= 60) urgency = 'NOTICE';\nelse urgency = 'UPCOMING';\nreturn [{ json: { ...$json, days_remaining: days, urgency, regulatory_note: regulatoryNote } }];"
},
"id": "c2",
"name": "Classify Urgency",
"type": "n8n-nodes-base.code",
"position": [
650,
300
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true
},
"conditions": [
{
"leftValue": "={{ $json.urgency }}",
"rightValue": "UPCOMING",
"operator": {
"type": "string",
"operation": "notEquals"
}
}
]
}
},
"id": "if1",
"name": "Skip UPCOMING",
"type": "n8n-nodes-base.if",
"position": [
850,
300
]
},
{
"parameters": {
"resource": "message",
"operation": "post",
"channel": "#regulatory-compliance",
"text": "={{ $json.urgency }}: {{ $json.deadline_type }} | {{ $json.days_remaining }} days | {{ $json.regulatory_note }}"
},
"id": "sl1",
"name": "Slack Alert",
"type": "n8n-nodes-base.slack",
"position": [
1050,
300
]
},
{
"parameters": {
"sendTo": "={{ $json.owner_email }}",
"subject": "={{ $json.urgency }}: {{ $json.deadline_type }} due in {{ $json.days_remaining }} days",
"message": "={{ $json.deadline_type }} deadline approaching.\\n\\nDue: {{ $json.deadline_date }}\\nDays remaining: {{ $json.days_remaining }}\\n\\nRegulatory note: {{ $json.regulatory_note }}\\n\\nNotes: {{ $json.notes }}",
"options": {}
},
"id": "e2",
"name": "Email Owner",
"type": "n8n-nodes-base.gmail",
"position": [
1050,
450
]
}
],
"connections": {
"Daily 7:30 AM": {
"main": [
[
{
"node": "Get Deadlines",
"type": "main",
"index": 0
}
]
]
},
"Get Deadlines": {
"main": [
[
{
"node": "Classify Urgency",
"type": "main",
"index": 0
}
]
]
},
"Classify Urgency": {
"main": [
[
{
"node": "Skip UPCOMING",
"type": "main",
"index": 0
}
]
]
},
"Skip UPCOMING": {
"main": [
[
{
"node": "Slack Alert",
"type": "main",
"index": 0
}
],
[
{
"node": "Email Owner",
"type": "main",
"index": 0
}
]
]
}
}
}
Workflow 3: MedTech API & Integration Health Monitor
Polls 5 integration endpoints every 3 minutes. Each endpoint is annotated with the specific regulatory risk its downtime creates:
-
fda_device_api— FDA 510(k)/GUDID database sync. Downtime = MDR malfunction reporting gap (21 CFR §803) -
ehr_integration_api— EHR/EMR integration. Downtime = HIPAA §164.312(a)(2)(iv) encrypted PHI transmission gap -
device_telemetry_api— Device data ingestion. Downtime = SaMD IMDRF N23 §4.1.1 clinical evaluation data integrity risk -
payment_processing_api— Patient billing. PCI DSS 4.0 Req 6.4.3 — note warns never to cache card data in n8n variables -
ftc_health_data_api— Consumer health metrics. FTC Health Breach Rule 16 CFR Part 318 scope annotation
{
"name": "MedTech API & Integration Health Monitor",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 3
}
]
}
},
"id": "t3",
"name": "Every 3 Minutes",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
250,
300
]
},
{
"parameters": {
"jsCode": "return [\n { json: { endpoint: 'fda_device_api', url: 'https://api.fda.gov/device/510k.json?limit=1', note: '21 CFR \u00a7803 MDR clock data feed \u2014 outage = 30-day malfunction reporting gap' }},\n { json: { endpoint: 'ehr_integration_api', url: '{{ $vars.EHR_HEALTH_ENDPOINT }}', note: '45 CFR \u00a7164.312(a)(2)(iv) encrypted PHI transmission \u2014 BAA subprocessor chain at risk' }},\n { json: { endpoint: 'device_telemetry_api', url: '{{ $vars.DEVICE_TELEMETRY_ENDPOINT }}', note: 'SaMD IMDRF N23 \u00a74.1.1 clinical evaluation data integrity \u2014 MDR serious injury risk' }},\n { json: { endpoint: 'payment_processing_api', url: '{{ $vars.PAYMENT_HEALTH_ENDPOINT }}', note: 'PCI DSS 4.0 Req 6.4.3 \u2014 patient billing data. Never cache card data in n8n variables' }},\n { json: { endpoint: 'ftc_health_data_api', url: '{{ $vars.HEALTH_DATA_ENDPOINT }}', note: 'FTC Health Breach Notification Rule 16 CFR Part 318 \u2014 consumer health metrics endpoint' }}\n];"
},
"id": "c3",
"name": "Define Endpoints",
"type": "n8n-nodes-base.code",
"position": [
450,
300
]
},
{
"parameters": {
"url": "={{ $json.url }}",
"options": {
"timeout": 5000,
"response": {
"response": {
"responseFormat": "text"
}
}
}
},
"id": "h1",
"name": "HTTP Ping",
"type": "n8n-nodes-base.httpRequest",
"position": [
650,
300
],
"continueOnFail": true
},
{
"parameters": {
"jsCode": "const ok = !$json.error && $input.first().json.statusCode < 400;\nif (ok) return [];\nreturn [{ json: { ...$json, status: 'DOWN', alert: `MEDTECH API ALERT: ${$json.endpoint} DOWN | ${$json.note}` } }];"
},
"id": "c4",
"name": "Check Status",
"type": "n8n-nodes-base.code",
"position": [
850,
300
]
},
{
"parameters": {
"resource": "message",
"operation": "post",
"channel": "#platform-ops",
"text": "={{ $json.alert }}"
},
"id": "sl2",
"name": "Slack Alert",
"type": "n8n-nodes-base.slack",
"position": [
1050,
300
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "INSERT INTO api_incident_log (endpoint, status, note, ts) VALUES ('{{ $json.endpoint }}', 'DOWN', '{{ $json.note }}', NOW())"
},
"id": "db4",
"name": "Log Incident",
"type": "n8n-nodes-base.postgres",
"position": [
1050,
450
]
}
],
"connections": {
"Every 3 Minutes": {
"main": [
[
{
"node": "Define Endpoints",
"type": "main",
"index": 0
}
]
]
},
"Define Endpoints": {
"main": [
[
{
"node": "HTTP Ping",
"type": "main",
"index": 0
}
]
]
},
"HTTP Ping": {
"main": [
[
{
"node": "Check Status",
"type": "main",
"index": 0
}
]
]
},
"Check Status": {
"main": [
[
{
"node": "Slack Alert",
"type": "main",
"index": 0
}
],
[
{
"node": "Log Incident",
"type": "main",
"index": 0
}
]
]
}
}
}
Workflow 4: FDA MDR + FTC Health Breach Incident Response Pipeline
Webhook endpoint receives incident events. The Code node classifies into 8 types:
| Incident Type | Clock | Regulatory Action |
|---|---|---|
FDA_MDR_DEATH_EXPEDITED |
5 days (§803.53) | #regulatory-urgent + VP Regulatory + FDA MedWatch |
FDA_MDR_MALFUNCTION_30DAY |
30 days (§803.50) | #regulatory-ops + Reg Affairs lead |
FDA_MDR_SERIOUS_INJURY_30DAY |
30 days (§803.50) | #regulatory-ops + VP Clinical Affairs |
HIPAA_PHI_BREACH |
60 days (45 CFR §164.404) | #privacy-incident + Privacy Officer + HHS OCR |
FTC_HEALTH_BREACH |
60 days (16 CFR §318.5) | #privacy-incident + General Counsel + FTC Web Report |
EU_MDR_SERIOUS_INCIDENT |
15 days (EU MDR Art.87) | #eu-regulatory + EU QP + Notified Body |
CURES_ACT_INFO_BLOCKING |
Immediate | #legal-urgent + GC + CEO — $1M/violation OIG |
FDA_SAMD_SOFTWARE_DEFECT |
10 biz days CAPA | #qa-urgent + QA Director + DHF update |
The CURES_ACT_INFO_BLOCKING branch includes an annotation: if your automation throttles data access or blocks interoperability requests, this incident type fires. OIG penalty is $1M per violation under 45 CFR §171.103.
{
"name": "FDA MDR + FTC Health Breach Incident Response Pipeline",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "medtech-incident",
"responseMode": "responseNode"
},
"id": "wh1",
"name": "Incident Webhook",
"type": "n8n-nodes-base.webhook",
"position": [
250,
300
]
},
{
"parameters": {
"jsCode": "const type = $json.incident_type;\nconst clocks = {\n 'FDA_MDR_DEATH_EXPEDITED': { days: 5, reg: '21 CFR \u00a7803.53 \u2014 EXPEDITED 5-DAY MDR. Device implant death or previously-reported failure.', channel: '#regulatory-urgent', escalate: 'VP Regulatory Affairs' },\n 'FDA_MDR_MALFUNCTION_30DAY': { days: 30, reg: '21 CFR \u00a7803.50 \u2014 30-day malfunction MDR. Report to FDA MedWatch.', channel: '#regulatory-ops', escalate: 'Regulatory Affairs Lead' },\n 'FDA_MDR_SERIOUS_INJURY_30DAY': { days: 30, reg: '21 CFR \u00a7803.50 \u2014 30-day serious injury MDR.', channel: '#regulatory-ops', escalate: 'VP Clinical Affairs' },\n 'HIPAA_PHI_BREACH': { days: 60, reg: '45 CFR \u00a7164.404 \u2014 60-day breach notification to HHS OCR + affected individuals. If >500 state residents: media notice.', channel: '#privacy-incident', escalate: 'Privacy Officer' },\n 'FTC_HEALTH_BREACH': { days: 60, reg: '16 CFR \u00a7318.5 \u2014 60-day FTC Health Breach Notification. If >500 state residents: media + FTC Web Report simultaneously.', channel: '#privacy-incident', escalate: 'General Counsel' },\n 'EU_MDR_SERIOUS_INCIDENT': { days: 15, reg: 'EU MDR Art.87 \u2014 15-day serious incident report to national competent authority. Notify Notified Body.', channel: '#eu-regulatory', escalate: 'EU Qualified Person' },\n 'CURES_ACT_INFO_BLOCKING': { days: 0, reg: '45 CFR \u00a7171.103 \u2014 $1M/violation OIG penalty. If your automation throttles or blocks data access for interoperability requests, this fires. Route to GC immediately.', channel: '#legal-urgent', escalate: 'General Counsel + CEO' },\n 'FDA_SAMD_SOFTWARE_DEFECT': { days: 10, reg: 'FDA SaMD CAPA requirement \u2014 10 business days CAPA initiation. Document in Design History File (DHF).', channel: '#qa-urgent', escalate: 'QA Director' }\n};\nconst c = clocks[type] || { days: 30, reg: 'Unknown incident type \u2014 review manually', channel: '#regulatory-ops', escalate: 'Compliance Team' };\nconst deadline = new Date(Date.now() + c.days * 86400000).toISOString().slice(0,10);\nreturn [{ json: { ...$json, clock_days: c.days, regulatory_note: c.reg, slack_channel: c.channel, escalate_to: c.escalate, response_deadline: deadline } }];"
},
"id": "c5",
"name": "Classify Incident",
"type": "n8n-nodes-base.code",
"position": [
450,
300
]
},
{
"parameters": {
"resource": "message",
"operation": "post",
"channel": "={{ $json.slack_channel }}",
"text": "INCIDENT: {{ $json.incident_type }} | Deadline: {{ $json.response_deadline }} ({{ $json.clock_days }}d) | Escalate: {{ $json.escalate_to }}\\n{{ $json.regulatory_note }}"
},
"id": "sl3",
"name": "Slack Incident Alert",
"type": "n8n-nodes-base.slack",
"position": [
650,
300
]
},
{
"parameters": {
"sendTo": "={{ $json.regulatory_contact_email }}",
"subject": "INCIDENT: {{ $json.incident_type }} \u2014 Response deadline {{ $json.response_deadline }}",
"message": "Incident type: {{ $json.incident_type }}\\nReported: {{ $now }}\\nResponse deadline: {{ $json.response_deadline }} ({{ $json.clock_days }} days)\\nEscalate to: {{ $json.escalate_to }}\\n\\nRegulatory note: {{ $json.regulatory_note }}\\n\\nIncident data: {{ $json | toJsonString }}",
"options": {}
},
"id": "e3",
"name": "Email Response Lead",
"type": "n8n-nodes-base.gmail",
"position": [
650,
450
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "INSERT INTO incident_log (incident_type, regulatory_note, response_deadline, clock_days, escalate_to, raw_payload, created_at) VALUES ('{{ $json.incident_type }}', '{{ $json.regulatory_note }}', '{{ $json.response_deadline }}', {{ $json.clock_days }}, '{{ $json.escalate_to }}', '{{ $json | toJsonString }}', NOW())"
},
"id": "db5",
"name": "Log to Postgres",
"type": "n8n-nodes-base.postgres",
"position": [
850,
300
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "{\"status\":\"received\",\"incident_type\":\"{{ $json.incident_type }}\",\"deadline\":\"{{ $json.response_deadline }}\"}"
},
"id": "r1",
"name": "Respond",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
850,
150
]
}
],
"connections": {
"Incident Webhook": {
"main": [
[
{
"node": "Classify Incident",
"type": "main",
"index": 0
}
]
]
},
"Classify Incident": {
"main": [
[
{
"node": "Slack Incident Alert",
"type": "main",
"index": 0
}
],
[
{
"node": "Email Response Lead",
"type": "main",
"index": 0
}
]
]
},
"Slack Incident Alert": {
"main": [
[
{
"node": "Log to Postgres",
"type": "main",
"index": 0
}
]
]
},
"Log to Postgres": {
"main": [
[
{
"node": "Respond",
"type": "main",
"index": 0
}
]
]
}
}
}
Workflow 5: Weekly MedTech SaaS KPI Dashboard
Runs Monday at 8 AM. Pulls MRR, account counts, and a compliance debt table: open MDR reports, critical MDR items (≤5 days remaining), open HIPAA breaches, open FTC Health Breach notifications, open EU MDR serious incidents, and overdue items. Sends HTML email to CEO + CPO with BCC to Regulatory Affairs + CISO.
The $getWorkflowStaticData call maintains WoW% MRR tracking across executions without a separate database table.
{
"name": "Weekly MedTech SaaS KPI Dashboard",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 8,
"triggerAtMinute": 0,
"weekdays": [
"monday"
]
}
]
}
},
"id": "t5",
"name": "Monday 8 AM",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
250,
300
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT COUNT(*) as total_accounts, SUM(mrr_usd) as total_mrr, SUM(CASE WHEN tier='ENTERPRISE_MEDICAL_DEVICE_MANUFACTURER' THEN mrr_usd ELSE 0 END) as enterprise_mrr, COUNT(CASE WHEN created_at >= NOW() - INTERVAL '7 days' THEN 1 END) as new_accounts_7d, COUNT(CASE WHEN churned_at >= NOW() - INTERVAL '7 days' THEN 1 END) as churned_7d FROM medtech_customers WHERE status='active'"
},
"id": "db6",
"name": "Get Revenue KPIs",
"type": "n8n-nodes-base.postgres",
"position": [
450,
250
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT COUNT(*) as open_mdr_reports, COUNT(CASE WHEN deadline_type LIKE 'FDA_MDR%' AND deadline_date <= NOW() + INTERVAL '5 days' THEN 1 END) as critical_mdr_count, COUNT(CASE WHEN deadline_type = 'HIPAA_BREACH_NOTIFICATION_60DAY' AND status = 'open' THEN 1 END) as open_hipaa_breaches, COUNT(CASE WHEN deadline_type = 'FTC_HEALTH_BREACH_NOTIFICATION_60DAY' AND status = 'open' THEN 1 END) as open_ftc_breaches, COUNT(CASE WHEN deadline_type = 'EU_MDR_SERIOUS_INCIDENT_15DAY' AND status = 'open' THEN 1 END) as open_eu_mdr, COUNT(CASE WHEN status = 'open' AND deadline_date < NOW() THEN 1 END) as overdue_items FROM medtech_regulatory_deadlines"
},
"id": "db7",
"name": "Get Compliance KPIs",
"type": "n8n-nodes-base.postgres",
"position": [
450,
450
]
},
{
"parameters": {
"mode": "combine",
"combinationMode": "mergeByIndex",
"options": {}
},
"id": "m1",
"name": "Merge KPIs",
"type": "n8n-nodes-base.merge",
"position": [
650,
350
]
},
{
"parameters": {
"jsCode": "const d = $json;\nconst prev = $getWorkflowStaticData('global');\nconst mrrWoW = prev.last_mrr ? (((d.total_mrr - prev.last_mrr) / prev.last_mrr) * 100).toFixed(1) : 'N/A';\nprev.last_mrr = d.total_mrr;\n$setWorkflowStaticData('global', prev);\nconst criticalMDR = d.critical_mdr_count > 0 ? `<span style='color:red'>⚠ ${d.critical_mdr_count} MDR CRITICAL (≤5 days)</span>` : '0 critical';\nconst html = `<h2>MedTech SaaS Weekly KPI \u2014 ${new Date().toISOString().slice(0,10)}</h2><table border='1' cellpadding='6' style='border-collapse:collapse'><tr><th>Metric</th><th>Value</th><th>WoW</th></tr><tr><td>Total MRR</td><td>$${Number(d.total_mrr||0).toLocaleString()}</td><td>${mrrWoW}%</td></tr><tr><td>Total Accounts</td><td>${d.total_accounts}</td><td>\u2014</td></tr><tr><td>New Accounts (7d)</td><td>${d.new_accounts_7d}</td><td>\u2014</td></tr><tr><td>Churned (7d)</td><td>${d.churned_7d}</td><td>\u2014</td></tr></table><h3>Compliance Debt</h3><table border='1' cellpadding='6' style='border-collapse:collapse'><tr><th>Item</th><th>Count</th></tr><tr><td>Open MDR Reports</td><td>${d.open_mdr_reports}</td></tr><tr><td>Critical MDR (≤5d)</td><td>${criticalMDR}</td></tr><tr><td>Open HIPAA Breaches</td><td>${d.open_hipaa_breaches}</td></tr><tr><td>Open FTC Health Breaches</td><td>${d.open_ftc_breaches}</td></tr><tr><td>Open EU MDR Incidents</td><td>${d.open_eu_mdr}</td></tr><tr><td>Overdue Items</td><td>${d.overdue_items}</td></tr></table>`;\nreturn [{ json: { ...d, html_report: html, mrr_wow: mrrWoW } }];"
},
"id": "c6",
"name": "Build KPI Report",
"type": "n8n-nodes-base.code",
"position": [
850,
350
]
},
{
"parameters": {
"sendTo": "ceo@yourcompany.com",
"subject": "MedTech Weekly KPI \u2014 MRR ${{ $json.total_mrr }} | {{ $json.open_mdr_reports }} open MDR",
"message": "={{ $json.html_report }}",
"options": {
"appendAttribution": false,
"ccList": "cpo@yourcompany.com",
"bccList": "regulatory@yourcompany.com, ciso@yourcompany.com"
}
},
"id": "e4",
"name": "Email KPI Report",
"type": "n8n-nodes-base.gmail",
"position": [
1050,
350
]
}
],
"connections": {
"Monday 8 AM": {
"main": [
[
{
"node": "Get Revenue KPIs",
"type": "main",
"index": 0
}
],
[
{
"node": "Get Compliance KPIs",
"type": "main",
"index": 0
}
]
]
},
"Get Revenue KPIs": {
"main": [
[
{
"node": "Merge KPIs",
"type": "main",
"index": 0
}
]
]
},
"Get Compliance KPIs": {
"main": [
[
{
"node": "Merge KPIs",
"type": "main",
"index": 1
}
]
]
},
"Merge KPIs": {
"main": [
[
{
"node": "Build KPI Report",
"type": "main",
"index": 0
}
]
]
},
"Build KPI Report": {
"main": [
[
{
"node": "Email KPI Report",
"type": "main",
"index": 0
}
]
]
}
}
}
Why Self-Hosted n8n for MedTech & Digital Health?
| Risk Area | Cloud iPaaS (Zapier/Make) | Self-Hosted n8n |
|---|---|---|
| HIPAA PHI data flow | PHI routes through vendor cloud — BAA required but data still egresses | PHI stays in your VPC — BAA is a formality, not a data boundary |
| FDA §11 audit trail | Vendor logs may not meet 21 CFR §11.10(e) tamper-evident requirement | Postgres audit log in your infrastructure — §11-compliant |
| EU MDR QMS traceability | Workflow data in US cloud — EU data localization risk under GDPR + MDR | On-prem/EU cloud — Art.10(9) QMS boundary intact |
| FTC Health Breach Rule | Health metrics in 3rd-party cloud = potential breach triggering 16 CFR §318 | Consumer health data stays on-prem — FTC Rule scope controlled |
| ISO 13485 CSV validation | Cloud SaaS updates require Computer System Validation revalidation | Version-pinned n8n — IQ/OQ/PQ on your schedule |
| SOC 2 CC9.2 subprocessor | Zapier/Make in your vendor list = scope expansion at every audit | n8n is infrastructure — removes from CC9.2 subprocessor assessment |
| FDA SBOM documentation | Third-party cloud workflow vendor = undocumented data flow in 510(k) | Self-hosted with versioned node SBOM = tractable FDA submission artifact |
The SBOM angle is increasingly material: FDA's 2023 cybersecurity guidance for medical devices (21 CFR §524b) requires a software bill of materials for premarket submissions. A self-hosted n8n instance with documented node versions and data flows is a tractable submission artifact. A third-party cloud iPaaS is a gap.
Get all 5 workflows (import-ready JSON + Postgres schema): stripeai.gumroad.com
All workflows import directly into n8n. Replace credential placeholders (your-postgres-connection, your-slack-workspace, email addresses) before activating.
Top comments (0)