Running a CyberSecurity SaaS company — an MSSP platform, EDR vendor, SIEM provider, or vulnerability management SaaS — means two demands pulling at once: detect and respond to your customers' threats in real time, while managing your own layered compliance obligations (SOC 2, NIST CSF 2.0, ISO 27001:2022, SEC Rule 17 CFR §229.106, NY DFS Part 500) without adding headcount.
Zapier and Make.com work fine for simple apps. But when your product IS security, running your operational backbone through a multi-tenant cloud automation tool creates problems:
- Your SOC 2 auditors ask about it in CC9.2 (vendor risk review)
- Enterprise prospects flag it in procurement questionnaires
- If Zapier goes down during a material incident, you've added "automation vendor SLA" to your SEC 8-K disclosure timeline
- Your ISO 27001:2022 Annex A 8.15 audit trail requirement conflicts with Zapier's 30-day log retention
n8n solves this differently: you self-host it in your VPC, your customer data never leaves your infrastructure, every workflow is git-versionable JSON (instant audit evidence), and it handles the event volume that would bankrupt you on Zapier at scale.
Here are 5 automations built specifically for CyberSecurity SaaS vendors — not startups using security tools, but the companies building and selling them.
Workflow 1: New Enterprise Customer Onboarding Drip
The problem: New enterprise EDR/SIEM/MSSP customers need different onboarding sequences based on their size, industry, and compliance profile. A Fortune 500 financial services firm covered by NY DFS Part 500 needs a different Day 0 touchpoint than an SMB MSSP. Manually managing Day 0 / Day 3 / Day 7 sequences across 50+ enterprise accounts is untenable.
How it works: When a new customer row appears in Google Sheets (or your CRM webhook), the workflow classifies them into tiers (FORTUNE500_ENTERPRISE / ENTERPRISE_MIDMARKET / SMB_MSSP / SMB_STANDARD) and flags relevant compliance frameworks (SOC2_TYPE2_REQUIRED, NY_DFS_PART_500_APPLICABLE, SEC_CYBER_DISCLOSURE_APPLICABLE, FEDRAMP_REQUIRED). It sends a tier-appropriate Day 0 welcome email, notifies the CSM on Slack with full context, logs the onboarding event to Postgres for SOC 2 CC7.1 audit evidence, then waits 3 days for a check-in and 7 days for an activation email.
{
"name": "CyberSec SaaS - Enterprise Customer Onboarding Drip",
"nodes": [
{
"id": "1",
"name": "Google Sheets Trigger",
"type": "n8n-nodes-base.googleSheetsTrigger",
"parameters": {
"sheetId": "YOUR_SHEET_ID",
"range": "Customers!A:Z",
"event": "rowAdded"
},
"position": [
100,
300
]
},
{
"id": "2",
"name": "Classify Customer Tier",
"type": "n8n-nodes-base.code",
"parameters": {
"jsCode": "const r = $input.first().json;\nconst arr = r.arr_usd ? parseFloat(r.arr_usd) : 0;\nconst employees = r.employee_count ? parseInt(r.employee_count) : 0;\nlet tier = 'SMB_STANDARD';\nif (arr >= 500000 || employees >= 5000) tier = 'FORTUNE500_ENTERPRISE';\nelse if (arr >= 100000 || employees >= 500) tier = 'ENTERPRISE_MIDMARKET';\nelse if (r.is_mssp === 'yes') tier = 'SMB_MSSP';\nconst flags = [];\nif (r.needs_soc2 === 'yes') flags.push('SOC2_TYPE2_REQUIRED');\nif (r.nist_csf === 'yes') flags.push('NIST_CSF_REQUIRED');\nif (r.iso_27001 === 'yes') flags.push('ISO_27001_REQUIRED');\nif (r.fedramp === 'yes') flags.push('FEDRAMP_REQUIRED');\nif (r.cjis === 'yes') flags.push('CJIS_REQUIRED');\nif (r.ny_licensed === 'yes') flags.push('NY_DFS_PART_500_APPLICABLE');\nif (r.publicly_traded === 'yes') flags.push('SEC_CYBER_DISCLOSURE_APPLICABLE');\nreturn [{json: {...r, tier, compliance_flags: flags.join(',')}}];"
},
"position": [
300,
300
]
},
{
"id": "3",
"name": "Send Day 0 Welcome",
"type": "n8n-nodes-base.gmail",
"parameters": {
"to": "={{ $json.contact_email }}",
"subject": "Welcome to [Your Platform] \u2014 Security Ops Setup",
"message": "Hi {{ $json.contact_name }}, welcome aboard. Your dedicated security engineer will contact you within 4 business hours for environment scoping."
},
"position": [
500,
200
]
},
{
"id": "4",
"name": "Notify CSM on Slack",
"type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#cs-enterprise",
"text": "New customer: {{ $json.company_name }} | Tier: {{ $json.tier }} | Flags: {{ $json.compliance_flags }} | ARR: ${{ $json.arr_usd }}"
},
"position": [
500,
400
]
},
{
"id": "5",
"name": "Log Onboarding Audit",
"type": "n8n-nodes-base.postgres",
"parameters": {
"operation": "executeQuery",
"query": "INSERT INTO onboarding_log (customer_id, company_name, tier, compliance_flags, arr_usd, created_at) VALUES ('{{ $json.customer_id }}', '{{ $json.company_name }}', '{{ $json.tier }}', '{{ $json.compliance_flags }}', {{ $json.arr_usd }}, NOW()) ON CONFLICT (customer_id) DO UPDATE SET tier=EXCLUDED.tier, compliance_flags=EXCLUDED.compliance_flags, updated_at=NOW()"
},
"position": [
500,
600
]
},
{
"id": "6",
"name": "Wait 3 Days",
"type": "n8n-nodes-base.wait",
"parameters": {
"amount": 3,
"unit": "days"
},
"position": [
700,
200
]
},
{
"id": "7",
"name": "Day 3 Integration Check",
"type": "n8n-nodes-base.gmail",
"parameters": {
"to": "={{ $json.contact_email }}",
"subject": "Day 3: Integration Status Check",
"message": "Hi {{ $json.contact_name }}, checking in on your API key setup and first agent deployment. Any blockers?"
},
"position": [
900,
200
]
},
{
"id": "8",
"name": "Wait 4 More Days",
"type": "n8n-nodes-base.wait",
"parameters": {
"amount": 4,
"unit": "days"
},
"position": [
1100,
200
]
},
{
"id": "9",
"name": "Day 7 Activation Email",
"type": "n8n-nodes-base.gmail",
"parameters": {
"to": "={{ $json.contact_email }}",
"subject": "Week 1 Done \u2014 3 Detection Rules to Enable This Week",
"message": "Hi {{ $json.contact_name }}, here are three detection rules your team should activate this week based on your {{ $json.tier }} profile and compliance flags: {{ $json.compliance_flags }}."
},
"position": [
1300,
200
]
}
],
"connections": {
"Google Sheets Trigger": {
"main": [
[
{
"node": "Classify Customer Tier",
"type": "main",
"index": 0
}
]
]
},
"Classify Customer Tier": {
"main": [
[
{
"node": "Send Day 0 Welcome",
"type": "main",
"index": 0
},
{
"node": "Notify CSM on Slack",
"type": "main",
"index": 0
},
{
"node": "Log Onboarding Audit",
"type": "main",
"index": 0
}
]
]
},
"Send Day 0 Welcome": {
"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": "Wait 4 More Days",
"type": "main",
"index": 0
}
]
]
},
"Wait 4 More Days": {
"main": [
[
{
"node": "Day 7 Activation Email",
"type": "main",
"index": 0
}
]
]
}
}
}
Key compliance angle: SOC 2 CC7.1 requires that you document who has access to your system — your own customer onboarding log IS security-relevant. Storing it in a Zapier history that expires in 30 days creates a gap.
Workflow 2: EDR/SIEM/MDR API & Threat Intelligence Feed Health Monitor
The problem: Your product's uptime IS your customer's security posture. When your detection API goes down or a threat intelligence feed goes stale, you need to know before your customer does — and before it becomes a SOC 2 CC7.1 availability finding or triggers your SEC §229.106 materiality assessment clock.
How it works: Every 3 minutes, the workflow pings all monitored API endpoints from Postgres (with per-endpoint customer attribution). It classifies each response: DOWN (CRITICAL — SEC 4-day clock risk), STALE_DATA if data lag > 15 minutes (HIGH — NIST DE.CM-8 violation), or DEGRADED if response > 5 seconds (MEDIUM). A 30-minute deduplication window prevents alert storms. All incidents are logged to Postgres with regulatory context.
{
"name": "CyberSec SaaS - EDR/SIEM API & Threat Feed Health Monitor",
"nodes": [
{
"id": "1",
"name": "Every 3 Minutes",
"type": "n8n-nodes-base.scheduleTrigger",
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 3
}
]
}
},
"position": [
100,
300
]
},
{
"id": "2",
"name": "Load Monitored Endpoints",
"type": "n8n-nodes-base.postgres",
"parameters": {
"operation": "executeQuery",
"query": "SELECT endpoint_id, customer_id, endpoint_url, endpoint_type, sla_tier FROM security_endpoints WHERE active = true"
},
"position": [
300,
300
]
},
{
"id": "3",
"name": "Split Into Batches",
"type": "n8n-nodes-base.splitInBatches",
"parameters": {
"batchSize": 5
},
"position": [
500,
300
]
},
{
"id": "4",
"name": "Ping Each Endpoint",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "={{ $json.endpoint_url }}/health",
"method": "GET",
"timeout": 10000,
"continueOnFail": true
},
"position": [
700,
300
]
},
{
"id": "5",
"name": "Evaluate Status",
"type": "n8n-nodes-base.code",
"parameters": {
"jsCode": "const r = $input.first().json;\nconst now = Date.now();\nlet status = 'OK';\nlet severity = null;\nlet sec_angle = null;\nif (r.error || r.statusCode >= 500) {\n status = 'DOWN';\n severity = 'CRITICAL';\n sec_angle = 'SEC_229_106_4DAY_CLOCK_RISK: product downtime during material incident blocks 8-K disclosure timeline';\n} else if (r.data_lag_seconds && r.data_lag_seconds > 900) {\n status = 'STALE_DATA';\n severity = 'HIGH';\n sec_angle = 'NIST_DE_CM8_VIOLATION: threat feed staleness exceeds 15min continuous monitoring baseline';\n} else if (r.response_ms && r.response_ms > 5000) {\n status = 'DEGRADED';\n severity = 'MEDIUM';\n sec_angle = 'NIST_DE_CM7_ALERT: API latency indicates capacity issue';\n}\nconst stateKey = r.endpoint_id + '_status';\nconst lastAlerted = $getWorkflowStaticData('global')[stateKey + '_alerted_at'] || 0;\nconst dedup_ok = status !== 'OK' && (now - lastAlerted) > 1800000;\nif (dedup_ok) {\n $getWorkflowStaticData('global')[stateKey + '_alerted_at'] = now;\n}\nreturn [{json: {...r, status, severity, sec_angle, should_alert: dedup_ok && status !== 'OK'}}];"
},
"position": [
900,
300
]
},
{
"id": "6",
"name": "Filter Alerts Only",
"type": "n8n-nodes-base.filter",
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $json.should_alert }}",
"value2": true
}
]
}
},
"position": [
1100,
300
]
},
{
"id": "7",
"name": "Slack Security Ops Alert",
"type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#cybersec-ops",
"text": "\ud83d\udd34 *{{ $json.status }}* | {{ $json.endpoint_url }} | Customer: {{ $json.customer_id }} | {{ $json.sec_angle }}"
},
"position": [
1300,
200
]
},
{
"id": "8",
"name": "Log Incident to Postgres",
"type": "n8n-nodes-base.postgres",
"parameters": {
"operation": "executeQuery",
"query": "INSERT INTO endpoint_incidents (endpoint_id, customer_id, status, severity, sec_angle, detected_at) VALUES ('{{ $json.endpoint_id }}', '{{ $json.customer_id }}', '{{ $json.status }}', '{{ $json.severity }}', '{{ $json.sec_angle }}', NOW()) ON CONFLICT DO NOTHING"
},
"position": [
1300,
400
]
}
],
"connections": {
"Every 3 Minutes": {
"main": [
[
{
"node": "Load Monitored Endpoints",
"type": "main",
"index": 0
}
]
]
},
"Load Monitored Endpoints": {
"main": [
[
{
"node": "Split Into Batches",
"type": "main",
"index": 0
}
]
]
},
"Split Into Batches": {
"main": [
[
{
"node": "Ping Each Endpoint",
"type": "main",
"index": 0
}
]
]
},
"Ping Each Endpoint": {
"main": [
[
{
"node": "Evaluate Status",
"type": "main",
"index": 0
}
]
]
},
"Evaluate Status": {
"main": [
[
{
"node": "Filter Alerts Only",
"type": "main",
"index": 0
}
]
]
},
"Filter Alerts Only": {
"main": [
[
{
"node": "Slack Security Ops Alert",
"type": "main",
"index": 0
},
{
"node": "Log Incident to Postgres",
"type": "main",
"index": 0
}
]
]
}
}
}
Key compliance angle: NIST CSF 2.0 DE.CM-08 requires continuous monitoring of your own detection capabilities. If your threat feed goes stale and a customer gets breached during that window, your SEC §229.106 materiality determination timeline starts from when you should have known — not when you noticed.
Workflow 3: SOC 2 / NIST CSF / ISO 27001 / SEC / NY DFS Compliance Deadline Tracker
The problem: CyberSecurity SaaS vendors face one of the most complex compliance stacks of any software company: SOC 2 Type II annual audit + evidence collection windows, NIST CSF 2.0 risk assessments (new GOVERN function in 2024), ISO 27001:2022 surveillance audits, SEC Rule 17 CFR §229.106 4-day Form 8-K disclosure + annual 10-K updates, NY DFS Part 500 §500.17 72-hour incident notice + annual certification, and GDPR/CCPA. Miss one, and you lose an enterprise deal or face an enforcement action.
How it works: Every weekday at 8AM, the workflow reads your compliance calendar from Google Sheets and scores each deadline: OVERDUE, CRITICAL (≤7 days), URGENT (≤21 days), WARNING (≤45 days), NOTICE (≤90 days). It maps 14 deadline types to specific action language (e.g., SEC_8K_CYBER_DISCLOSURE → "4-day Form 8-K disclosure window: verify no material incidents unreported"), routes by urgency, and notifies the right people via Slack and email. A 4-hour deduplication prevents duplicate daily alerts.
{
"name": "CyberSec SaaS - SOC2/NIST/ISO/SEC/NYDFS Compliance Deadline Tracker",
"nodes": [
{
"id": "1",
"name": "Weekdays 8AM",
"type": "n8n-nodes-base.scheduleTrigger",
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 8 * * 1-5"
}
]
}
},
"position": [
100,
300
]
},
{
"id": "2",
"name": "Load Compliance Calendar",
"type": "n8n-nodes-base.googleSheets",
"parameters": {
"operation": "getAll",
"documentId": {
"value": "YOUR_SHEET_ID"
},
"sheetName": {
"value": "ComplianceCalendar"
}
},
"position": [
300,
300
]
},
{
"id": "3",
"name": "Score Deadlines",
"type": "n8n-nodes-base.code",
"parameters": {
"jsCode": "const items = $input.all();\nconst now = new Date();\nconst actionMap = {\n 'SOC2_TYPE2_AUDIT': 'SOC 2 Type II annual audit \u2014 schedule with Big 4 auditor 90 days out',\n 'SOC2_EVIDENCE_COLLECTION': 'SOC 2 evidence collection window open \u2014 pull access logs, change control records, vendor reviews',\n 'SOC2_PENETRATION_TEST': 'Annual pentest required before SOC 2 renewal \u2014 engage approved tester',\n 'SOC2_VENDOR_REVIEW': 'Annual sub-processor risk review required per SOC 2 CC9.2',\n 'NIST_CSF_RISK_ASSESSMENT': 'NIST CSF 2.0 GV.RM-03 annual risk assessment due',\n 'NIST_CSF_IR_TEST': 'NIST CSF 2.0 RS.RP-01 incident response tabletop exercise due',\n 'ISO_27001_INTERNAL_AUDIT': 'ISO 27001:2022 Clause 9.2 internal audit \u2014 assign internal auditor',\n 'ISO_27001_SURVEILLANCE_AUDIT': 'ISO 27001:2022 surveillance audit by certification body due',\n 'SEC_8K_CYBER_DISCLOSURE': 'SEC 17 CFR \u00a7229.106(c) \u2014 4-day Form 8-K disclosure window: verify no material incidents unreported',\n 'SEC_10K_CYBER_RISK_UPDATE': 'SEC \u00a7229.106(b) annual 10-K cybersecurity risk factor update due',\n 'NY_DFS_500_17_CERT': 'NY DFS Part 500 \u00a7500.17 annual certification of compliance due Feb 15',\n 'NY_DFS_500_17_INCIDENT_REVIEW': 'NY DFS Part 500 \u00a7500.17(a) 72h incident notice log quarterly review',\n 'GDPR_ART32_SECURITY_REVIEW': 'GDPR Art.32 technical/organizational measures annual review',\n 'GDPR_DSR_30DAY_AUDIT': 'GDPR Art.17 DSR 30-day response window compliance audit',\n 'CCPA_ANNUAL_SECURITY_AUDIT': 'CPRA annual security audit and data map update required',\n};\nconst urgency = (dueDate) => {\n const days = Math.ceil((new Date(dueDate) - now) / 86400000);\n if (days < 0) return {level: 'OVERDUE', days};\n if (days <= 7) return {level: 'CRITICAL', days};\n if (days <= 21) return {level: 'URGENT', days};\n if (days <= 45) return {level: 'WARNING', days};\n if (days <= 90) return {level: 'NOTICE', days};\n return null;\n};\nconst now_ts = now.getTime();\nconst lastAlerted = $getWorkflowStaticData('global')['compliance_alerted_at'] || 0;\nif ((now_ts - lastAlerted) < 14400000) return [];\n$getWorkflowStaticData('global')['compliance_alerted_at'] = now_ts;\nreturn items.map(i => {\n const u = urgency(i.json.due_date);\n if (!u) return null;\n return {json: {...i.json, ...u, action: actionMap[i.json.deadline_type] || i.json.deadline_type}};\n}).filter(Boolean);"
},
"position": [
500,
300
]
},
{
"id": "4",
"name": "Route By Urgency",
"type": "n8n-nodes-base.switch",
"parameters": {
"dataPropertyName": "level",
"rules": {
"values": [
{
"value": "OVERDUE",
"outputKey": "0"
},
{
"value": "CRITICAL",
"outputKey": "1"
},
{
"value": "URGENT",
"outputKey": "2"
},
{
"value": "WARNING",
"outputKey": "3"
}
]
}
},
"position": [
700,
300
]
},
{
"id": "5",
"name": "Slack Urgent",
"type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#compliance-critical",
"text": "<!here> *{{ $json.level }}* ({{ $json.days }}d) | {{ $json.deadline_type }} | {{ $json.action }}"
},
"position": [
900,
100
]
},
{
"id": "6",
"name": "Gmail Compliance Officer",
"type": "n8n-nodes-base.gmail",
"parameters": {
"to": "compliance@yourcompany.com",
"cc": "ciso@yourcompany.com",
"subject": "[{{ $json.level }}] {{ $json.deadline_type }} \u2014 {{ $json.days }} days",
"message": "Action required: {{ $json.action }}\n\nDue date: {{ $json.due_date }}\nOwner: {{ $json.owner }}"
},
"position": [
900,
300
]
}
],
"connections": {
"Weekdays 8AM": {
"main": [
[
{
"node": "Load Compliance Calendar",
"type": "main",
"index": 0
}
]
]
},
"Load Compliance Calendar": {
"main": [
[
{
"node": "Score Deadlines",
"type": "main",
"index": 0
}
]
]
},
"Score Deadlines": {
"main": [
[
{
"node": "Route By Urgency",
"type": "main",
"index": 0
}
]
]
},
"Route By Urgency": {
"main": [
[
{
"node": "Slack Urgent",
"type": "main",
"index": 0
}
],
[
{
"node": "Slack Urgent",
"type": "main",
"index": 0
}
],
[
{
"node": "Gmail Compliance Officer",
"type": "main",
"index": 0
}
],
[
{
"node": "Gmail Compliance Officer",
"type": "main",
"index": 0
}
]
]
}
}
}
Key compliance angle: NY DFS Part 500 §500.17 annual certification is due February 15 each year. Most CyberSecurity SaaS companies are "covered persons" if they operate in New York or hold nonpublic information of NY-licensed entities — but many miss this because they focus on SOC 2 and forget NY DFS. This workflow catches it.
Workflow 4: Cyber Incident & SEC / NY DFS Breach Alert Pipeline
The problem: When a material cybersecurity incident hits — ransomware, data exfiltration, unauthorized access — your team needs to act within hours, not days. SEC Rule 17 CFR §229.106(c) requires Form 8-K disclosure within 4 business days of determining materiality. NY DFS Part 500 §500.17(a) requires DFS notice within 72 hours. GDPR Art. 33 requires supervisory authority notification within 72 hours. Missing any of these windows means enforcement exposure.
How it works: A webhook receives incident reports from your SIEM, EDR, or internal tools. The Code node classifies each incident type (MATERIAL_CYBERSECURITY_INCIDENT, NY_DFS_MATERIAL_EVENT, RANSOMWARE_DETECTED, DATA_EXFILTRATION, UNAUTHORIZED_ACCESS, THREAT_INTELLIGENCE_IOC_MATCH, SUPPLY_CHAIN_COMPROMISE) with severity, response window in hours, and the specific regulatory citation. It calculates the exact deadline timestamp and routes via Slack @channel with pre-populated regulatory context. A 30-minute per-incident dedup prevents duplicate alerts.
{
"name": "CyberSec SaaS - Cyber Incident & SEC/NYDFS Breach Alert Pipeline",
"nodes": [
{
"id": "1",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"parameters": {
"path": "cybersec-incident",
"method": "POST",
"responseMode": "responseNode"
},
"position": [
100,
300
]
},
{
"id": "2",
"name": "Classify Incident",
"type": "n8n-nodes-base.code",
"parameters": {
"jsCode": "const r = $input.first().json.body || $input.first().json;\nconst type = r.incident_type || 'UNKNOWN';\nconst classMap = {\n 'MATERIAL_CYBERSECURITY_INCIDENT': {severity: 'CRITICAL', window_hours: 96, reg: 'SEC 17 CFR \u00a7229.106(c) \u2014 4-day Form 8-K disclosure from materiality determination'},\n 'NY_DFS_MATERIAL_EVENT': {severity: 'CRITICAL', window_hours: 72, reg: 'NY DFS Part 500 \u00a7500.17(a) \u2014 72h notice to DFS Superintendent'},\n 'RANSOMWARE_DETECTED': {severity: 'CRITICAL', window_hours: 1, reg: 'Internal SLA: 1h containment + SEC materiality assessment required within 24h'},\n 'DATA_EXFILTRATION': {severity: 'CRITICAL', window_hours: 2, reg: 'GDPR Art.33 \u2014 72h supervisory authority notification; SEC materiality assessment within 24h'},\n 'UNAUTHORIZED_ACCESS': {severity: 'HIGH', window_hours: 24, reg: 'SOC 2 CC7.3 \u2014 document unauthorized access; ISO 27001:2022 \u00a76.1.2 residual risk update'},\n 'THREAT_INTELLIGENCE_IOC_MATCH': {severity: 'MEDIUM', window_hours: 4, reg: 'NIST CSF 2.0 DE.CM-01 \u2014 threat intelligence match requires investigation within 4h'},\n 'SUPPLY_CHAIN_COMPROMISE': {severity: 'CRITICAL', window_hours: 2, reg: 'NIST CSF 2.0 GV.SC-07 \u2014 supply chain incident; SEC \u00a7229.106 supply chain risk disclosure may apply'},\n};\nconst c = classMap[type] || {severity: 'MEDIUM', window_hours: 24, reg: 'Review per internal IR policy'};\nconst incident_id = r.incident_id || ('INC-' + Date.now());\nconst stateKey = 'incident_' + incident_id;\nconst now = Date.now();\nconst lastAlerted = $getWorkflowStaticData('global')[stateKey] || 0;\nconst dedup_ok = (now - lastAlerted) > 1800000;\nif (dedup_ok) $getWorkflowStaticData('global')[stateKey] = now;\nconst deadline = new Date(now + c.window_hours * 3600000).toISOString();\nreturn [{json: {...r, ...c, incident_id, deadline_iso: deadline, should_alert: dedup_ok}}];"
},
"position": [
300,
300
]
},
{
"id": "3",
"name": "Filter Dedup",
"type": "n8n-nodes-base.filter",
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $json.should_alert }}",
"value2": true
}
]
}
},
"position": [
500,
300
]
},
{
"id": "4",
"name": "Slack Channel Alert",
"type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#security-incident-response",
"text": "<!channel> *{{ $json.severity }}* | {{ $json.incident_type }} | ID: {{ $json.incident_id }}\nDeadline: {{ $json.deadline_iso }}\nRegulatory: {{ $json.reg }}"
},
"position": [
700,
200
]
},
{
"id": "5",
"name": "Log to Postgres",
"type": "n8n-nodes-base.postgres",
"parameters": {
"operation": "executeQuery",
"query": "INSERT INTO incident_log (incident_id, incident_type, severity, window_hours, reg, deadline_iso, detected_at) VALUES ('{{ $json.incident_id }}', '{{ $json.incident_type }}', '{{ $json.severity }}', {{ $json.window_hours }}, '{{ $json.reg }}', '{{ $json.deadline_iso }}', NOW()) ON CONFLICT DO NOTHING"
},
"position": [
700,
400
]
},
{
"id": "6",
"name": "Respond 200",
"type": "n8n-nodes-base.respondToWebhook",
"parameters": {
"responseCode": 200,
"responseBody": "{\"status\":\"received\",\"incident_id\":\"{{ $json.incident_id }}\"}"
},
"position": [
900,
300
]
}
],
"connections": {
"Webhook": {
"main": [
[
{
"node": "Classify Incident",
"type": "main",
"index": 0
}
]
]
},
"Classify Incident": {
"main": [
[
{
"node": "Filter Dedup",
"type": "main",
"index": 0
}
]
]
},
"Filter Dedup": {
"main": [
[
{
"node": "Slack Channel Alert",
"type": "main",
"index": 0
},
{
"node": "Log to Postgres",
"type": "main",
"index": 0
}
]
]
},
"Slack Channel Alert": {
"main": [
[
{
"node": "Respond 200",
"type": "main",
"index": 0
}
]
]
}
}
}
Key compliance angle: The SEC rule 17 CFR §229.106 irony for CyberSecurity SaaS vendors: you sell security products to enterprises, and your Form 8-K disclosure accuracy depends partly on your incident response automation being self-hosted — because if Zapier is down during your material incident, you've introduced a single point of failure into your 4-day compliance window.
Workflow 5: Weekly CyberSecurity Platform KPI Dashboard
The problem: Your CEO, CISO, and CFO need a weekly pulse on detection rates, customer health, churn signals, and ARR movement — but pulling this from your SIEM telemetry database, CRM, and billing platform takes 2+ hours manually.
How it works: Every Monday at 8AM, the workflow runs two parallel Postgres queries — one for platform metrics (total ARR, detection rate, enterprise count, at-risk customers) and one for customer health (new signups, churns, new ARR). It merges the results, calculates week-over-week ARR change using $getWorkflowStaticData for persistence, color-codes the detection rate (green ≥99.9%, orange ≥99.0%, red below), and emails an HTML report to CEO/CISO with CFO BCC, plus a one-liner to Slack #exec-kpis.
{
"name": "CyberSec SaaS - Weekly Platform KPI Dashboard",
"nodes": [
{
"id": "1",
"name": "Monday 8AM",
"type": "n8n-nodes-base.scheduleTrigger",
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 8 * * 1"
}
]
}
},
"position": [
100,
300
]
},
{
"id": "2",
"name": "Fetch Platform Metrics",
"type": "n8n-nodes-base.postgres",
"parameters": {
"operation": "executeQuery",
"query": "SELECT COUNT(*) AS total_customers, COUNT(*) FILTER (WHERE tier='FORTUNE500_ENTERPRISE') AS enterprise_count, AVG(detection_rate_pct) AS avg_detection_rate, SUM(arr_usd) AS total_arr, COUNT(*) FILTER (WHERE health_score < 50) AS at_risk_count FROM platform_metrics WHERE week_start = date_trunc('week', NOW()) - interval '7 days'"
},
"position": [
300,
200
]
},
{
"id": "3",
"name": "Fetch Customer Health",
"type": "n8n-nodes-base.postgres",
"parameters": {
"operation": "executeQuery",
"query": "SELECT COUNT(*) AS new_customers, COUNT(*) FILTER (WHERE churned_at >= NOW() - interval '7 days') AS churned_count, SUM(arr_usd) FILTER (WHERE created_at >= NOW() - interval '7 days') AS new_arr FROM customers WHERE active = true"
},
"position": [
300,
400
]
},
{
"id": "4",
"name": "Merge",
"type": "n8n-nodes-base.merge",
"parameters": {
"mode": "multiplex"
},
"position": [
500,
300
]
},
{
"id": "5",
"name": "Build KPI Report",
"type": "n8n-nodes-base.code",
"parameters": {
"jsCode": "const items = $input.all();\nconst m = items[0]?.json || {};\nconst h = items[1]?.json || {};\nconst prevArr = $getWorkflowStaticData('global')['prev_total_arr'] || m.total_arr || 1;\nconst arrWoW = (((m.total_arr - prevArr) / prevArr) * 100).toFixed(1);\n$getWorkflowStaticData('global')['prev_total_arr'] = m.total_arr;\nconst detRate = parseFloat(m.avg_detection_rate || 0);\nconst detColor = detRate >= 99.9 ? '#00aa44' : detRate >= 99.0 ? '#ff9900' : '#cc0000';\nconst html = [\n '<h2>Weekly CyberSecurity Platform KPI Report</h2>',\n '<table border=\"1\" cellpadding=\"6\" style=\"border-collapse:collapse;font-family:monospace\">',\n '<tr><th>Metric</th><th>Value</th><th>WoW</th></tr>',\n '<tr><td>Total ARR</td><td>$' + Number(m.total_arr||0).toLocaleString() + '</td><td>' + arrWoW + '%</td></tr>',\n '<tr><td>New Customers</td><td>' + (h.new_customers||0) + '</td><td>\u2014</td></tr>',\n '<tr><td>Churned</td><td>' + (h.churned_count||0) + '</td><td>\u2014</td></tr>',\n '<tr><td>Enterprise Customers</td><td>' + (m.enterprise_count||0) + '</td><td>\u2014</td></tr>',\n '<tr><td>Avg Detection Rate</td><td style=\"color:' + detColor + '\">' + detRate.toFixed(2) + '%</td><td>\u2014</td></tr>',\n '<tr><td>At-Risk Customers</td><td>' + (m.at_risk_count||0) + '</td><td>\u2014</td></tr>',\n '</table>'\n].join('');\nreturn [{json: {html, total_arr: m.total_arr, arr_wow: arrWoW, at_risk_count: m.at_risk_count, detection_rate: detRate}}];"
},
"position": [
700,
300
]
},
{
"id": "6",
"name": "Gmail to Leadership",
"type": "n8n-nodes-base.gmail",
"parameters": {
"to": "ceo@yourcompany.com",
"cc": "ciso@yourcompany.com",
"bcc": "cfo@yourcompany.com",
"subject": "Weekly Security Platform KPI \u2014 ARR ${{ $json.total_arr | number }} | Detection {{ $json.detection_rate }}%",
"message": "={{ $json.html }}",
"options": {
"bodyContentType": "html"
}
},
"position": [
900,
200
]
},
{
"id": "7",
"name": "Slack Exec Summary",
"type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#exec-kpis",
"text": "Weekly KPI | ARR: ${{ $json.total_arr }} ({{ $json.arr_wow }}% WoW) | Detection: {{ $json.detection_rate }}% | At-Risk: {{ $json.at_risk_count }} customers"
},
"position": [
900,
400
]
}
],
"connections": {
"Monday 8AM": {
"main": [
[
{
"node": "Fetch Platform Metrics",
"type": "main",
"index": 0
},
{
"node": "Fetch Customer Health",
"type": "main",
"index": 0
}
]
]
},
"Fetch Platform Metrics": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"Fetch Customer Health": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"Merge": {
"main": [
[
{
"node": "Build KPI Report",
"type": "main",
"index": 0
}
]
]
},
"Build KPI Report": {
"main": [
[
{
"node": "Gmail to Leadership",
"type": "main",
"index": 0
},
{
"node": "Slack Exec Summary",
"type": "main",
"index": 0
}
]
]
}
}
}
Why n8n vs Zapier or Make.com for CyberSecurity SaaS?
| Factor | n8n (self-hosted) | Zapier | Make.com |
|---|---|---|---|
| SOC 2 CC9.2 vendor risk | ✅ You own it — no sub-processor | ❌ Sub-processor in your audit scope | ❌ Sub-processor in your audit scope |
| ISO 27001:2022 Annex A 8.15 audit trail | ✅ Git-versioned JSON, custom retention | ❌ 30-day log limit | ❌ 90-day log limit |
| SEC §229.106 incident disclosure chain | ✅ Self-hosted = not in your disclosure narrative | ❌ SaaS dependency adds to disclosure scope | ❌ SaaS dependency adds to disclosure scope |
| NY DFS Part 500 §500.17 72h notice | ✅ No external dependency in response chain | ❌ Zapier downtime = response delay | ❌ Make.com downtime = response delay |
| NIST CSF 2.0 GV.SC-07 supply chain | ✅ No third-party automation in security chain | ❌ Zapier is a supply chain dependency | ❌ Make.com is a supply chain dependency |
| Volume cost at 100M events/mo | ✅ ~$600/mo VPS | ❌ ~$500K+/mo | ❌ ~$300K+/mo |
| Workflow as code / change control | ✅ JSON in git = instant audit evidence | ❌ GUI clicks, no git trail | ❌ GUI clicks, no git trail |
Get All 5 Workflows Pre-Built
These are available as import-ready n8n JSON files in the FlowKit store — drop them into your n8n instance and configure your credentials.
The CyberSecurity SaaS bundle includes all 5 workflows above plus the full n8n template library (141 workflows across verticals).
If you're building on n8n for your security platform, these workflows give you a compliance-ready operational foundation without building from scratch.
Top comments (0)