If your company runs a CDP, marketing automation platform, or email marketing SaaS, you have a data sovereignty problem that Zapier and Make.com cannot solve.
Contact behavioral data, consent records, campaign engagement signals, and suppression lists cannot legally flow through a third-party cloud automation platform under GDPR Art.28, CCPA Cal.Civ.Code §1798.140(ah)(1), CAN-SPAM §7704, TCPA 47 U.S.C. §227, and an expanding stack of US state privacy laws.
n8n runs entirely on your infrastructure. No event data leaves your network. Every workflow is git-versionable, environment-isolated, and auditable by your DPO or CISO at any time.
Here are 5 production automations for MarTech SaaS companies — with import-ready workflow JSON.
1. New Marketing Brand Client Onboarding Drip
Who needs it: Any MarTech SaaS — CDPs, email platforms, marketing automation tools — that onboards brands at multiple tier levels.
What it does: When a new client row is added to your Sheets CRM, classify by tier (enterprise CDP, mid-market, SMB, starter), route to the right CSM channel, send tier-appropriate Day 0 emails, flag GDPR/CCPA DPA requirements, and log to SOC 2 audit sheet — automatically.
Compliance relevance: GDPR Art.28 sub-processor DPA (required before processing EU personal data for a client), CCPA service provider agreement (required before processing CA consumer data), SOC 2 CC7.1 onboarding documentation.
Tier logic:
-
ENTERPRISE_CDP(≥10M contacts or ≥$500K ARR): dedicated CSM, GDPR/CCPA DPA flag -
MID_MARKET_MARTECH(≥1M contacts or ≥$100K ARR): shared CSM channel, compliance review -
SMB_MARKETING/STARTER: self-serve onboarding email
{
"name": "New Marketing Brand Client Onboarding Drip",
"nodes": [
{
"parameters": {
"event": "rowAdded",
"sheetId": "YOUR_CLIENTS_SHEET_ID",
"range": "Clients!A:H",
"options": {}
},
"name": "New Client Row Trigger",
"type": "n8n-nodes-base.googleSheetsTrigger",
"typeVersion": 4,
"position": [
250,
300
],
"id": "trg1"
},
{
"parameters": {
"jsCode": "const d=$json;\nconst contacts=parseInt(d.total_contacts||0);\nconst arr=parseFloat(d.arr_usd||0);\nlet tier;\nif(contacts>=10000000||arr>=500000)tier='ENTERPRISE_CDP';\nelse if(contacts>=1000000||arr>=100000)tier='MID_MARKET_MARTECH';\nelse if(contacts>=100000||arr>=10000)tier='SMB_MARKETING';\nelse tier='STARTER';\nconst requiresGdprDpa=(d.eu_customers||'').toLowerCase()==='yes';\nconst requiresCcpaDpa=(d.ca_customers||'').toLowerCase()==='yes';\nreturn[{json:{...d,tier,requiresGdprDpa,requiresCcpaDpa,\n csmChannel:tier==='ENTERPRISE_CDP'?'#csm-enterprise':tier==='MID_MARKET_MARTECH'?'#csm-midmarket':'#csm-smb'}}];"
},
"name": "Classify Client Tier",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
450,
300
],
"id": "cls1"
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"string": [
{
"value1": "={{$json.tier}}",
"operation": "equal",
"value2": "ENTERPRISE_CDP"
}
]
},
"renameOutput": true,
"outputKey": "ENTERPRISE"
},
{
"conditions": {
"string": [
{
"value1": "={{$json.tier}}",
"operation": "equal",
"value2": "MID_MARKET_MARTECH"
}
]
},
"renameOutput": true,
"outputKey": "MID_MARKET"
},
{
"conditions": {
"string": [
{
"value1": "={{$json.tier}}",
"operation": "startsWith",
"value2": "SMB"
}
]
},
"renameOutput": true,
"outputKey": "SMB"
}
]
}
},
"name": "Route by Tier",
"type": "n8n-nodes-base.switch",
"typeVersion": 2,
"position": [
650,
300
],
"id": "sw1"
},
{
"parameters": {
"toEmail": "={{$json.primary_contact_email}}",
"subject": "Welcome to [Platform] \u2014 your enterprise onboarding starts now",
"message": "Hi {{$json.primary_contact_name}},\n\nWelcome to [Platform]. Your dedicated CSM will reach out within 2 hours to schedule your kickoff call.\n\nYour account includes:\n- Dedicated enterprise support channel\n- Custom data ingestion setup\n- GDPR/CCPA compliance review\n- SLA: 99.99% uptime with 1-hour response\n\nIn the meantime, here is your enterprise onboarding guide:\nhttps://docs.yourplatform.com/enterprise-onboarding\n\nLooking forward to working with you.\n\n\u2014 Customer Success Team"
},
"name": "Gmail Enterprise Day0",
"type": "n8n-nodes-base.gmail",
"typeVersion": 1,
"position": [
850,
160
],
"id": "gm1a"
},
{
"parameters": {
"toEmail": "={{$json.primary_contact_email}}",
"subject": "Get started with [Platform] \u2014 3 quick wins for your first week",
"message": "Hi {{$json.primary_contact_name}},\n\nWelcome to [Platform]! Here are 3 quick wins to get started:\n\n1. Connect your first data source (5 min): https://app.yourplatform.com/sources\n2. Create your first segment: https://app.yourplatform.com/segments\n3. Launch your first campaign: https://app.yourplatform.com/campaigns\n\nNeed help? Book a 30-min setup call: https://calendly.com/yourplatform/setup\n\n\u2014 [Platform] Team"
},
"name": "Gmail SMB Day0",
"type": "n8n-nodes-base.gmail",
"typeVersion": 1,
"position": [
850,
420
],
"id": "gm1b"
},
{
"parameters": {
"channel": "={{$json.csmChannel}}",
"text": ":rocket: New client onboarded: *{{$json.company_name}}* ({{$json.tier}})\nContacts: {{$json.total_contacts}} | ARR: ${{$json.arr_usd}}\nPrimary contact: {{$json.primary_contact_name}} <{{$json.primary_contact_email}}>\nEU customers: {{$json.eu_customers}} | CA customers: {{$json.ca_customers}}\n{{$json.requiresGdprDpa ? ':warning: GDPR DPA required (Art.28)' : ''}}\n{{$json.requiresCcpaDpa ? ':warning: CCPA service provider agreement required' : ''}}"
},
"name": "Slack CSM Alert",
"type": "n8n-nodes-base.slack",
"typeVersion": 1,
"position": [
1050,
300
],
"id": "sl1"
},
{
"parameters": {
"operation": "append",
"sheetId": "YOUR_AUDIT_SHEET_ID",
"range": "OnboardingLog!A:K",
"options": {},
"dataMode": "autoMapInputData"
},
"name": "SOC2 Audit Log",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4,
"position": [
1250,
300
],
"id": "gs1"
}
],
"connections": {
"New Client Row Trigger": {
"main": [
[
{
"node": "Classify Client Tier",
"type": "main",
"index": 0
}
]
]
},
"Classify Client Tier": {
"main": [
[
{
"node": "Route by Tier",
"type": "main",
"index": 0
}
]
]
},
"Route by Tier": {
"ENTERPRISE": [
[
{
"node": "Gmail Enterprise Day0",
"type": "main",
"index": 0
}
]
],
"MID_MARKET": [
[
{
"node": "Gmail SMB Day0",
"type": "main",
"index": 0
}
]
],
"SMB": [
[
{
"node": "Gmail SMB Day0",
"type": "main",
"index": 0
}
]
]
},
"Gmail Enterprise Day0": {
"main": [
[
{
"node": "Slack CSM Alert",
"type": "main",
"index": 0
}
]
]
},
"Gmail SMB Day0": {
"main": [
[
{
"node": "Slack CSM Alert",
"type": "main",
"index": 0
}
]
]
},
"Slack CSM Alert": {
"main": [
[
{
"node": "SOC2 Audit Log",
"type": "main",
"index": 0
}
]
]
}
}
}
2. Campaign API & Event Pipeline Health Monitor
Who needs it: Any MarTech platform with inbound event webhooks, outbound campaign APIs, or real-time segmentation pipelines.
What it does: Every 3 minutes, ping all your campaign API endpoints stored in Postgres. Classify DOWN/STALE_DATA/DEGRADED states, suppress repeat alerts with $getWorkflowStaticData (30-minute dedup window), alert #platform-ops, and write a SOC 2-compliant incident log.
Why self-hosted n8n matters here: Zapier/Make webhooks route event data through their cloud infrastructure. For a CDP processing EU behavioral signals, that's GDPR Art.28 territory — every Zapier task log entry containing user IDs or behavioral data requires a documented DPA and is scoped into your GDPR record of processing activities.
{
"name": "Campaign API & Event Pipeline Health Monitor",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 3
}
]
}
},
"name": "Every 3 Minutes",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1,
"position": [
250,
300
],
"id": "sc2"
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT endpoint_name, endpoint_url, endpoint_type, team FROM campaign_endpoints WHERE active = true ORDER BY endpoint_name"
},
"name": "Load Endpoints",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
450,
300
],
"id": "pg2"
},
{
"parameters": {
"url": "={{$json.endpoint_url}}",
"options": {
"timeout": 8000,
"response": {
"response": {
"responseFormat": "text"
}
}
}
},
"name": "HTTP Ping",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
650,
300
],
"id": "http2"
},
{
"parameters": {
"jsCode": "const items=[];\nfor(const item of $input.all()){\n const d=item.json;\n const status=d.$response?.statusCode||d.statusCode||0;\n const latencyMs=d.$response?.responseTime||0;\n const lastDataAge=d.last_event_age_minutes||0;\n let health;\n if(status===0||status>=500)health='DOWN';\n else if(lastDataAge>=15)health='STALE_DATA';\n else if(latencyMs>=3000||status>=400)health='DEGRADED';\n else health='OK';\n if(health!=='OK'){\n const state=$getWorkflowStaticData('global');\n const key=`alert_${d.endpoint_name}`;\n const now=Date.now();\n const lastAlert=state[key]||0;\n if(now-lastAlert>1800000){\n state[key]=now;\n items.push({json:{...d,health,status,latencyMs,alertedAt:new Date().toISOString()}});\n }\n }\n}\nreturn items.length?items:[{json:{__all_healthy:true}}];"
},
"name": "Classify Health",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
850,
300
],
"id": "cls2"
},
{
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{!$json.__all_healthy}}",
"value2": true
}
]
}
},
"name": "Has Issues?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
1050,
300
],
"id": "if2"
},
{
"parameters": {
"channel": "#platform-ops",
"text": ":rotating_light: *Campaign API {{$json.health}}*\nEndpoint: {{$json.endpoint_name}} ({{$json.endpoint_type}})\nURL: {{$json.endpoint_url}}\nStatus: {{$json.status}} | Latency: {{$json.latencyMs}}ms\nTeam: @{{$json.team}} \u2014 investigate immediately"
},
"name": "Slack Platform Ops",
"type": "n8n-nodes-base.slack",
"typeVersion": 1,
"position": [
1250,
200
],
"id": "sl2"
},
{
"parameters": {
"operation": "executeQuery",
"query": "INSERT INTO campaign_incidents (endpoint_name, health_status, http_status, latency_ms, alerted_at) VALUES ('{{$json.endpoint_name}}', '{{$json.health}}', {{$json.status}}, {{$json.latencyMs}}, NOW()) ON CONFLICT DO NOTHING"
},
"name": "Log Incident (SOC2)",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
1250,
420
],
"id": "pg2b"
}
],
"connections": {
"Every 3 Minutes": {
"main": [
[
{
"node": "Load Endpoints",
"type": "main",
"index": 0
}
]
]
},
"Load Endpoints": {
"main": [
[
{
"node": "HTTP Ping",
"type": "main",
"index": 0
}
]
]
},
"HTTP Ping": {
"main": [
[
{
"node": "Classify Health",
"type": "main",
"index": 0
}
]
]
},
"Classify Health": {
"main": [
[
{
"node": "Has Issues?",
"type": "main",
"index": 0
}
]
]
},
"Has Issues?": {
"main": [
[
{
"node": "Slack Platform Ops",
"type": "main",
"index": 0
}
],
[
{
"node": "Log Incident (SOC2)",
"type": "main",
"index": 0
}
]
]
}
}
}
3. GDPR/CCPA Consent & Privacy Compliance Deadline Tracker
Who needs it: Any MarTech SaaS operating in the EU, UK, California, or any US state with a comprehensive privacy law.
What it does: Every weekday at 8AM, scan your privacy compliance calendar. Calculate urgency tiers (OVERDUE → CRITICAL → URGENT → WARNING → NOTICE), look up the specific regulatory action required for each obligation type, deduplicate against last-notified timestamp, and route alerts to your privacy team and obligation owners.
Compliance obligations tracked:
- GDPR: consent audit (Art.7), ROPA update (Art.30), DSR deadlines (Art.12), DPA review (Art.28)
- CCPA/CPRA: DSR 45-day response, annual privacy notice, data inventory update
- CAN-SPAM: suppression list audit (§7704 — 10 business days to honor opt-outs)
- CASL: express consent records review
- TCPA: prior express written consent audit
- US state laws: Iowa CPA, Texas TDPSA, Colorado CPA, Virginia VCDPA, Montana CDPA
{
"name": "GDPR/CCPA Consent & Privacy Compliance Deadline Tracker",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "weekdays",
"triggerAtHour": 8
}
]
}
},
"name": "Weekdays 8AM",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1,
"position": [
250,
300
],
"id": "sc3"
},
{
"parameters": {
"operation": "getAll",
"sheetId": "YOUR_COMPLIANCE_SHEET_ID",
"range": "PrivacyDeadlines!A:H",
"options": {
"headerRow": 1
}
},
"name": "Load Privacy Calendar",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4,
"position": [
450,
300
],
"id": "gs3"
},
{
"parameters": {
"jsCode": "const today=new Date();\nconst actionMap={\n 'GDPR_CONSENT_AUDIT':'Review and refresh consent records for EU contacts \u2014 Art.7 requires freely given, specific, informed, unambiguous consent',\n 'GDPR_ART30_ROPA':'Update Records of Processing Activities \u2014 required for any org with >250 employees or processing sensitive data',\n 'GDPR_DSR_30DAY':'Data Subject Request deadline \u2014 must respond within 30 days (Art.12), extendable to 90 with notice',\n 'GDPR_DPA_REVIEW':'Review Data Processing Agreement with sub-processors \u2014 Art.28 requires documented DPA',\n 'CCPA_DSR_45DAY':'CCPA Data Subject Request deadline \u2014 Cal.Civ.Code \u00a71798.100 requires response within 45 days',\n 'CCPA_ANNUAL_PRIVACY':'Update Privacy Policy \u2014 \u00a71798.130 requires annual update if data practices changed',\n 'CCPA_DATA_INVENTORY':'Annual data inventory update \u2014 CPRA \u00a71798.100 requires mapping of personal information categories',\n 'CAN_SPAM_ANNUAL':'CAN-SPAM suppression list audit \u2014 \u00a77704(a)(4) requires honoring opt-outs within 10 business days',\n 'CASL_COMPLIANCE_REVIEW':'CASL consent records review \u2014 express consent required for commercial electronic messages to Canadian recipients',\n 'TCPA_CONSENT_SCRUB':'TCPA prior express written consent audit \u2014 47 U.S.C. \u00a7227 requires documented consent for marketing calls/texts',\n 'COPPA_REVIEW_IF_U13':'COPPA review if platform reaches under-13 users \u2014 16 CFR Part 312 requires verifiable parental consent',\n 'SOC2_TYPE2':'SOC 2 Type II audit renewal \u2014 access review, vendor assessment, penetration test evidence collection',\n 'IOWA_CPA_COMPLIANCE':'Iowa CPA (Iowa Code Ch. 715D) \u2014 opt-out rights for targeted advertising and sale of personal data',\n 'TEXAS_TDPSA':'Texas TDPSA (Bus. & Com. Code Ch. 541) \u2014 universal opt-out mechanism required by Jan 1 2025',\n 'COLORADO_CPA':'Colorado Privacy Act (C.R.S. \u00a76-1-1301) \u2014 data protection assessment required for high-risk processing'\n};\nconst alerts=[];\nfor(const item of $input.all()){\n const d=item.json;\n if(!d.due_date||d.status==='COMPLETED')continue;\n const due=new Date(d.due_date);\n const days=Math.floor((due-today)/86400000);\n let urgency='OK';\n if(days<0)urgency='OVERDUE';\n else if(days<=7)urgency='CRITICAL';\n else if(days<=14)urgency='URGENT';\n else if(days<=30)urgency='WARNING';\n else if(days<=60)urgency='NOTICE';\n if(urgency!=='OK'){\n const action=actionMap[d.compliance_type]||'Review compliance obligation and take required action';\n const lastNotified=d.last_notified_at?new Date(d.last_notified_at):new Date(0);\n const hoursSince=(today-lastNotified)/3600000;\n if(hoursSince>=24)alerts.push({json:{...d,daysUntilDue:days,urgency,action}});\n }\n}\nreturn alerts.length?alerts:[{json:{__no_alerts:true}}];"
},
"name": "Prioritize Deadlines",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
650,
300
],
"id": "cls3"
},
{
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{!$json.__no_alerts}}",
"value2": true
}
]
}
},
"name": "Any Alerts?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
850,
300
],
"id": "if3"
},
{
"parameters": {
"channel": "#compliance-privacy",
"text": ":lock: *Privacy Deadline \u2014 {{$json.urgency}}*\nType: {{$json.compliance_type}}\nDue: {{$json.due_date}} ({{$json.daysUntilDue}} days)\nOwner: {{$json.owner_name}} | Region: {{$json.jurisdiction}}\nAction: {{$json.action}}"
},
"name": "Slack Privacy Team",
"type": "n8n-nodes-base.slack",
"typeVersion": 1,
"position": [
1050,
180
],
"id": "sl3"
},
{
"parameters": {
"toEmail": "={{$json.owner_email}}",
"subject": "{{$json.urgency}}: {{$json.compliance_type}} due {{$json.due_date}} ({{$json.daysUntilDue}} days)",
"message": "Privacy Compliance Deadline Alert\n\nType: {{$json.compliance_type}}\nJurisdiction: {{$json.jurisdiction}}\nDue date: {{$json.due_date}}\nDays remaining: {{$json.daysUntilDue}}\nAlert level: {{$json.urgency}}\n\nRequired action:\n{{$json.action}}\n\nPlease confirm status or escalate to DPO/Legal immediately.\n\nThis alert is generated automatically by your compliance tracking system."
},
"name": "Email Owner",
"type": "n8n-nodes-base.gmail",
"typeVersion": 1,
"position": [
1050,
420
],
"id": "gm3"
}
],
"connections": {
"Weekdays 8AM": {
"main": [
[
{
"node": "Load Privacy Calendar",
"type": "main",
"index": 0
}
]
]
},
"Load Privacy Calendar": {
"main": [
[
{
"node": "Prioritize Deadlines",
"type": "main",
"index": 0
}
]
]
},
"Prioritize Deadlines": {
"main": [
[
{
"node": "Any Alerts?",
"type": "main",
"index": 0
}
]
]
},
"Any Alerts?": {
"main": [
[
{
"node": "Slack Privacy Team",
"type": "main",
"index": 0
}
],
[
{
"node": "Email Owner",
"type": "main",
"index": 0
}
]
]
}
}
}
4. Campaign Performance Anomaly & Revenue Impact Alert
Who needs it: Email platform operators, CDP teams, marketing automation SaaS — any platform where a deliverability drop translates directly to customer revenue loss.
What it does: Every 30 minutes, pull live campaign metrics from your analytics Postgres table. Detect delivery rate drops, open rate declines, and bounce spikes. Classify by severity (CRITICAL/HIGH/MEDIUM), deduplicate with 2-hour suppress window via $getWorkflowStaticData, alert #campaign-ops, and log for root-cause analysis.
Severity thresholds:
-
CRITICAL: delivery rate dropped ≥30% vs 7d avg OR estimated revenue impact ≥$10K -
HIGH: delivery rate dropped ≥15%, bounce rate spike >5%, OR revenue impact ≥$2K -
MEDIUM: open rate dropped ≥20% vs 7d avg
{
"name": "Campaign Performance Anomaly & Revenue Impact Alert",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 30
}
]
}
},
"name": "Every 30 Minutes",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1,
"position": [
250,
300
],
"id": "sc4"
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT campaign_id, campaign_name, channel, delivery_rate_1h, delivery_rate_7d_avg, open_rate_1h, open_rate_7d_avg, bounce_rate_1h, bounce_rate_7d_avg, estimated_revenue_impact_usd, team_owner FROM campaign_metrics WHERE updated_at > NOW() - INTERVAL '1 hour'"
},
"name": "Load Campaign Metrics",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
450,
300
],
"id": "pg4"
},
{
"parameters": {
"jsCode": "const items=[];\nfor(const item of $input.all()){\n const d=item.json;\n const dr7=parseFloat(d.delivery_rate_7d_avg||95);\n const dr1=parseFloat(d.delivery_rate_1h||95);\n const br1=parseFloat(d.bounce_rate_1h||0);\n const or7=parseFloat(d.open_rate_7d_avg||20);\n const or1=parseFloat(d.open_rate_1h||20);\n const revenue=parseFloat(d.estimated_revenue_impact_usd||0);\n const deliveryDrop=(dr7-dr1)/dr7*100;\n const openDrop=(or7-or1)/or7*100;\n const bounceSpiked=br1>5;\n let severity;\n if(deliveryDrop>=30||revenue>=10000)severity='CRITICAL';\n else if(deliveryDrop>=15||bounceSpiked||revenue>=2000)severity='HIGH';\n else if(openDrop>=20)severity='MEDIUM';\n else continue;\n const state=$getWorkflowStaticData('global');\n const key=`anomaly_${d.campaign_id}`;\n const now=Date.now();\n if(now-(state[key]||0)>7200000){\n state[key]=now;\n items.push({json:{...d,severity,deliveryDrop:deliveryDrop.toFixed(1),openDrop:openDrop.toFixed(1),revenue}});\n }\n}\nreturn items.length?items:[{json:{__no_anomalies:true}}];"
},
"name": "Detect Anomalies",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
650,
300
],
"id": "cls4"
},
{
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{!$json.__no_anomalies}}",
"value2": true
}
]
}
},
"name": "Any Anomalies?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
850,
300
],
"id": "if4"
},
{
"parameters": {
"channel": "#campaign-ops",
"text": "{{$json.severity==='CRITICAL'?':red_circle: CRITICAL':':large_yellow_circle: HIGH'}} *Campaign Anomaly: {{$json.campaign_name}}*\nChannel: {{$json.channel}} | Severity: {{$json.severity}}\nDelivery drop: {{$json.deliveryDrop}}% (vs 7d avg)\nOpen rate drop: {{$json.openDrop}}%\nEstimated revenue impact: ${{$json.revenue}}\n@{{$json.team_owner}} \u2014 investigate immediately"
},
"name": "Slack Campaign Ops",
"type": "n8n-nodes-base.slack",
"typeVersion": 1,
"position": [
1050,
180
],
"id": "sl4"
},
{
"parameters": {
"operation": "executeQuery",
"query": "INSERT INTO campaign_anomalies (campaign_id, severity, delivery_drop_pct, open_drop_pct, revenue_impact_usd, detected_at) VALUES ('{{$json.campaign_id}}', '{{$json.severity}}', {{$json.deliveryDrop}}, {{$json.openDrop}}, {{$json.revenue}}, NOW()) ON CONFLICT DO NOTHING"
},
"name": "Log Anomaly",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
1050,
420
],
"id": "pg4b"
}
],
"connections": {
"Every 30 Minutes": {
"main": [
[
{
"node": "Load Campaign Metrics",
"type": "main",
"index": 0
}
]
]
},
"Load Campaign Metrics": {
"main": [
[
{
"node": "Detect Anomalies",
"type": "main",
"index": 0
}
]
]
},
"Detect Anomalies": {
"main": [
[
{
"node": "Any Anomalies?",
"type": "main",
"index": 0
}
]
]
},
"Any Anomalies?": {
"main": [
[
{
"node": "Slack Campaign Ops",
"type": "main",
"index": 0
}
],
[
{
"node": "Log Anomaly",
"type": "main",
"index": 0
}
]
]
}
}
}
5. Weekly MarTech Platform KPI Dashboard
Who needs it: Heads of product, CEOs, and investor-facing operators at growth-stage MarTech companies.
What it does: Every Monday at 8AM, run two parallel Postgres queries (platform metrics + account health), merge results, calculate WoW changes via $getWorkflowStaticData (persisted across runs), build a color-coded HTML email (delivery rate traffic-light coloring: green ≥95%, orange ≥90%, red <90%), and deliver to CEO/CTO with a Slack one-liner.
Key MarTech metrics tracked:
- Monthly Active Users (MAU) with WoW % change
- Campaign sends (7d) with WoW % change
- Average delivery rate (color-coded — drops below 90% = inbox deliverability crisis)
- Average open rate
- Revenue attributed (7d)
- Account health: new, churned, expanded in last 7 days
{
"name": "Weekly MarTech Platform KPI Dashboard",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "weeks",
"weeksInterval": 1,
"triggerAtDay": [
1
],
"triggerAtHour": 8
}
]
}
},
"name": "Monday 8AM",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1,
"position": [
250,
300
],
"id": "sc5"
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT mau, campaign_sends_7d, avg_delivery_rate, avg_open_rate, revenue_attributed_7d, contacts_processed_7d FROM platform_metrics ORDER BY recorded_at DESC LIMIT 2"
},
"name": "Platform Metrics",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
450,
200
],
"id": "pg5a"
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT COUNT(CASE WHEN churned_at > NOW()-INTERVAL '7 days' THEN 1 END) AS churned_7d, COUNT(CASE WHEN created_at > NOW()-INTERVAL '7 days' THEN 1 END) AS new_7d, COUNT(CASE WHEN expanded_at > NOW()-INTERVAL '7 days' THEN 1 END) AS expanded_7d, COUNT(*) AS total_active FROM accounts WHERE status='active'"
},
"name": "Account Health",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
450,
420
],
"id": "pg5b"
},
{
"parameters": {
"mode": "combine",
"combinationMode": "mergeByIndex",
"options": {}
},
"name": "Merge Metrics",
"type": "n8n-nodes-base.merge",
"typeVersion": 2,
"position": [
650,
300
],
"id": "mrg5"
},
{
"parameters": {
"jsCode": "const rows=$input.all().map(i=>i.json);\nconst cur=rows[0]||{};\nconst prev=rows[1]||{};\nconst state=$getWorkflowStaticData('global');\nconst prevMau=state.prev_mau||cur.mau;\nstate.prev_mau=cur.mau;\nconst mauWoW=prevMau?((cur.mau-prevMau)/prevMau*100).toFixed(1):0;\nconst prevSends=state.prev_sends||cur.campaign_sends_7d;\nstate.prev_sends=cur.campaign_sends_7d;\nconst sendsWoW=prevSends?((cur.campaign_sends_7d-prevSends)/prevSends*100).toFixed(1):0;\nconst drColor=cur.avg_delivery_rate>=95?'#27ae60':cur.avg_delivery_rate>=90?'#f39c12':'#e74c3c';\nconst html=`<h2 style='font-family:sans-serif'>Weekly MarTech Platform Report</h2>\n<p style='font-family:sans-serif'>Week of ${new Date().toISOString().slice(0,10)}</p>\n<table border='1' cellpadding='10' style='border-collapse:collapse;font-family:sans-serif'>\n<tr style='background:#2c3e50;color:white'><th>Metric</th><th>This Week</th><th>WoW</th></tr>\n<tr><td>Monthly Active Users</td><td>${(cur.mau||0).toLocaleString()}</td><td>${mauWoW>0?'+':''}${mauWoW}%</td></tr>\n<tr><td>Campaign Sends (7d)</td><td>${(cur.campaign_sends_7d||0).toLocaleString()}</td><td>${sendsWoW>0?'+':''}${sendsWoW}%</td></tr>\n<tr><td>Avg Delivery Rate</td><td style='color:${drColor};font-weight:bold'>${(cur.avg_delivery_rate||0).toFixed(2)}%</td><td>\u2014</td></tr>\n<tr><td>Avg Open Rate</td><td>${(cur.avg_open_rate||0).toFixed(2)}%</td><td>\u2014</td></tr>\n<tr><td>Revenue Attributed (7d)</td><td>$${(cur.revenue_attributed_7d||0).toLocaleString()}</td><td>\u2014</td></tr>\n<tr><td>Contacts Processed (7d)</td><td>${(cur.contacts_processed_7d||0).toLocaleString()}</td><td>\u2014</td></tr>\n</table>\n<h3 style='font-family:sans-serif'>Account Health</h3>\n<table border='1' cellpadding='10' style='border-collapse:collapse;font-family:sans-serif'>\n<tr style='background:#2c3e50;color:white'><th>Metric</th><th>Value</th></tr>\n<tr><td>Total Active Accounts</td><td>${cur.total_active||0}</td></tr>\n<tr><td>New Accounts (7d)</td><td style='color:#27ae60'>+${cur.new_7d||0}</td></tr>\n<tr><td>Churned Accounts (7d)</td><td style='color:#e74c3c'>-${cur.churned_7d||0}</td></tr>\n<tr><td>Expanded Accounts (7d)</td><td style='color:#3498db'>${cur.expanded_7d||0}</td></tr>\n</table>`;\nreturn[{json:{html,mau:cur.mau,mauWoW,sendsWoW,deliveryRate:cur.avg_delivery_rate,newAccounts:cur.new_7d,churnedAccounts:cur.churned_7d}}];"
},
"name": "Build KPI Report",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
850,
300
],
"id": "cls5"
},
{
"parameters": {
"toEmail": "ceo@yourplatform.com,cto@yourplatform.com",
"subject": "Weekly MarTech KPI \u2014 MAU: {{$json.mau?.toLocaleString()}} | Delivery: {{$json.deliveryRate?.toFixed(1)}}% | New Accounts: +{{$json.newAccounts}}",
"message": "={{$json.html}}",
"options": {
"allowHtml": true
}
},
"name": "Email Leadership",
"type": "n8n-nodes-base.gmail",
"typeVersion": 1,
"position": [
1050,
200
],
"id": "gm5"
},
{
"parameters": {
"channel": "#exec-kpis",
"text": ":bar_chart: Weekly MarTech KPIs\nMAU: {{$json.mau?.toLocaleString()}} ({{$json.mauWoW}}% WoW)\nCampaign sends: WoW {{$json.sendsWoW}}%\nDelivery rate: {{$json.deliveryRate?.toFixed(1)}}%\nNew accounts: +{{$json.newAccounts}} | Churned: -{{$json.churnedAccounts}}"
},
"name": "Slack Exec Summary",
"type": "n8n-nodes-base.slack",
"typeVersion": 1,
"position": [
1050,
420
],
"id": "sl5"
}
],
"connections": {
"Monday 8AM": {
"main": [
[
{
"node": "Platform Metrics",
"type": "main",
"index": 0
}
],
[
{
"node": "Account Health",
"type": "main",
"index": 0
}
]
]
},
"Platform Metrics": {
"main": [
[
{
"node": "Merge Metrics",
"type": "main",
"index": 0
}
]
]
},
"Account Health": {
"main": [
[
{
"node": "Merge Metrics",
"type": "main",
"index": 1
}
]
]
},
"Merge Metrics": {
"main": [
[
{
"node": "Build KPI Report",
"type": "main",
"index": 0
}
]
]
},
"Build KPI Report": {
"main": [
[
{
"node": "Email Leadership",
"type": "main",
"index": 0
}
]
]
},
"Email Leadership": {
"main": [
[
{
"node": "Slack Exec Summary",
"type": "main",
"index": 0
}
]
]
}
}
}
Why n8n for MarTech SaaS (vs. Zapier or Make.com)
| Requirement | Zapier/Make | n8n (self-hosted) |
|---|---|---|
| GDPR Art.28 sub-processor | ❌ Zapier/Make = additional sub-processor requiring documented DPA | ✅ Your infra — no new sub-processor in Art.30 records |
| CCPA 'sharing' (§1798.140(ah)(1)) | ❌ Behavioral data + cookies through Zapier = 'sharing for CBAA' → opt-out right triggered | ✅ Internal — no third-party data routing |
| CAN-SPAM §7704 suppression | ❌ Suppression webhook failure not visible — silent violation risk | ✅ Full execution log + failure alerting built in |
| TCPA consent records | ❌ 30-day Zapier log vs permanent consent record requirement | ✅ Your Postgres — consent records retained indefinitely |
| SOC 2 CC7.2 logical access | ❌ Zapier workspace accessible to all members incl. contractors | ✅ Self-hosted — access controlled by your IAM |
| Real-time anomaly detection | ⚠️ Limited to webhook triggers, no stateful dedup | ✅ Code node + $getWorkflowStaticData for stateful suppress |
| Multi-environment isolation | ❌ Single workspace | ✅ Separate instances: dev / staging / prod |
Cost comparison: At 1B marketing events/month, Zapier's Operations plan exceeds $100K/year. n8n on a $200/month VPS handles unlimited executions for a rounding error.
Get these workflows pre-built
All 5 workflows are available as tested, import-ready templates at FlowKit — n8n Automation Templates.
Each template includes:
- Import-ready JSON with all nodes pre-configured
- Postgres schema (CREATE TABLE scripts)
- Google Sheets data structure
- Setup guide (credentials, webhook config, environment variables)
- Compliance notes mapping each workflow to specific regulations
Most relevant for MarTech SaaS:
- AI Customer Support Bot — classify and route customer inquiries with Claude AI ($29)
- Customer Feedback Analyzer — analyze campaign feedback and NPS responses ($29)
- Lead Capture to CRM — capture and route marketing qualified leads ($19)
Building a MarTech automation your team relies on? Share it in the comments — what workflow do you wish you had running automatically?
Top comments (0)