If you're building PharmaTech or biotech software, your customers operate under some of the most demanding compliance regimes on earth: FDA 21 CFR Part 11 (electronic records and signatures), GxP (GMP/GLP/GCP across manufacturing, labs, and clinical trials), and ICH Q10 (Pharmaceutical Quality System). One audit trail gap, one missed deadline, one data integrity finding — and your customer is facing a Warning Letter, a consent decree, or a clinical hold.
Here's the problem with Zapier and Make.com in this space:
- 21 CFR §11.10(e) requires audit trails that cannot be modified or deleted. Zapier auto-deletes execution logs after 30 days. FDA inspections routinely look back 2–5 years. Logs are gone before the investigator opens their laptop.
- GxP Computer System Validation (CSV): Zapier is a closed-source black box — writing a compliant IQ/OQ/PQ validation protocol for it is legally indefensible. Self-hosted n8n gives you source code, git-tagged releases, and a full change control audit trail.
- EMA Annex 11 §7.1 requires audit trails to be available for the lifetime of the GxP computer system. Zapier's 30-day rolling window is non-compliant for EU GMP-regulated systems.
- GDPR Article 44+: Batch record fragments, clinical data, and patient-adjacent information routed through Zapier may require Standard Contractual Clauses for US→EU transfers. Self-hosted n8n eliminates the transfer entirely.
- ICH Q10 §3.2.3 CAPA: The Pharmaceutical Quality System requires documented evidence of corrective action effectiveness. Zapier's task history doesn't generate the audit evidence regulators expect.
n8n self-hosted changes the compliance calculus entirely. Here are 5 production-grade workflows every PharmaTech SaaS company should be running.
Workflow 1: PharmaTech Customer GxP Onboarding Drip
New customers come in as BIG_PHARMA, MID_MARKET_PHARMA, CDMO, CRO, or BIOTECH — each with different compliance flags active. This workflow routes them through the right onboarding cadence automatically.
What it does:
- Classifies incoming customer by revenue tier and company type
- Activates compliance flags: CFR_PART_11_APPLICABLE, GMP_REQUIRED, GCP_APPLICABLE, GLP_APPLICABLE, HIPAA_BAA_REQUIRED, GDPR_DPA_REQUIRED, ICH_Q10_ALIGNED
- Sends tier-appropriate Day 0 welcome email
- Fires CSM Slack alert with Part 11 scope flag for systems requiring CSV validation
- Day 3: checks integration status — if LIMS/ERP not connected, sends reminder and escalates Part 11 scope accounts
- Day 7: sends activation check-in with GMP/Part 11 status
Import-ready JSON:
{
"name": "PharmaTech Customer GxP Onboarding Drip",
"nodes": [
{
"id": "1",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
250,
300
],
"parameters": {
"path": "pharma-customer-onboarding",
"method": "POST",
"responseMode": "responseNode"
}
},
{
"id": "2",
"name": "Classify Customer Tier",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
470,
300
],
"parameters": {
"jsCode": "const d=items[0].json;\nconst tier=d.annual_revenue_usd>500e6?'BIG_PHARMA':d.annual_revenue_usd>50e6?'MID_MARKET_PHARMA':d.company_type==='CDMO'?'CDMO':d.company_type==='CRO'?'CRO':'BIOTECH';\nconst flags=[];\nif(d.has_electronic_batch_records||d.lims_integration)flags.push('CFR_PART_11_APPLICABLE');\nif(d.manufactures_drug_product||d.cmo_client)flags.push('GMP_REQUIRED');\nif(d.runs_clinical_trials)flags.push('GCP_APPLICABLE');\nif(d.has_lab_operations)flags.push('GLP_APPLICABLE');\nif(d.processes_patient_data)flags.push('HIPAA_BAA_REQUIRED');\nif(d.eu_customers||d.eu_sites)flags.push('GDPR_DPA_REQUIRED');\nif(d.formal_pqs_deployed)flags.push('ICH_Q10_ALIGNED');\nreturn[{json:{...d,customer_tier:tier,compliance_flags:flags,onboarding_ts:new Date().toISOString()}}];"
}
},
{
"id": "3",
"name": "Log to Postgres",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
690,
300
],
"parameters": {
"operation": "insert",
"table": "pharma_customer_onboarding",
"columns": "customer_id,company_name,customer_tier,compliance_flags,onboarding_ts",
"values": "={{$json.customer_id}},={{$json.company_name}},={{$json.customer_tier}},={{JSON.stringify($json.compliance_flags)}},={{$json.onboarding_ts}}"
}
},
{
"id": "4",
"name": "Day 0 Welcome Email",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2,
"position": [
910,
200
],
"parameters": {
"operation": "send",
"to": "={{$json.contact_email}}",
"subject": "Welcome to FlowKit \u2014 Your GxP Automation Platform is Ready",
"message": "={{$json.customer_tier==='BIG_PHARMA'?'Your dedicated CSM will contact you within 2 business hours.':$json.customer_tier==='CDMO'?'Your CDMO workflow templates are pre-configured.':'Your onboarding checklist is attached.'}} Compliance flags active: {{$json.compliance_flags.join(', ')}}"
}
},
{
"id": "5",
"name": "Day 0 Slack CSM Alert",
"type": "n8n-nodes-base.slack",
"typeVersion": 2,
"position": [
910,
350
],
"parameters": {
"channel": "#csm-pharma-onboarding",
"text": "New {{$json.customer_tier}} customer: *{{$json.company_name}}*. Flags: {{$json.compliance_flags.join(' | ')}}. {{$json.compliance_flags.includes('CFR_PART_11_APPLICABLE')?'\u26a0\ufe0f Part 11 scope \u2014 CSV validation required':''}}"
}
},
{
"id": "6",
"name": "Wait 3 Days",
"type": "n8n-nodes-base.wait",
"typeVersion": 1,
"position": [
1130,
300
],
"parameters": {
"amount": 3,
"unit": "days"
}
},
{
"id": "7",
"name": "Day 3 Integration Check",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
1350,
300
],
"parameters": {
"operation": "select",
"table": "pharma_integration_status",
"where": "customer_id = '{{$json.customer_id}}'"
}
},
{
"id": "8",
"name": "Day 3 \u2014 Integration Not Confirmed?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1570,
300
],
"parameters": {
"conditions": {
"string": [
{
"value1": "={{$json.integration_status}}",
"operation": "notEqual",
"value2": "CONNECTED"
}
]
}
}
},
{
"id": "9",
"name": "Day 3 Reminder Email",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2,
"position": [
1790,
200
],
"parameters": {
"operation": "send",
"to": "={{$json.contact_email}}",
"subject": "Your LIMS/ERP integration \u2014 5-minute setup",
"message": "Hi {{$json.contact_name}}, your integration has not been confirmed. {{$json.compliance_flags.includes('CFR_PART_11_APPLICABLE')?'Part 11 audit trails require confirmed integration before go-live.':''}} Book a 15-min setup call: calendly.com/flowkit-pharma"
}
},
{
"id": "10",
"name": "Day 3 CSM Escalation Slack",
"type": "n8n-nodes-base.slack",
"typeVersion": 2,
"position": [
1790,
380
],
"parameters": {
"channel": "#csm-pharma-onboarding",
"text": "\u26a0\ufe0f *{{$json.company_name}}* Day 3 \u2014 no integration. {{$json.customer_tier==='BIG_PHARMA'||$json.customer_tier==='CDMO'?'PRIORITY: assign CSM now.':'Monitor.'}}"
}
},
{
"id": "11",
"name": "Wait 4 More Days",
"type": "n8n-nodes-base.wait",
"typeVersion": 1,
"position": [
2010,
300
],
"parameters": {
"amount": 4,
"unit": "days"
}
},
{
"id": "12",
"name": "Day 7 Activation Email",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2,
"position": [
2230,
300
],
"parameters": {
"operation": "send",
"to": "={{$json.contact_email}}",
"subject": "Week 1 Check-In \u2014 Are your GxP workflows running?",
"message": "Hi {{$json.contact_name}}, it has been 7 days. {{$json.compliance_flags.includes('GMP_REQUIRED')?'Your batch record automation should be live.':''}} {{$json.compliance_flags.includes('CFR_PART_11_APPLICABLE')?'Part 11 audit trail is active from Day 1.':''}} Anything blocking you? Reply to this email."
}
}
],
"connections": {
"Webhook": {
"main": [
[
{
"node": "Classify Customer Tier",
"type": "main",
"index": 0
}
]
]
},
"Classify Customer Tier": {
"main": [
[
{
"node": "Log to Postgres",
"type": "main",
"index": 0
}
]
]
},
"Log to Postgres": {
"main": [
[
{
"node": "Day 0 Welcome Email",
"type": "main",
"index": 0
},
{
"node": "Day 0 Slack CSM Alert",
"type": "main",
"index": 0
}
]
]
},
"Day 0 Welcome Email": {
"main": [
[
{
"node": "Wait 3 Days",
"type": "main",
"index": 0
}
]
]
},
"Wait 3 Days": {
"main": [
[
{
"node": "Day 3 Integration Check",
"type": "main",
"index": 0
}
]
]
},
"Day 3 Integration Check": {
"main": [
[
{
"node": "Day 3 \u2014 Integration Not Confirmed?",
"type": "main",
"index": 0
}
]
]
},
"Day 3 \u2014 Integration Not Confirmed?": {
"main": [
[
{
"node": "Day 3 Reminder Email",
"type": "main",
"index": 0
},
{
"node": "Day 3 CSM Escalation Slack",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait 4 More Days",
"type": "main",
"index": 0
}
]
]
},
"Day 3 Reminder Email": {
"main": [
[
{
"node": "Wait 4 More Days",
"type": "main",
"index": 0
}
]
]
},
"Wait 4 More Days": {
"main": [
[
{
"node": "Day 7 Activation Email",
"type": "main",
"index": 0
}
]
]
}
}
}
Workflow 2: FDA 21 CFR Part 11 Audit Trail & System Validation Monitor
Runs every 15 minutes. Polls your Part 11 system registry, checks audit trail continuity, validation status, and e-signature binding. One gap → immediate Slack alert.
What it monitors:
- AUDIT_TRAIL_DISABLED → CRITICAL (§11.10(e) violation in progress)
- Audit trail stale >60min → CRITICAL (§11.10(e) gap — document immediately)
- VALIDATION_EXPIRED → CRITICAL (Part 11 use of system blocked until revalidation)
- ERES_SIGNATURE_BINDING_FAILURE → CRITICAL (§11.50 violation)
- Open deviations >5 → HIGH (ICH Q10 quality system alert)
{
"name": "FDA 21 CFR Part 11 Audit Trail Monitor",
"nodes": [
{
"id": "1",
"name": "Schedule 15min",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1,
"position": [
250,
300
],
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 15
}
]
}
}
},
{
"id": "2",
"name": "Fetch Audit Trail Status",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
470,
300
],
"parameters": {
"operation": "select",
"query": "SELECT system_name, last_audit_entry_ts, audit_trail_enabled, validation_status, eres_status, open_deviations FROM part11_system_registry WHERE active = true"
}
},
{
"id": "3",
"name": "Evaluate Compliance",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
690,
300
],
"parameters": {
"jsCode": "const alerts=[];\nconst now=Date.now();\nfor(const s of items){\n const d=s.json;\n const minsSince=(now-new Date(d.last_audit_entry_ts).getTime())/60000;\n if(!d.audit_trail_enabled){alerts.push({...d,alert_type:'AUDIT_TRAIL_DISABLED',severity:'CRITICAL',message:`CRITICAL: ${d.system_name} audit trail disabled \u2014 21 CFR \u00a711.10(e) violation`});}\n else if(minsSince>60){alerts.push({...d,alert_type:'AUDIT_TRAIL_STALE',severity:'CRITICAL',message:`CRITICAL: ${d.system_name} no audit entries for ${Math.round(minsSince)}min \u2014 potential \u00a711.10(e) gap`});}\n if(d.validation_status==='OUT_OF_VALIDATION'){alerts.push({...d,alert_type:'VALIDATION_EXPIRED',severity:'CRITICAL',message:`CRITICAL: ${d.system_name} validation expired \u2014 Part 11 use blocked until revalidation`});}\n if(d.eres_status==='SIGNATURE_BINDING_FAILURE'){alerts.push({...d,alert_type:'ERES_FAILURE',severity:'CRITICAL',message:`CRITICAL: ${d.system_name} e-signature binding failed \u2014 \u00a711.50 violation`});}\n if(d.open_deviations>5){alerts.push({...d,alert_type:'DEVIATION_BACKLOG',severity:'HIGH',message:`HIGH: ${d.system_name} has ${d.open_deviations} open deviations`});}\n}\nreturn alerts.length?alerts:[{json:{no_alerts:true,checked_at:new Date().toISOString()}}];"
}
},
{
"id": "4",
"name": "Has Alerts?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
910,
300
],
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{$json.no_alerts}}",
"operation": "notEqual",
"value2": true
}
]
}
}
},
{
"id": "5",
"name": "Log Alert to Postgres",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
1130,
200
],
"parameters": {
"operation": "insert",
"table": "part11_audit_alerts",
"columns": "system_name,alert_type,severity,message,detected_at",
"values": "={{$json.system_name}},={{$json.alert_type}},={{$json.severity}},={{$json.message}},={{new Date().toISOString()}}"
}
},
{
"id": "6",
"name": "Slack #quality-systems-critical",
"type": "n8n-nodes-base.slack",
"typeVersion": 2,
"position": [
1350,
200
],
"parameters": {
"channel": "#quality-systems-critical",
"text": "{{$json.message}} | System: {{$json.system_name}} | {{new Date().toISOString()}}"
}
}
],
"connections": {
"Schedule 15min": {
"main": [
[
{
"node": "Fetch Audit Trail Status",
"type": "main",
"index": 0
}
]
]
},
"Fetch Audit Trail Status": {
"main": [
[
{
"node": "Evaluate Compliance",
"type": "main",
"index": 0
}
]
]
},
"Evaluate Compliance": {
"main": [
[
{
"node": "Has Alerts?",
"type": "main",
"index": 0
}
]
]
},
"Has Alerts?": {
"main": [
[
{
"node": "Log Alert to Postgres",
"type": "main",
"index": 0
}
],
[]
]
},
"Log Alert to Postgres": {
"main": [
[
{
"node": "Slack #quality-systems-critical",
"type": "main",
"index": 0
}
]
]
}
}
}
Workflow 3: GxP & FDA Regulatory Filing Deadline Tracker
Runs weekdays at 7AM. Pulls from Google Sheets. Covers 12 deadline types across FDA, EMA, WHO, and DEA — per customer.
Deadline types covered:
| Deadline | Regulatory Cite | Penalty |
|---|---|---|
| FDA Annual Product Review (APR) | 21 CFR §211.180(e) | FDA 483 → Warning Letter |
| IND Annual Report | 21 CFR §312.33 | IND placed on clinical hold |
| NDA/BLA PDUFA Date | PDUFA commitment | CRL → 12+ month delay |
| BIMO Inspection Prep | FDA BIMO Program | Clinical data rejected |
| ICH Q10 Management Review | ICH Q10 §3.4.1 | Inspection finding |
| ICH Q9 Risk Assessment | ICH Q9 | GMP deviation — recall risk |
| EMA Annual GMP Compliance | EMA/189422/2014 | Certificate suspension |
| EU GMP Annual Product Review | Annex 16 | QP certification blocked |
| WHO GMP Certificate Renewal | WHO TRS 986 | Export to WHO markets blocked |
| DEA Schedule Registration | 21 CFR Part 1301 | Controlled substance halt |
{
"name": "GxP & FDA Regulatory Filing Deadline Tracker",
"nodes": [
{
"id": "1",
"name": "Schedule Weekdays 7AM",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1,
"position": [
250,
300
],
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 7 * * 1-5"
}
]
}
}
},
{
"id": "2",
"name": "Fetch Deadlines",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4,
"position": [
470,
300
],
"parameters": {
"operation": "read",
"sheetId": "YOUR_SHEET_ID",
"range": "A:H",
"keyRow": 1
}
},
{
"id": "3",
"name": "Classify Urgency",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
690,
300
],
"parameters": {
"jsCode": "const today=new Date();\nconst results=[];\nconst deadlineTypes={\n 'FDA_ANNUAL_PRODUCT_REVIEW':{'desc':'Annual Product Review (APR/PQR)','penalty':'FDA 483 observation \u2192 Warning Letter','days_notice':60},\n 'IND_ANNUAL_REPORT':{'desc':'IND Annual Report (21 CFR \u00a7312.33)','penalty':'IND placed on clinical hold','days_notice':30},\n 'NDA_PDUFA_DATE':{'desc':'NDA/BLA PDUFA Action Date','penalty':'Complete Response Letter \u2192 12+ month delay','days_notice':90},\n 'FDA_BIORESEARCH_MONITORING':{'desc':'Bioresearch Monitoring (BIMO) Inspection Prep','penalty':'Clinical data rejected \u2192 NDA refusal','days_notice':30},\n 'ICH_Q10_MGMT_REVIEW':{'desc':'ICH Q10 Management Review (annual)','penalty':'Regulatory inspection finding','days_notice':30},\n 'ICH_Q9_RISK_ASSESSMENT':{'desc':'ICH Q9 Risk Assessment Review','penalty':'GMP deviation \u2014 recall risk','days_notice':21},\n 'EMA_ANNUAL_COMPLIANCE':{'desc':'EMA Annual GMP Compliance Report','penalty':'EMA GMP certificate suspension','days_notice':45},\n 'EU_GMP_ANNUAL_PRODUCT_REVIEW':{'desc':'EU GMP Annual Product Review (Annex 16)','penalty':'Qualified Person (QP) certification blocked','days_notice':60},\n 'WHO_GMP_RENEWAL':{'desc':'WHO GMP Certificate Renewal','penalty':'Export to WHO markets blocked','days_notice':90},\n 'DEA_SCHEDULE_RENEWAL':{'desc':'DEA Schedule I-V Registration Renewal','penalty':'Controlled substance manufacturing halted','days_notice':60},\n 'USDA_BIOLOGICAL_LICENSE':{'desc':'USDA Biological License Renewal','penalty':'Veterinary biologics manufacturing halted','days_notice':90},\n 'CLIA_CERTIFICATE_RENEWAL':{'desc':'CLIA Certificate of Compliance Renewal','penalty':'Clinical lab testing operations halted','days_notice':60}\n};\nfor(const row of items){\n const d=row.json;\n if(!d.due_date||!d.deadline_type)continue;\n const due=new Date(d.due_date);\n const daysLeft=Math.round((due-today)/(86400000));\n const meta=deadlineTypes[d.deadline_type]||{};\n const urgency=daysLeft<0?'OVERDUE':daysLeft<=7?'CRITICAL':daysLeft<=14?'URGENT':daysLeft<=30?'WARNING':'NOTICE';\n results.push({json:{...d,...meta,days_left:daysLeft,urgency,penalty_risk:meta.penalty,due_date_fmt:d.due_date}});\n}\nreturn results.filter(r=>r.json.urgency!=='NOTICE');"
}
},
{
"id": "4",
"name": "Route by Urgency",
"type": "n8n-nodes-base.switch",
"typeVersion": 3,
"position": [
910,
300
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "critical",
"conditions": {
"string": [
{
"value1": "={{$json.urgency}}",
"operation": "equals",
"value2": "CRITICAL"
}
]
}
},
{
"outputKey": "overdue",
"conditions": {
"string": [
{
"value1": "={{$json.urgency}}",
"operation": "equals",
"value2": "OVERDUE"
}
]
}
},
{
"outputKey": "urgent",
"conditions": {
"string": [
{
"value1": "={{$json.urgency}}",
"operation": "equals",
"value2": "URGENT"
}
]
}
}
]
}
}
},
{
"id": "5",
"name": "Slack #regulatory-critical",
"type": "n8n-nodes-base.slack",
"typeVersion": 2,
"position": [
1130,
150
],
"parameters": {
"channel": "#regulatory-critical",
"text": "\ud83d\udea8 {{$json.urgency}} ({{$json.days_left}}d): *{{$json.desc}}* | Customer: {{$json.customer_name}} | Risk: {{$json.penalty_risk}}"
}
},
{
"id": "6",
"name": "Gmail Regulatory Team",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2,
"position": [
1130,
300
],
"parameters": {
"operation": "send",
"to": "={{$json.owner_email}}",
"subject": "[{{$json.urgency}}] {{$json.desc}} \u2014 {{$json.days_left}} days remaining",
"message": "Regulatory deadline {{$json.urgency}}: {{$json.desc}} for {{$json.customer_name}}. Due: {{$json.due_date_fmt}}. Penalty: {{$json.penalty_risk}}."
}
},
{
"id": "7",
"name": "Log to Regulatory Register",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
1130,
450
],
"parameters": {
"operation": "insert",
"table": "regulatory_deadline_log",
"columns": "customer_id,deadline_type,urgency,days_left,notified_at",
"values": "={{$json.customer_id}},={{$json.deadline_type}},={{$json.urgency}},={{$json.days_left}},={{new Date().toISOString()}}"
}
}
],
"connections": {
"Schedule Weekdays 7AM": {
"main": [
[
{
"node": "Fetch Deadlines",
"type": "main",
"index": 0
}
]
]
},
"Fetch Deadlines": {
"main": [
[
{
"node": "Classify Urgency",
"type": "main",
"index": 0
}
]
]
},
"Classify Urgency": {
"main": [
[
{
"node": "Route by Urgency",
"type": "main",
"index": 0
}
]
]
},
"Route by Urgency": {
"critical": [
[
{
"node": "Slack #regulatory-critical",
"type": "main",
"index": 0
},
{
"node": "Gmail Regulatory Team",
"type": "main",
"index": 0
},
{
"node": "Log to Regulatory Register",
"type": "main",
"index": 0
}
]
],
"overdue": [
[
{
"node": "Slack #regulatory-critical",
"type": "main",
"index": 0
},
{
"node": "Gmail Regulatory Team",
"type": "main",
"index": 0
},
{
"node": "Log to Regulatory Register",
"type": "main",
"index": 0
}
]
],
"urgent": [
[
{
"node": "Gmail Regulatory Team",
"type": "main",
"index": 0
},
{
"node": "Log to Regulatory Register",
"type": "main",
"index": 0
}
]
]
}
}
}
Workflow 4: FDA Inspection & Warning Letter Alert Pipeline
Webhook-triggered. Covers 7 event types with 30-minute deduplication and a Postgres inspection log.
Event types handled:
- FDA_WARNING_LETTER → CRITICAL, 15 working days to respond (FD&C Act §305)
- FDA_483_OBSERVATION → HIGH, 15 business days CAPA response
- DATA_INTEGRITY_FINDING → CRITICAL, 72h initial CAPA plan (FDA Data Integrity Guidance 2018)
- EMA_GMP_STATEMENT_CONCERN → HIGH, 30 days EMA response
- OOS_RESULT → CRITICAL, full investigation required (21 CFR §211.192)
- CAPA_OVERDUE → HIGH, immediate escalation (ICH Q10 §3.2.3)
- DEVIATION_CORRECTIVE_ACTION → MEDIUM, 30-day CAPA closure target
{
"name": "FDA Inspection & Warning Letter Alert Pipeline",
"nodes": [
{
"id": "1",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
250,
300
],
"parameters": {
"path": "pharma-regulatory-event",
"method": "POST",
"responseMode": "responseNode"
}
},
{
"id": "2",
"name": "Deduplicate 30min",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
470,
300
],
"parameters": {
"jsCode": "const d=items[0].json;\nconst key=`${d.event_type}_${d.customer_id}_${d.system_name||'global'}`;\nconst state=$getWorkflowStaticData('global');\nconst now=Date.now();\nif(state[key]&&(now-state[key])<30*60*1000){return[];}\nstate[key]=now;\nconst classmap={\n 'FDA_WARNING_LETTER':{'severity':'CRITICAL','channel':'#quality-systems-critical','deadline':'15 working days to respond (21 CFR \u00a7558.8)','cite':'FD&C Act \u00a7305'},\n 'FDA_483_OBSERVATION':{'severity':'HIGH','channel':'#quality-systems','deadline':'15 business days for CAPA response','cite':'21 CFR Part 20'},\n 'DATA_INTEGRITY_FINDING':{'severity':'CRITICAL','channel':'#quality-systems-critical','deadline':'72h for initial CAPA plan','cite':'FDA Data Integrity Guidance 2018'},\n 'EMA_GMP_STATEMENT_CONCERN':{'severity':'HIGH','channel':'#quality-systems','deadline':'30 days EMA response','cite':'EMA/189422/2014'},\n 'OOS_RESULT':{'severity':'CRITICAL','channel':'#quality-systems','deadline':'21 CFR \u00a7211.192 full investigation required','cite':'FDA OOS Guidance 2006'},\n 'CAPA_OVERDUE':{'severity':'HIGH','channel':'#capa-management','deadline':'Immediate escalation','cite':'ICH Q10 \u00a73.2.3'},\n 'DEVIATION_CORRECTIVE_ACTION':{'severity':'MEDIUM','channel':'#quality-systems','deadline':'30-day CAPA closure target','cite':'ICH Q10'}\n};\nconst meta=classmap[d.event_type]||{severity:'MEDIUM',channel:'#quality-systems',deadline:'Assess within 24h',cite:'SOP-QS-001'};\nreturn[{json:{...d,...meta,detected_at:new Date().toISOString()}}];"
}
},
{
"id": "3",
"name": "Respond 200",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
470,
450
],
"parameters": {
"responseCode": 200,
"responseBody": "{\"status\":\"received\"}"
}
},
{
"id": "4",
"name": "Log Inspection Event",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
690,
300
],
"parameters": {
"operation": "insert",
"table": "fda_inspection_log",
"columns": "customer_id,event_type,severity,deadline,detected_at,cite",
"values": "={{$json.customer_id}},={{$json.event_type}},={{$json.severity}},={{$json.deadline}},={{$json.detected_at}},={{$json.cite}}"
}
},
{
"id": "5",
"name": "Slack Alert",
"type": "n8n-nodes-base.slack",
"typeVersion": 2,
"position": [
910,
250
],
"parameters": {
"channel": "={{$json.channel}}",
"text": "\ud83d\udea8 *{{$json.severity}}* \u2014 {{$json.event_type}} | Customer: {{$json.customer_name}} | Deadline: {{$json.deadline}} | Cite: {{$json.cite}} | {{$json.detected_at}}"
}
},
{
"id": "6",
"name": "Gmail QA Director",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2,
"position": [
910,
400
],
"parameters": {
"operation": "send",
"to": "={{$json.qa_director_email}}",
"subject": "[{{$json.severity}}] {{$json.event_type}} \u2014 {{$json.customer_name}} \u2014 Action Required",
"message": "Regulatory event: {{$json.event_type}}\nCustomer: {{$json.customer_name}}\nSeverity: {{$json.severity}}\nDeadline: {{$json.deadline}}\nRegulatory cite: {{$json.cite}}\nDetected: {{$json.detected_at}}"
}
}
],
"connections": {
"Webhook": {
"main": [
[
{
"node": "Deduplicate 30min",
"type": "main",
"index": 0
},
{
"node": "Respond 200",
"type": "main",
"index": 0
}
]
]
},
"Deduplicate 30min": {
"main": [
[
{
"node": "Log Inspection Event",
"type": "main",
"index": 0
}
]
]
},
"Log Inspection Event": {
"main": [
[
{
"node": "Slack Alert",
"type": "main",
"index": 0
},
{
"node": "Gmail QA Director",
"type": "main",
"index": 0
}
]
]
}
}
}
Workflow 5: Weekly PharmaTech Platform KPI Dashboard
Runs Monday 8AM. Pulls dual Postgres views. Builds HTML table with WoW% deltas. Flags CAPA rate <90%, audit trail completeness <99%, and high open deviations. BCC CEO, VP Quality, CMO.
Metrics tracked:
- MRR (with WoW%)
- Active customers (with WoW%)
- Part 11 coverage (customers with active audit trails / total)
- Average audit trail completeness %
- Open deviations total
- CAPA on-time rate %
- New trials this week / trial conversion
- Average time to validation (days)
{
"name": "Weekly PharmaTech Platform KPI Dashboard",
"nodes": [
{
"id": "1",
"name": "Schedule Monday 8AM",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1,
"position": [
250,
300
],
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 8 * * 1"
}
]
}
}
},
{
"id": "2",
"name": "Fetch Platform Metrics",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
470,
200
],
"parameters": {
"operation": "select",
"query": "SELECT active_customers, customers_with_part11_active, avg_audit_trail_completeness_pct, open_deviations_total, capa_on_time_rate_pct, mrr_usd, new_trials_this_week, trials_converted_this_week, avg_time_to_validation_days FROM platform_metrics_weekly WHERE week = current_date - interval '7 days'"
}
},
{
"id": "3",
"name": "Fetch Prior Week",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
470,
400
],
"parameters": {
"operation": "select",
"query": "SELECT mrr_usd as prior_mrr, active_customers as prior_customers FROM platform_metrics_weekly WHERE week = current_date - interval '14 days'"
}
},
{
"id": "4",
"name": "Merge",
"type": "n8n-nodes-base.merge",
"typeVersion": 3,
"position": [
690,
300
],
"parameters": {
"mode": "combine",
"combineBy": "position"
}
},
{
"id": "5",
"name": "Build KPI Report",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
910,
300
],
"parameters": {
"jsCode": "const d=items[0].json;\nconst mrrGrowth=d.prior_mrr?((d.mrr_usd-d.prior_mrm)/d.prior_mrr*100).toFixed(1):0;\nconst custGrowth=d.prior_customers?((d.active_customers-d.prior_customers)/d.prior_customers*100).toFixed(1):0;\nconst capaFlag=d.capa_on_time_rate_pct<90?'\u26a0\ufe0f CAPA rate below 90% \u2014 ICH Q10 \u00a73.2.3 risk':'';\nconst devFlag=d.open_deviations_total>20?'\u26a0\ufe0f High open deviations \u2014 quality risk':'';\nconst part11Flag=d.avg_audit_trail_completeness_pct<99?'\u26a0\ufe0f Audit trail completeness below 99% \u2014 Part 11 gap':'';\nconst html=`<h2>FlowKit PharmaTech Platform \u2014 Weekly KPI</h2>\n<table border='1' cellpadding='6'>\n<tr><th>Metric</th><th>This Week</th><th>WoW</th></tr>\n<tr><td>MRR</td><td>$${Number(d.mrr_usd).toLocaleString()}</td><td>${mrrGrowth>0?'+':''}${mrrGrowth}%</td></tr>\n<tr><td>Active Customers</td><td>${d.active_customers}</td><td>${custGrowth>0?'+':''}${custGrowth}%</td></tr>\n<tr><td>Part 11 Coverage</td><td>${d.customers_with_part11_active}/${d.active_customers}</td><td>\u2014</td></tr>\n<tr><td>Audit Trail Completeness</td><td>${d.avg_audit_trail_completeness_pct}%</td><td>\u2014</td></tr>\n<tr><td>Open Deviations</td><td>${d.open_deviations_total}</td><td>\u2014</td></tr>\n<tr><td>CAPA On-Time Rate</td><td>${d.capa_on_time_rate_pct}%</td><td>\u2014</td></tr>\n<tr><td>New Trials</td><td>${d.new_trials_this_week}</td><td>\u2014</td></tr>\n<tr><td>Trial Conversion</td><td>${d.trials_converted_this_week}</td><td>\u2014</td></tr>\n<tr><td>Avg Time to Validation</td><td>${d.avg_time_to_validation_days}d</td><td>\u2014</td></tr>\n</table>\n${capaFlag?'<p>'+capaFlag+'</p>':''}\n${devFlag?'<p>'+devFlag+'</p>':''}\n${part11Flag?'<p>'+part11Flag+'</p>':''}`;\nconst flags=[capaFlag,devFlag,part11Flag].filter(Boolean);\nconst subject=flags.length?`[ACTION: ${flags.length} quality flags] FlowKit PharmaTech KPI \u2014 Week of ${new Date().toISOString().slice(0,10)}`:`FlowKit PharmaTech KPI \u2014 Week of ${new Date().toISOString().slice(0,10)}`;\nreturn[{json:{...d,html_body:html,email_subject:subject,slack_summary:`MRR $${Number(d.mrr_usd).toLocaleString()} (${mrrGrowth>0?'+':''}${mrrGrowth}%) | Customers: ${d.active_customers} | Part 11: ${d.customers_with_part11_active} active | CAPA: ${d.capa_on_time_rate_pct}% on-time | Deviations open: ${d.open_deviations_total}`}}];"
}
},
{
"id": "6",
"name": "Gmail CEO + QA",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2,
"position": [
1130,
250
],
"parameters": {
"operation": "send",
"to": "ceo@yourcompany.com",
"bcc": "vp-quality@yourcompany.com,cmo@yourcompany.com",
"subject": "={{$json.email_subject}}",
"message": "={{$json.html_body}}",
"options": {
"contentType": "html"
}
}
},
{
"id": "7",
"name": "Slack #exec-pharma-kpis",
"type": "n8n-nodes-base.slack",
"typeVersion": 2,
"position": [
1130,
400
],
"parameters": {
"channel": "#exec-pharma-kpis",
"text": "={{$json.slack_summary}}"
}
}
],
"connections": {
"Schedule Monday 8AM": {
"main": [
[
{
"node": "Fetch Platform Metrics",
"type": "main",
"index": 0
},
{
"node": "Fetch Prior Week",
"type": "main",
"index": 0
}
]
]
},
"Fetch Platform Metrics": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"Fetch Prior Week": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"Merge": {
"main": [
[
{
"node": "Build KPI Report",
"type": "main",
"index": 0
}
]
]
},
"Build KPI Report": {
"main": [
[
{
"node": "Gmail CEO + QA",
"type": "main",
"index": 0
},
{
"node": "Slack #exec-pharma-kpis",
"type": "main",
"index": 0
}
]
]
}
}
}
Why PharmaTech SaaS Teams Are Replacing Zapier with n8n
| Issue | Zapier/Make | n8n Self-Hosted |
|---|---|---|
| FDA audit trail retention | 30-day auto-delete | Unlimited — git-versioned |
| GxP Computer System Validation | Black-box, no IQ/OQ/PQ evidence | Full source, reproducible validation |
| EMA Annex 11 audit availability | 30 days only | System lifetime |
| GDPR Art.44 data transfer | US→EU SCCs required | No transfer — stays on-prem |
| ICH Q10 CAPA evidence | Task log only | Full execution trace + Postgres |
| NERC CIP / 21 CFR Part 11 air-gap | Cannot self-host | Docker on-prem, air-gapped |
| Cost at 1M tasks/month | $500+/mo | $0 + $20/mo VPS |
The FDA's 2023 Data Integrity Guidance specifically calls out automated systems that don't retain complete audit trails. If your PharmaTech SaaS is routing GxP-adjacent data through Zapier and a regulatory agency asks for 18 months of workflow audit history, the answer is: it doesn't exist.
Get the Pre-Built Templates
All 5 workflows are available as import-ready JSON on the FlowKit store.
The AI Customer Support Bot and Customer Feedback Analyzer templates are particularly useful for PharmaTech SaaS teams managing high-volume regulatory inquiries and customer QA tickets.
Questions or want the full Postgres schema for the part11_system_registry and pharma_customer_onboarding tables? Drop a comment below.
Top comments (0)