Utility-scale SaaS vendors face a compliance reality their counterparts in other verticals don't: NERC CIP-003-8 R2 defines 'external routable connectivity' as any communication path between a BES Cyber System and an external network. When your team routes grid operational data through Zapier or Make.com, you've created that connectivity — and the cloud iPaaS vendor is now part of your CIP compliance scope.
This isn't a theoretical risk. It's a literal violation of the standard, and FERC enforcement actions against utilities for CIP gaps run into six-figure penalties per violation per day.
This article gives you five production-ready n8n workflow templates built for utility, grid, pipeline, and nuclear SaaS vendors. Each includes complete, importable JSON — copy it into your n8n instance and adapt the Sheets schema to your data.
Who These Workflows Are For
These automations target SaaS companies selling into regulated utility markets:
| Tier | Example Customers | Primary Regulations |
|---|---|---|
INVESTOR_OWNED_UTILITY |
Duke Energy, Dominion, Xcel scale | NERC CIP v7, FERC Form 1, state PUC |
MUNICIPAL_UTILITY |
City-owned electric/gas utilities | NERC CIP, state PUC, EPA CAA §111(d) |
RURAL_ELECTRIC_COOPERATIVE |
NRECA member co-ops | NERC CIP, USDA RUS, state PUC |
GAS_DISTRIBUTION_SAAS_VENDOR |
Pipeline operators | PHMSA 49 CFR §192/§195 |
DER_AGGREGATION_PLATFORM |
Virtual power plant operators | FERC Order 2222, ISO/RTO rules |
NUCLEAR_COMPLIANCE_SAAS |
NRC licensees | NRC 10 CFR §73.54, §50.72 |
STATE_PUC_REGULATORY_TECH |
Rate case filing tools | State PUC, IRP filings |
Workflow 1: UtilityTech Customer Onboarding Drip
What it does: Segments new customers by tier and compliance flag, injects the relevant regulatory context into each email, then sends a 14-day onboarding sequence that drives time-to-value.
The compliance injection: Customers flagged NERC_CIP_HIGH_IMPACT_BES get a Day 0 note explaining why BES Cyber System data must stay within their Electronic Security Perimeter — and why their self-hosted n8n instance satisfies that requirement. Same logic for NRC, PHMSA, and FERC Order 2222 customers.
{
"name": "UtilityTech Customer Onboarding Drip",
"nodes": [
{
"id": "w1n1",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [
240,
300
],
"parameters": {
"path": "utility-customer-signup",
"responseMode": "responseNode",
"responseData": "firstEntryJson"
}
},
{
"id": "w1n2",
"name": "Segment Tier",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
460,
300
],
"parameters": {
"jsCode": "const inp = $input.first().json;\nconst tier = inp.customer_tier || 'MUNICIPAL_UTILITY';\nconst flags = inp.compliance_flags || [];\nconst name = inp.contact_name || 'Team';\nconst company = inp.company_name || 'Your Company';\nconst subjectMap = {\n INVESTOR_OWNED_UTILITY: 'Welcome to FlowKit \u2014 NERC CIP & FERC Compliance Automation',\n MUNICIPAL_UTILITY: 'Welcome to FlowKit \u2014 Grid & Utility Compliance Automation',\n RURAL_ELECTRIC_COOPERATIVE: 'Welcome to FlowKit \u2014 Cooperative Grid Operations Automation',\n GAS_DISTRIBUTION_SAAS_VENDOR: 'Welcome to FlowKit \u2014 PHMSA Pipeline Integrity Automation',\n DER_AGGREGATION_PLATFORM: 'Welcome to FlowKit \u2014 FERC Order 2222 & VPP Automation',\n NUCLEAR_COMPLIANCE_SAAS: 'Welcome to FlowKit \u2014 NRC Cyber Security & Compliance Automation',\n STATE_PUC_REGULATORY_TECH: 'Welcome to FlowKit \u2014 PUC Rate Case & Regulatory Filing Automation'\n};\nlet complianceNote = 'Your n8n instance runs on your own infrastructure \u2014 utility operational data stays within your control boundary.';\nif (flags.includes('NERC_CIP_HIGH_IMPACT_BES')) {\n complianceNote = 'NERC CIP Note: Under CIP-003-8 R2, BES Cyber System data routed through cloud iPaaS creates external routable connectivity \u2014 a CIP compliance gap. Self-hosted n8n keeps BES data within your Electronic Security Perimeter.';\n} else if (flags.includes('NRC_NUCLEAR_LICENSEE')) {\n complianceNote = 'NRC Note: 10 CFR \u00a773.54 requires all digital pathways in your cyber security plan. Self-hosted n8n operates inside your plan perimeter \u2014 no cloud vendor expansion of your attack surface.';\n} else if (flags.includes('PHMSA_GAS_OPERATOR')) {\n complianceNote = 'PHMSA Note: 49 CFR \u00a7192.911 IMP data requirements apply. Self-hosted n8n keeps pipeline integrity records within your operational boundary.';\n} else if (flags.includes('FERC_ORDER_2222_DER')) {\n complianceNote = 'FERC Order 2222 Note: DER dispatch data and bid curves are market-sensitive. Self-hosted n8n keeps aggregation data within your ISO/RTO exchange boundary.';\n}\nreturn [{json: {customer_id: inp.customer_id, email: inp.email, name, company, tier, flags,\n subject: subjectMap[tier] || 'Welcome to FlowKit \u2014 Utility Compliance Automation',\n compliance_note: complianceNote, enrolled_at: new Date().toISOString()}}];"
}
},
{
"id": "w1n3",
"name": "Gmail Day 0 Welcome",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2,
"position": [
680,
300
],
"parameters": {
"operation": "send",
"toList": "={{$json.email}}",
"subject": "={{$json.subject}}",
"message": "<h2>Welcome to FlowKit, {name}!</h2><p>Your FlowKit workspace is active.</p><p><strong>{note}</strong></p><h3>Quick-Start Checklist</h3><ul><li>Connect your Google Sheets data sources</li><li>Configure Slack channels (#regulatory-compliance, #grid-ops)</li><li>Activate the compliance deadline tracker (Workflow 2)</li><li>Set up the API health monitor (Workflow 3)</li></ul><p>Reply to this email \u2014 we respond within 4 business hours.</p>",
"options": {
"replyTo": "support@flowkithq.com"
}
}
},
{
"id": "w1n4",
"name": "Wait 3 Days",
"type": "n8n-nodes-base.wait",
"typeVersion": 1,
"position": [
900,
300
],
"parameters": {
"amount": 3,
"unit": "days"
}
},
{
"id": "w1n5",
"name": "Gmail Day 3 Integration",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2,
"position": [
1120,
300
],
"parameters": {
"operation": "send",
"toList": "={{$node['Segment Tier'].json.email}}",
"subject": "Your FlowKit integration checklist",
"message": "<h2>Day 3 Check-In</h2><p>Top integrations for utility SaaS teams: Google Sheets compliance tracker, Slack #regulatory-compliance, SCADA/EMS API health monitor, state PUC e-filing deadline sync.</p><p>Reply if you want a 15-min setup call.</p>"
}
},
{
"id": "w1n6",
"name": "Wait 4 Days",
"type": "n8n-nodes-base.wait",
"typeVersion": 1,
"position": [
1340,
300
],
"parameters": {
"amount": 4,
"unit": "days"
}
},
{
"id": "w1n7",
"name": "Gmail Day 7 Use Case",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2,
"position": [
1560,
300
],
"parameters": {
"operation": "send",
"toList": "={{$node['Segment Tier'].json.email}}",
"subject": "The automation that saves utility compliance teams 6 hours/week",
"message": "<h2>The Compliance Deadline Tracker</h2><p>The #1 FlowKit automation for utility SaaS customers: a daily NERC CIP / FERC / PHMSA / NRC deadline tracker that pings Slack before anything goes overdue. <a href='https://stripeai.gumroad.com'>Get the full workflow JSON \u2192</a></p>"
}
},
{
"id": "w1n8",
"name": "Wait 7 Days",
"type": "n8n-nodes-base.wait",
"typeVersion": 1,
"position": [
1780,
300
],
"parameters": {
"amount": 7,
"unit": "days"
}
},
{
"id": "w1n9",
"name": "Gmail Day 14 Exec",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2,
"position": [
2000,
300
],
"parameters": {
"operation": "send",
"toList": "={{$node['Segment Tier'].json.email}}",
"subject": "14-day check-in \u2014 how is FlowKit working?",
"message": "<p>Hi {{$node['Segment Tier'].json.name}}, two weeks in \u2014 which workflows are running, and what's still on your list? Our Complete Bundle has all 15 templates pre-configured for NERC CIP / FERC / PHMSA / NRC at <a href='https://stripeai.gumroad.com'>stripeai.gumroad.com</a>.</p>"
}
},
{
"id": "w1n10",
"name": "Log Sheets",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4,
"position": [
2220,
300
],
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "YOUR_SPREADSHEET_ID",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "onboarding_log",
"mode": "name"
},
"columns": {
"mappingMode": "autoMapInputData",
"value": {},
"matchingColumns": []
},
"options": {}
}
}
],
"connections": {
"Webhook": {
"main": [
[
{
"node": "Segment Tier",
"type": "main",
"index": 0
}
]
]
},
"Segment Tier": {
"main": [
[
{
"node": "Gmail Day 0 Welcome",
"type": "main",
"index": 0
}
]
]
},
"Gmail Day 0 Welcome": {
"main": [
[
{
"node": "Wait 3 Days",
"type": "main",
"index": 0
}
]
]
},
"Wait 3 Days": {
"main": [
[
{
"node": "Gmail Day 3 Integration",
"type": "main",
"index": 0
}
]
]
},
"Gmail Day 3 Integration": {
"main": [
[
{
"node": "Wait 4 Days",
"type": "main",
"index": 0
}
]
]
},
"Wait 4 Days": {
"main": [
[
{
"node": "Gmail Day 7 Use Case",
"type": "main",
"index": 0
}
]
]
},
"Gmail Day 7 Use Case": {
"main": [
[
{
"node": "Wait 7 Days",
"type": "main",
"index": 0
}
]
]
},
"Wait 7 Days": {
"main": [
[
{
"node": "Gmail Day 14 Exec",
"type": "main",
"index": 0
}
]
]
},
"Gmail Day 14 Exec": {
"main": [
[
{
"node": "Log Sheets",
"type": "main",
"index": 0
}
]
]
}
}
}
Workflow 2: NERC CIP v7 / FERC / PHMSA / NRC Compliance Deadline Tracker
What it does: Reads a Google Sheet of upcoming regulatory deadlines, classifies each as OVERDUE / CRITICAL / URGENT / WARNING / NOTICE, and fires Slack alerts plus owner emails for anything within 45 days.
12 deadline types supported:
| Deadline Type | Regulatory Clock |
|---|---|
NERC_CIP_ANNUAL_SELF_CERTIFICATION |
Feb 15 annually — ERO Enterprise submission |
NERC_CIP_PATCH_MANAGEMENT_35_DAY |
CIP-007-6 R2.2 — 35 calendar days from vendor notification |
FERC_FORM_1_ANNUAL |
March 31 — annual electric utility report |
FERC_ORDER_2222_PARTICIPATION_MODEL |
FERC docket compliance per ISO/RTO tariff |
PHMSA_GAS_INTEGRITY_ANNUAL |
49 CFR §192.937 — annual IMP reassessment |
PHMSA_OPERATOR_QUALIFICATION_RENEWAL |
49 CFR §192.805 — OQ plan renewal |
NRC_10_CFR_50_ANNUAL_REPORT |
§50.71(b) — 6 months after fiscal year end |
NRC_CYBER_SECURITY_ANNUAL_REVIEW |
10 CFR §73.54(e) — annual CS plan review |
STATE_PUC_RATE_CASE_ANNUAL |
Per state PUC schedule |
STATE_PUC_OUTAGE_REPORT |
24–48h per state commission rules |
EPA_CLEAN_POWER_ANNUAL |
CAA §111(d) — annual compliance report |
SOC2_TYPE2_RENEWAL |
Annual audit renewal |
{
"name": "NERC CIP v7 / FERC / PHMSA / NRC Deadline Tracker",
"nodes": [
{
"id": "w2n1",
"name": "Daily 7AM",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1,
"position": [
240,
300
],
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 7 * * *"
}
]
}
}
},
{
"id": "w2n2",
"name": "Read Deadlines Sheet",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4,
"position": [
460,
300
],
"parameters": {
"operation": "read",
"documentId": {
"__rl": true,
"value": "YOUR_SPREADSHEET_ID",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "utility_compliance_deadlines",
"mode": "name"
},
"options": {}
}
},
{
"id": "w2n3",
"name": "Classify Urgency",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
680,
300
],
"parameters": {
"jsCode": "const rows = $input.all().map(r => r.json);\nconst now = new Date();\nconst results = [];\nfor (const row of rows) {\n if (!row.deadline_date || !row.regulation_type || !row.owner_email) continue;\n const dl = new Date(row.deadline_date);\n const daysLeft = Math.ceil((dl - now) / 86400000);\n let urgency = 'NOTICE';\n if (daysLeft < 0) urgency = 'OVERDUE';\n else if (daysLeft <= 7) urgency = 'CRITICAL';\n else if (daysLeft <= 21) urgency = 'URGENT';\n else if (daysLeft <= 45) urgency = 'WARNING';\n else if (daysLeft <= 90) urgency = 'NOTICE';\n else continue;\n const clockMap = {\n NERC_CIP_ANNUAL_SELF_CERTIFICATION: 'NERC CIP-007-6 R2.2 \u2014 annual Feb 15 submission to ERO Enterprise',\n NERC_CIP_PATCH_MANAGEMENT_35_DAY: 'NERC CIP-007-6 R2.2 \u2014 35 calendar days from vendor notification',\n FERC_FORM_1_ANNUAL: 'FERC Form No. 1 \u2014 March 31 annual electric utility report',\n FERC_ORDER_2222_PARTICIPATION_MODEL: 'FERC Order 2222 \u2014 DER aggregator participation model compliance',\n PHMSA_GAS_INTEGRITY_ANNUAL: 'PHMSA 49 CFR \u00a7192.937 \u2014 annual integrity management reassessment',\n PHMSA_OPERATOR_QUALIFICATION_RENEWAL: 'PHMSA 49 CFR \u00a7192.805 \u2014 operator qualification plan renewal',\n NRC_10_CFR_50_ANNUAL_REPORT: 'NRC 10 CFR \u00a750.71(b) \u2014 annual safety report within 6 months of fiscal year end',\n NRC_CYBER_SECURITY_ANNUAL_REVIEW: 'NRC 10 CFR \u00a773.54(e) \u2014 annual cyber security plan review',\n STATE_PUC_RATE_CASE_ANNUAL: 'State PUC \u2014 annual rate case or IRP filing per state schedule',\n STATE_PUC_OUTAGE_REPORT: 'State PUC \u2014 major outage report 24-48h per state rules',\n EPA_CLEAN_POWER_ANNUAL: 'EPA CAA \u00a7111(d) \u2014 annual clean power plan compliance report',\n SOC2_TYPE2_RENEWAL: 'SOC 2 Type II \u2014 annual audit renewal'\n };\n results.push({json: {\n ...row, daysLeft, urgency,\n regulatory_clock: clockMap[row.regulation_type] || row.regulation_type,\n alert_message: `[${urgency}] ${row.regulation_type}: ${row.deadline_name} \u2014 ${daysLeft < 0 ? Math.abs(daysLeft) + ' days OVERDUE' : daysLeft + ' days remaining'} (${row.deadline_date}). Owner: ${row.owner_email}. ${clockMap[row.regulation_type] || ''}`\n }});\n}\nreturn results.length ? results : [{json: {urgency: 'NONE', alert_message: 'No deadlines require attention today.'}}];"
}
},
{
"id": "w2n4",
"name": "Skip if None",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
900,
300
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": false,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "c1",
"leftValue": "={{$json.urgency}}",
"rightValue": "NONE",
"operator": {
"type": "string",
"operation": "notEquals"
}
}
],
"combinator": "and"
}
}
},
{
"id": "w2n5",
"name": "Slack Alert",
"type": "n8n-nodes-base.slack",
"typeVersion": 2,
"position": [
1120,
200
],
"parameters": {
"operation": "post",
"channel": "#regulatory-compliance",
"text": "={{$json.alert_message}}",
"otherOptions": {}
}
},
{
"id": "w2n6",
"name": "Gmail to Owner",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2,
"position": [
1120,
400
],
"parameters": {
"operation": "send",
"toList": "={{$json.owner_email}}",
"subject": "=[{{$json.urgency}}] {{$json.regulation_type}}: Action Required \u2014 {{$json.deadline_name}}",
"message": "<p>{{$json.alert_message}}</p><p>Regulatory clock: {{$json.regulatory_clock}}</p>"
}
},
{
"id": "w2n7",
"name": "Log Alert",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4,
"position": [
1340,
300
],
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "YOUR_SPREADSHEET_ID",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "deadline_alerts",
"mode": "name"
},
"columns": {
"mappingMode": "autoMapInputData",
"value": {},
"matchingColumns": []
},
"options": {}
}
}
],
"connections": {
"Daily 7AM": {
"main": [
[
{
"node": "Read Deadlines Sheet",
"type": "main",
"index": 0
}
]
]
},
"Read Deadlines Sheet": {
"main": [
[
{
"node": "Classify Urgency",
"type": "main",
"index": 0
}
]
]
},
"Classify Urgency": {
"main": [
[
{
"node": "Skip if None",
"type": "main",
"index": 0
}
]
]
},
"Skip if None": {
"main": [
[
{
"node": "Slack Alert",
"type": "main",
"index": 0
},
{
"node": "Gmail to Owner",
"type": "main",
"index": 0
}
],
[]
]
},
"Slack Alert": {
"main": [
[
{
"node": "Log Alert",
"type": "main",
"index": 0
}
]
]
},
"Gmail to Owner": {
"main": [
[
{
"node": "Log Alert",
"type": "main",
"index": 0
}
]
]
}
}
}
Workflow 3: Grid & Pipeline API Health Monitor
What it does: Pings five critical infrastructure API endpoints every 5 minutes — SCADA grid, FERC eSubmission, PHMSA pipeline integrity, NRC event reporting, and state PUC portal. Classifies failures as CRITICAL or DEGRADED with the specific regulatory risk annotation, fires Slack alerts to #grid-ops, and logs all incidents to Google Sheets.
Why this matters beyond uptime: NERC CIP-007-6 R6.1 requires logging of all security events on BES Cyber Systems. A health monitor that tracks your SCADA API is also your CIP-007 evidence log. Your n8n job logs are timestamped, structured, and auditable.
{
"name": "Grid & Pipeline API Health Monitor",
"nodes": [
{
"id": "w3n1",
"name": "Every 5 Min",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1,
"position": [
240,
300
],
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 5
}
]
}
}
},
{
"id": "w3n2",
"name": "Check SCADA Grid API",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
460,
180
],
"parameters": {
"method": "GET",
"url": "https://your-scada-grid-api.internal/health",
"options": {
"response": {
"response": {
"neverError": true
}
},
"timeout": 5000
},
"sendBody": false
}
},
{
"id": "w3n3",
"name": "Check FERC eSubmission API",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
460,
280
],
"parameters": {
"method": "GET",
"url": "https://esubmit.ferc.gov/api/health",
"options": {
"response": {
"response": {
"neverError": true
}
},
"timeout": 5000
},
"sendBody": false
}
},
{
"id": "w3n4",
"name": "Check PHMSA Pipeline API",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
460,
380
],
"parameters": {
"method": "GET",
"url": "https://your-phmsa-pipeline-api.internal/health",
"options": {
"response": {
"response": {
"neverError": true
}
},
"timeout": 5000
},
"sendBody": false
}
},
{
"id": "w3n5",
"name": "Check NRC Event API",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
460,
480
],
"parameters": {
"method": "GET",
"url": "https://your-nrc-event-api.internal/health",
"options": {
"response": {
"response": {
"neverError": true
}
},
"timeout": 5000
},
"sendBody": false
}
},
{
"id": "w3n6",
"name": "Check State PUC Portal",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
460,
580
],
"parameters": {
"method": "GET",
"url": "https://your-state-puc-portal.gov/api/health",
"options": {
"response": {
"response": {
"neverError": true
}
},
"timeout": 5000
},
"sendBody": false
}
},
{
"id": "w3n7",
"name": "Merge Health Results",
"type": "n8n-nodes-base.merge",
"typeVersion": 3,
"position": [
680,
380
],
"parameters": {
"mode": "combine",
"combineBy": "combineAll",
"options": {}
}
},
{
"id": "w3n8",
"name": "Classify Issues",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
900,
380
],
"parameters": {
"jsCode": "const checks = $input.all().map(r => r.json);\nconst issues = [];\nfor (const check of checks) {\n const ok = check.statusCode >= 200 && check.statusCode < 300;\n const slow = (check.responseTime || 0) > 3000;\n if (!ok || slow) {\n const endpointMap = {\n scada_grid_api: {label: 'SCADA Grid API', reg: 'NERC CIP-007-6 R6.1 \u2014 BES Cyber System asset monitoring'},\n ferc_esubmission_api: {label: 'FERC eSubmission API', reg: 'FERC Order 2222 \u2014 DER participation model filings'},\n phmsa_pipeline_api: {label: 'PHMSA Pipeline Integrity API', reg: 'PHMSA 49 CFR \u00a7192.937 \u2014 IMP reassessment data'},\n nrc_event_api: {label: 'NRC Event Reporting API', reg: 'NRC 10 CFR \u00a750.72 \u2014 8-hour notification window'},\n state_puc_portal_api: {label: 'State PUC Portal API', reg: 'State PUC \u2014 rate case and outage report filings'}\n };\n const info = endpointMap[check.endpoint_key] || {label: check.endpoint_key, reg: 'Unknown regulation'};\n issues.push({json: {\n endpoint_key: check.endpoint_key,\n endpoint_label: info.label,\n regulatory_note: info.reg,\n status: ok ? 'DEGRADED' : 'CRITICAL',\n statusCode: check.statusCode,\n responseTime: check.responseTime,\n checked_at: new Date().toISOString(),\n message: `[${ok ? 'DEGRADED' : 'CRITICAL'}] ${info.label} \u2014 HTTP ${check.statusCode}, ${check.responseTime}ms. ${info.reg}`\n }});\n }\n}\nreturn issues.length ? issues : [{json: {status: 'OK', message: 'All grid/pipeline endpoints healthy.'}}];"
}
},
{
"id": "w3n9",
"name": "Has Issues?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1120,
380
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": false
},
"conditions": [
{
"id": "c1",
"leftValue": "={{$json.status}}",
"rightValue": "OK",
"operator": {
"type": "string",
"operation": "notEquals"
}
}
],
"combinator": "and"
}
}
},
{
"id": "w3n10",
"name": "Slack #grid-ops",
"type": "n8n-nodes-base.slack",
"typeVersion": 2,
"position": [
1340,
280
],
"parameters": {
"operation": "post",
"channel": "#grid-ops",
"text": "={{$json.message}}",
"otherOptions": {}
}
},
{
"id": "w3n11",
"name": "Log Incident",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4,
"position": [
1340,
480
],
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "YOUR_SPREADSHEET_ID",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "api_health_log",
"mode": "name"
},
"columns": {
"mappingMode": "autoMapInputData",
"value": {},
"matchingColumns": []
},
"options": {}
}
}
],
"connections": {
"Every 5 Min": {
"main": [
[
{
"node": "Check SCADA Grid API",
"type": "main",
"index": 0
},
{
"node": "Check FERC eSubmission API",
"type": "main",
"index": 0
},
{
"node": "Check PHMSA Pipeline API",
"type": "main",
"index": 0
},
{
"node": "Check NRC Event API",
"type": "main",
"index": 0
},
{
"node": "Check State PUC Portal",
"type": "main",
"index": 0
}
]
]
},
"Check SCADA Grid API": {
"main": [
[
{
"node": "Merge Health Results",
"type": "main",
"index": 0
}
]
]
},
"Check FERC eSubmission API": {
"main": [
[
{
"node": "Merge Health Results",
"type": "main",
"index": 1
}
]
]
},
"Check PHMSA Pipeline API": {
"main": [
[
{
"node": "Merge Health Results",
"type": "main",
"index": 2
}
]
]
},
"Check NRC Event API": {
"main": [
[
{
"node": "Merge Health Results",
"type": "main",
"index": 3
}
]
]
},
"Check State PUC Portal": {
"main": [
[
{
"node": "Merge Health Results",
"type": "main",
"index": 4
}
]
]
},
"Merge Health Results": {
"main": [
[
{
"node": "Classify Issues",
"type": "main",
"index": 0
}
]
]
},
"Classify Issues": {
"main": [
[
{
"node": "Has Issues?",
"type": "main",
"index": 0
}
]
]
},
"Has Issues?": {
"main": [
[
{
"node": "Slack #grid-ops",
"type": "main",
"index": 0
},
{
"node": "Log Incident",
"type": "main",
"index": 0
}
],
[]
]
}
}
}
Workflow 4: Utility Compliance Incident Pipeline
What it does: Accepts incident events via webhook, classifies each by type and regulatory clock, routes CRITICAL incidents to #incident-command and executive email, and routes WARNING incidents to #compliance-ops — all within seconds of the webhook firing.
Fastest regulatory clocks:
| Incident Type | Clock | Regulator |
|---|---|---|
NERC_CIP_CYBER_INCIDENT_REPORTABLE |
IMMEDIATE — 1 hour max | E-ISAC portal (CIP-008-6 R4) |
NRC_10_CFR_50_72_EVENT |
8 hours | NRC Operations Center |
PHMSA_GAS_PIPELINE_INCIDENT |
1 hour telephonic | PHMSA National Response Center |
FERC_ORDER_2222_DER_NONPERFORMANCE |
24 hours | ISO/RTO per Order 2222 |
STATE_PUC_MAJOR_OUTAGE |
24–48 hours | State PUC commission |
GRID_BES_RELIABILITY_EVENT |
1 hour | E-ISAC |
EPA_CLEAN_AIR_EXCEEDANCE |
72 hours | EPA |
DATA_BREACH_UTILITY_OPS |
72 hours | State breach law |
The NERC_CIP_CYBER_INCIDENT_REPORTABLE path has zero tolerance: CIP-008-6 R4 requires E-ISAC portal submission within one hour of identifying a reportable cyber security incident. The workflow fires immediately and sets response_deadline: 'IMMEDIATE' — there is no queue, no wait node, no retry window.
{
"name": "Utility Compliance Incident Pipeline",
"nodes": [
{
"id": "w4n1",
"name": "Incident Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [
240,
300
],
"parameters": {
"path": "utility-incident",
"responseMode": "responseNode",
"responseData": "firstEntryJson"
}
},
{
"id": "w4n2",
"name": "Classify Incident & Clock",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
460,
300
],
"parameters": {
"jsCode": "const inp = $input.first().json;\nconst incidentType = inp.incident_type;\nconst responseMap = {\n NERC_CIP_CYBER_INCIDENT_REPORTABLE: {severity: 'CRITICAL', responseHours: 0, clock: 'IMMEDIATE \u2014 CIP-008-6 R4: report to E-ISAC portal within 1 hour of identification. No grace period.', channel: '#incident-command', escalateTo: 'SVP Engineering + CISO + Legal'},\n NRC_10_CFR_50_72_EVENT: {severity: 'CRITICAL', responseHours: 8, clock: '8-hour NRC Operations Center notification \u2014 10 CFR \u00a750.72(a). Clock starts at event identification.', channel: '#incident-command', escalateTo: 'VP Engineering + Nuclear Safety Officer'},\n PHMSA_GAS_PIPELINE_INCIDENT: {severity: 'CRITICAL', responseHours: 1, clock: '1-hour telephonic report to PHMSA NRC \u2014 49 CFR \u00a7191.5. Follow with written report within 30 days.', channel: '#incident-command', escalateTo: 'Pipeline Integrity Manager + Legal'},\n FERC_ORDER_2222_DER_NONPERFORMANCE: {severity: 'WARNING', responseHours: 24, clock: '24-hour report to ISO/RTO per FERC Order 2222 compliance rules.', channel: '#compliance-ops', escalateTo: 'DER Operations Lead'},\n STATE_PUC_MAJOR_OUTAGE: {severity: 'WARNING', responseHours: 24, clock: 'State PUC major outage reporting \u2014 24-48h per state commission rules.', channel: '#compliance-ops', escalateTo: 'Regulatory Affairs Manager'},\n GRID_BES_RELIABILITY_EVENT: {severity: 'CRITICAL', responseHours: 1, clock: '1-hour E-ISAC notification per NERC reliability standard requirements.', channel: '#incident-command', escalateTo: 'Grid Operations Manager + CISO'},\n EPA_CLEAN_AIR_EXCEEDANCE: {severity: 'WARNING', responseHours: 72, clock: '72-hour EPA notification per CAA \u00a7111(d) compliance requirements.', channel: '#compliance-ops', escalateTo: 'Environmental Compliance Manager'},\n DATA_BREACH_UTILITY_OPS: {severity: 'WARNING', responseHours: 72, clock: '72-hour state breach notification law. Check state-specific rules for utility customer data.', channel: '#compliance-ops', escalateTo: 'CISO + Legal'}\n};\nconst resp = responseMap[incidentType] || {severity: 'WARNING', responseHours: 24, clock: 'Unknown incident type \u2014 default 24h response.', channel: '#compliance-ops', escalateTo: 'Compliance Officer'};\nconst deadlineTs = new Date(Date.now() + resp.responseHours * 3600000).toISOString();\nreturn [{json: {\n ...inp, ...resp,\n response_deadline: resp.responseHours === 0 ? 'IMMEDIATE' : deadlineTs,\n alert_message: `[${resp.severity}] ${incidentType}: ${inp.description || 'No description'}. Regulatory clock: ${resp.clock}. Response deadline: ${resp.responseHours === 0 ? 'IMMEDIATE' : deadlineTs}. Escalate to: ${resp.escalateTo}`\n}}];"
}
},
{
"id": "w4n3",
"name": "CRITICAL or WARNING?",
"type": "n8n-nodes-base.switch",
"typeVersion": 3,
"position": [
680,
300
],
"parameters": {
"mode": "rules",
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": false
},
"conditions": [
{
"leftValue": "={{$json.severity}}",
"rightValue": "CRITICAL",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"outputKey": "CRITICAL"
},
{
"conditions": {
"options": {
"caseSensitive": false
},
"conditions": [
{
"leftValue": "={{$json.severity}}",
"rightValue": "WARNING",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"outputKey": "WARNING"
}
]
}
}
},
{
"id": "w4n4",
"name": "Slack #incident-command",
"type": "n8n-nodes-base.slack",
"typeVersion": 2,
"position": [
900,
180
],
"parameters": {
"operation": "post",
"channel": "#incident-command",
"text": "\ud83d\udea8 {{$json.alert_message}}",
"otherOptions": {}
}
},
{
"id": "w4n5",
"name": "Gmail Escalation",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2,
"position": [
900,
300
],
"parameters": {
"operation": "send",
"toList": "={{$json.escalateTo}}",
"subject": "[CRITICAL] {{$json.incident_type}} \u2014 Regulatory Clock Active",
"message": "<p><strong>{{$json.alert_message}}</strong></p><p>Incident ID: {{$json.incident_id}}</p><p>Reported at: {{$json.reported_at}}</p>"
}
},
{
"id": "w4n6",
"name": "Slack #compliance-ops",
"type": "n8n-nodes-base.slack",
"typeVersion": 2,
"position": [
900,
420
],
"parameters": {
"operation": "post",
"channel": "#compliance-ops",
"text": "\u26a0\ufe0f {{$json.alert_message}}",
"otherOptions": {}
}
},
{
"id": "w4n7",
"name": "Log Incident Sheets",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4,
"position": [
1120,
300
],
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "YOUR_SPREADSHEET_ID",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "compliance_incidents",
"mode": "name"
},
"columns": {
"mappingMode": "autoMapInputData",
"value": {},
"matchingColumns": []
},
"options": {}
}
},
{
"id": "w4n8",
"name": "Webhook Response",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
1340,
300
],
"parameters": {
"respondWith": "json",
"responseBody": "={\"received\": true, \"incident_type\": \"{{$node['Classify Incident & Clock'].json.incident_type}}\", \"severity\": \"{{$node['Classify Incident & Clock'].json.severity}}\", \"response_deadline\": \"{{$node['Classify Incident & Clock'].json.response_deadline}}\"}"
}
}
],
"connections": {
"Incident Webhook": {
"main": [
[
{
"node": "Classify Incident & Clock",
"type": "main",
"index": 0
}
]
]
},
"Classify Incident & Clock": {
"main": [
[
{
"node": "CRITICAL or WARNING?",
"type": "main",
"index": 0
}
]
]
},
"CRITICAL or WARNING?": {
"main": [
[
{
"node": "Slack #incident-command",
"type": "main",
"index": 0
},
{
"node": "Gmail Escalation",
"type": "main",
"index": 0
}
],
[
{
"node": "Slack #compliance-ops",
"type": "main",
"index": 0
}
]
]
},
"Slack #incident-command": {
"main": [
[
{
"node": "Log Incident Sheets",
"type": "main",
"index": 0
}
]
]
},
"Gmail Escalation": {
"main": [
[
{
"node": "Log Incident Sheets",
"type": "main",
"index": 0
}
]
]
},
"Slack #compliance-ops": {
"main": [
[
{
"node": "Log Incident Sheets",
"type": "main",
"index": 0
}
]
]
},
"Log Incident Sheets": {
"main": [
[
{
"node": "Webhook Response",
"type": "main",
"index": 0
}
]
]
}
}
}
Workflow 5: Weekly UtilityTech SaaS KPI Dashboard
What it does: Every Monday at 8AM, reads your platform metrics from Google Sheets, computes week-over-week deltas using $getWorkflowStaticData, builds an HTML table covering active accounts, MRR, grid MWh monitored, pipeline miles covered, and all four compliance open-item counts (NERC CIP / PHMSA / FERC / NRC), then emails the CEO and BCCs the CISO and VP Sales.
{
"name": "Weekly UtilityTech SaaS KPI Dashboard",
"nodes": [
{
"id": "w5n1",
"name": "Monday 8AM",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1,
"position": [
240,
300
],
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 8 * * 1"
}
]
}
}
},
{
"id": "w5n2",
"name": "Read Platform Metrics",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4,
"position": [
460,
300
],
"parameters": {
"operation": "read",
"documentId": {
"__rl": true,
"value": "YOUR_SPREADSHEET_ID",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "platform_metrics_weekly",
"mode": "name"
},
"options": {}
}
},
{
"id": "w5n3",
"name": "Build KPI Report",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
680,
300
],
"parameters": {
"jsCode": "const rows = $input.all().map(r => r.json);\nconst prevData = $getWorkflowStaticData('global');\nconst prev = prevData.lastWeek || {};\nconst calcDelta = (cur, p) => p ? (((cur - p) / p) * 100).toFixed(1) + '%' : 'N/A';\nconst cur = {\n active_accounts: rows.filter(r => r.account_status === 'active').length,\n mrr_usd: rows.reduce((s, r) => s + (parseFloat(r.mrr_usd) || 0), 0),\n grid_mwh_monitored: rows.reduce((s, r) => s + (parseFloat(r.grid_mwh_monitored) || 0), 0),\n pipeline_miles: rows.reduce((s, r) => s + (parseFloat(r.pipeline_miles) || 0), 0),\n nerc_cip_open: rows.filter(r => r.nerc_cip_open === 'true' || r.nerc_cip_open === true).length,\n phmsa_open: rows.filter(r => r.phmsa_open === 'true' || r.phmsa_open === true).length,\n ferc_open: rows.filter(r => r.ferc_open === 'true' || r.ferc_open === true).length,\n nrc_open: rows.filter(r => r.nrc_open === 'true' || r.nrc_open === true).length\n};\nprevData.lastWeek = cur;\n$setWorkflowStaticData('global', prevData);\nconst html = `<h2>UtilityTech SaaS \u2014 Weekly KPI Report</h2><table border='1' cellpadding='6'><tr><th>Metric</th><th>This Week</th><th>WoW Delta</th></tr><tr><td>Active Utility Accounts</td><td>${cur.active_accounts}</td><td>${calcDelta(cur.active_accounts, prev.active_accounts)}</td></tr><tr><td>MRR (USD)</td><td>$${cur.mrr_usd.toLocaleString()}</td><td>${calcDelta(cur.mrr_usd, prev.mrr_usd)}</td></tr><tr><td>Grid MWh Monitored</td><td>${cur.grid_mwh_monitored.toLocaleString()}</td><td>${calcDelta(cur.grid_mwh_monitored, prev.grid_mwh_monitored)}</td></tr><tr><td>Pipeline Miles Covered</td><td>${cur.pipeline_miles.toLocaleString()}</td><td>${calcDelta(cur.pipeline_miles, prev.pipeline_miles)}</td></tr><tr><th colspan='3'>Compliance Open Items</th></tr><tr><td>NERC CIP Open</td><td>${cur.nerc_cip_open}</td><td>\u2014</td></tr><tr><td>PHMSA Integrity Open</td><td>${cur.phmsa_open}</td><td>\u2014</td></tr><tr><td>FERC Order 2222 Open</td><td>${cur.ferc_open}</td><td>\u2014</td></tr><tr><td>NRC Reporting Open</td><td>${cur.nrc_open}</td><td>\u2014</td></tr></table>`;\nreturn [{json: {html, ...cur}}];"
}
},
{
"id": "w5n4",
"name": "Gmail to CEO + CISO BCC",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2,
"position": [
900,
300
],
"parameters": {
"operation": "send",
"toList": "ceo@yourcompany.com",
"subject": "=UtilityTech SaaS \u2014 Weekly KPI Report {{new Date().toISOString().split('T')[0]}}",
"message": "={{$json.html}}",
"options": {
"bccList": "ciso@yourcompany.com,vpsales@yourcompany.com"
}
}
},
{
"id": "w5n5",
"name": "Slack #leadership",
"type": "n8n-nodes-base.slack",
"typeVersion": 2,
"position": [
900,
480
],
"parameters": {
"operation": "post",
"channel": "#leadership",
"text": "=\ud83d\udcca Weekly UtilityTech SaaS: {{$json.active_accounts}} accounts | MRR ${{$json.mrr_usd}} | Grid {{$json.grid_mwh_monitored}} MWh | Pipeline {{$json.pipeline_miles}} mi | NERC CIP open: {{$json.nerc_cip_open}} | PHMSA: {{$json.phmsa_open}} | FERC: {{$json.ferc_open}} | NRC: {{$json.nrc_open}}",
"otherOptions": {}
}
}
],
"connections": {
"Monday 8AM": {
"main": [
[
{
"node": "Read Platform Metrics",
"type": "main",
"index": 0
}
]
]
},
"Read Platform Metrics": {
"main": [
[
{
"node": "Build KPI Report",
"type": "main",
"index": 0
}
]
]
},
"Build KPI Report": {
"main": [
[
{
"node": "Gmail to CEO + CISO BCC",
"type": "main",
"index": 0
},
{
"node": "Slack #leadership",
"type": "main",
"index": 0
}
]
]
}
}
}
Why Self-Hosted n8n Solves the NERC CIP-003-8 R2 Problem
| Regulation | Cloud iPaaS Risk | Self-Hosted n8n Solution |
|---|---|---|
| NERC CIP-003-8 R2 | BES Cyber System data in Zapier/Make = external routable connectivity = CIP violation | n8n inside OT network segment — BES data stays within your ESP |
| NRC 10 CFR §73.54 | Each cloud iPaaS vendor expands your cyber security plan attack surface | n8n inside your NRC cyber security plan perimeter |
| PHMSA 49 CFR §191.5 | Pipeline incident data with control system locations transits vendor servers | Air-gapped or on-prem n8n with direct PHMSA NRC hotline API |
| FERC Order 2222 | DER dispatch data and bid curves are market-sensitive — cloud iPaaS = discovery risk | Self-hosted n8n in ISO/RTO data exchange boundary |
| State PUC rate cases | Rate case financial models are commercially sensitive — cloud = discovery risk | Self-hosted n8n with state PUC portal direct integration |
Get All 15 Workflow Templates
The five workflows in this article are part of the FlowKit n8n Template Library — 15 production-ready workflows covering the full utility SaaS ops stack: onboarding, compliance tracking, health monitoring, incident response, and revenue reporting.
→ Download all 15 at stripeai.gumroad.com
Individual templates: $12–$29. Complete bundle: $97.
FlowKit by Alex Kane — n8n automation templates for regulated industries.
Top comments (0)