DEV Community

Alex Kane
Alex Kane

Posted on

n8n for RetailTech SaaS Vendors: 5 Automations for PCI DSS, CCPA, ADA Title III, CPSC, and FTC Compliance

If you sell retail technology — point-of-sale software, e-commerce platforms, inventory management, loyalty programs — your customers operate inside a dense compliance lattice: PCI DSS for cardholder data, CCPA for California consumer rights, ADA Title III for digital accessibility, CPSC recall obligations for marketplaces, FTC Endorsement Rules for affiliate programs, and a patchwork of state sales tax nexus laws.

Routing your own automations — onboarding drips, health monitors, incident pipelines — through Zapier or Make means your audit logs, customer PII, and compliance artifacts live in a third-party cloud. For a PCI DSS Level 1 merchant (6M+ card transactions/year), that's a scope expansion finding. For an enterprise retailer under ADA demand letters, a SaaS outage is a $4,000-per-visit statutory damages exposure.

Self-hosted n8n eliminates both risks. Here are five production-ready workflows for RetailTech SaaS vendors — import the JSON, swap in your credentials, and ship.


Workflow 1: Customer Tier Segmentation & Onboarding Drip

Retailer customers span six distinct operational profiles. One generic onboarding email wastes the enterprise retailer's time (they need PCI ROC guidance, not Shopify tips) and confuses the boutique e-commerce founder (they need FTC endorsement disclosure help, not CPSC recall readiness).

This workflow detects six customer tiers and seven compliance flags at signup and branches the onboarding drip accordingly.

Tiers detected:

  • ENTERPRISE_RETAILER ($100M+ GMV) — PCI DSS Level 1 ROC + ADA Title III + 50-state tax nexus
  • MID_MARKET_RETAILER ($10M–$100M GMV) — PCI DSS SAQ-D + CCPA + regional nexus
  • BOUTIQUE_ECOMM (<$10M GMV) — PCI SAQ-A + CCPA + FTC endorsement basics
  • MARKETPLACE_OPERATOR — CPSC pass-through recall + seller review FTC disclosure
  • OMNICHANNEL_BRAND (online + physical) — unified PCI scope + dual ADA (digital + physical)
  • SUBSCRIPTION_BOX — FTC Negative Option Rule + state auto-renewal laws

Compliance flags:

  • PCI_DSS_LEVEL_1 — annual_card_volume >= 6M or card_transactions >= 6M
  • CCPA_SALE_SHARING — state='CA' or sells_ca_consumers
  • ADA_TITLE_III_ECOMM — always true (DOJ 2024 final rule 28 CFR Part 36 covers all ecomm)
  • CPSC_RECALL_OBLIGATION — marketplace operator or manufactures products
  • FTC_ENDORSEMENT_APPLICABLE — uses influencer marketing or has affiliate program
  • GDPR_EU_CONSUMER — sells to EU consumers
  • STATE_SALES_TAX_NEXUS — nexus in 5+ states (South Dakota v. Wayfair economic nexus)

Day 0: tier-specific welcome email + compliance flag summary. Day 3: compliance quick-start guide for their tier. Day 7: advanced setup guide + book-a-call CTA.

{
  "name": "RetailTech SaaS \u2014 Customer Onboarding Drip",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "retail-customer-onboarding",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "a1",
      "name": "New Customer Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "\nconst d = $json.body;\nconst tier = d.plan_tier || 'MID_MARKET_RETAILER';\nconst flags = {\n  PCI_DSS_LEVEL_1:          (d.annual_card_volume_millions || 0) >= 6 || (d.card_transactions_millions || 0) >= 6,\n  CCPA_SALE_SHARING:        d.state === 'CA' || !!d.sells_ca_consumers,\n  ADA_TITLE_III_ECOMM:      true, // All ecomm under ADA Title III (11th Cir. Gil v. Winn-Dixie; DOJ 2024 final rule)\n  CPSC_RECALL_OBLIGATION:   tier === 'MARKETPLACE_OPERATOR' || !!d.manufactures_products,\n  FTC_ENDORSEMENT_APPLICABLE: !!d.uses_influencer_marketing || !!d.has_affiliate_program,\n  GDPR_EU_CONSUMER:         !!d.sells_eu_consumers,\n  STATE_SALES_TAX_NEXUS:    (d.states_with_nexus || 0) >= 5\n};\nconst tierMap = {\n  ENTERPRISE_RETAILER:  { label: 'Enterprise Retailer ($100M+ GMV)',\n    d3: 'PCI DSS Level 1 ROC integration guide + ADA Title III WCAG 2.1 AA accessibility module',\n    d7: 'SOC 2 Type II shared responsibility matrix + multi-state sales tax nexus dashboard' },\n  MID_MARKET_RETAILER:  { label: 'Mid-Market Retailer ($10M\u2013$100M GMV)',\n    d3: 'PCI DSS SAQ-D completion walkthrough + CCPA consumer rights portal configuration',\n    d7: 'ADA accessibility audit workflow + state sales tax economic nexus threshold monitor' },\n  BOUTIQUE_ECOMM:       { label: 'Boutique E-Commerce (<$10M GMV)',\n    d3: 'PCI DSS SAQ-A completion guide + CCPA privacy policy generator',\n    d7: 'FTC Endorsement Guide compliance checklist + Shopify app integration tutorial' },\n  MARKETPLACE_OPERATOR: { label: 'Marketplace Operator',\n    d3: 'CPSC recall pass-through obligation setup + seller review FTC endorsement disclosure config',\n    d7: 'Marketplace seller data governance policy + state consumer protection compliance tracker' },\n  OMNICHANNEL_BRAND:    { label: 'Omnichannel Brand (Online + Physical)',\n    d3: 'Unified PCI DSS scope map (ecomm + POS) + ADA digital + physical accessibility gap analysis',\n    d7: 'CCPA cross-channel data inventory + omnichannel sales tax nexus by location guide' },\n  SUBSCRIPTION_BOX:     { label: 'Subscription Box / Recurring Commerce',\n    d3: 'FTC Negative Option Rule compliance config + CCPA recurring consent tracking setup',\n    d7: 'Subscription churn dashboard + state auto-renewal law deadline tracker' }\n};\nconst cfg = tierMap[tier] || tierMap.MID_MARKET_RETAILER;\nconst flagList = Object.entries(flags).filter(([,v]) => v).map(([k]) => k);\nreturn [{ customer_email: d.customer_email, customer_name: d.customer_name || 'Team',\n  company: d.company || 'your company', tier, segment: cfg.label,\n  day3_focus: cfg.d3, day7_focus: cfg.d7,\n  flags: flagList, pci_l1: flags.PCI_DSS_LEVEL_1, ccpa: flags.CCPA_SALE_SHARING,\n  cpsc: flags.CPSC_RECALL_OBLIGATION, ftc: flags.FTC_ENDORSEMENT_APPLICABLE }];\n"
      },
      "id": "a2",
      "name": "Tier + Compliance Flag Classifier",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_SHEET_ID",
          "mode": "id"
        },
        "sheetName": "onboarding_log",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "customer_email": "={{ $json.customer_email }}",
            "company": "={{ $json.company }}",
            "tier": "={{ $json.tier }}",
            "flags": "={{ $json.flags.join(', ') }}",
            "enrolled_at": "={{ $now.toISO() }}"
          }
        }
      },
      "id": "a3",
      "name": "Log to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        680,
        300
      ]
    },
    {
      "parameters": {
        "sendTo": "={{ $json.customer_email }}",
        "subject": "Welcome to [YourPlatform] \u2014 your retail compliance hub is ready",
        "emailType": "html",
        "message": "<p>Hi {{ $json.customer_name }},</p><p>Welcome! Your account is configured for <strong>{{ $json.segment }}</strong>.</p><p>Detected compliance obligations: <strong>{{ $json.flags.join(', ') || 'Standard tier' }}</strong></p><p>Your customer success manager will reach out within 24 hours.</p>"
      },
      "id": "a4",
      "name": "Day 0 \u2014 Welcome Email",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        900,
        200
      ]
    },
    {
      "parameters": {
        "amount": 3,
        "unit": "days"
      },
      "id": "a5",
      "name": "Wait 3 Days",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1,
      "position": [
        900,
        400
      ]
    },
    {
      "parameters": {
        "sendTo": "={{ $('Tier + Compliance Flag Classifier').item.json.customer_email }}",
        "subject": "Your {{ $('Tier + Compliance Flag Classifier').item.json.segment }} compliance quick-start",
        "emailType": "html",
        "message": "<p>Today's focus: <strong>{{ $('Tier + Compliance Flag Classifier').item.json.day3_focus }}</strong></p><p>Open your compliance dashboard: [link]</p>"
      },
      "id": "a6",
      "name": "Day 3 \u2014 Compliance Quick-Start",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        1120,
        400
      ]
    },
    {
      "parameters": {
        "amount": 4,
        "unit": "days"
      },
      "id": "a7",
      "name": "Wait 4 Days",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1,
      "position": [
        1120,
        500
      ]
    },
    {
      "parameters": {
        "sendTo": "={{ $('Tier + Compliance Flag Classifier').item.json.customer_email }}",
        "subject": "Week 1 advanced setup \u2014 {{ $('Tier + Compliance Flag Classifier').item.json.segment }}",
        "emailType": "html",
        "message": "<p>This week: <strong>{{ $('Tier + Compliance Flag Classifier').item.json.day7_focus }}</strong></p><p>Book your week-1 check-in: [calendly]</p>"
      },
      "id": "a8",
      "name": "Day 7 \u2014 Advanced Setup",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        1340,
        500
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "{\"status\":\"enrolled\"}"
      },
      "id": "a9",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        900,
        100
      ]
    }
  ],
  "connections": {
    "New Customer Webhook": {
      "main": [
        [
          {
            "node": "Tier + Compliance Flag Classifier",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tier + Compliance Flag Classifier": {
      "main": [
        [
          {
            "node": "Log to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log to Google Sheets": {
      "main": [
        [
          {
            "node": "Day 0 \u2014 Welcome Email",
            "type": "main",
            "index": 0
          },
          {
            "node": "Wait 3 Days",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Day 0 \u2014 Welcome Email": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 3 Days": {
      "main": [
        [
          {
            "node": "Day 3 \u2014 Compliance Quick-Start",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Day 3 \u2014 Compliance Quick-Start": {
      "main": [
        [
          {
            "node": "Wait 4 Days",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 4 Days": {
      "main": [
        [
          {
            "node": "Day 7 \u2014 Advanced Setup",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 2: Retail Platform API Health Monitor

RetailTech SaaS platforms typically expose five critical API surfaces, each carrying a distinct compliance obligation when it goes down.

Endpoint Compliance Risk on Downtime
payment_processing_api PCI DSS Req 6.4.3 — 4-hr RTO obligation for Level 1 merchants
product_catalog_api ADA Title III — catalog outage = accessibility barrier = $4k/visit statutory damages risk (CA Unruh Act)
order_fulfillment_api CPSC 15 USC §2064(b)(3) — recall integration failure = delayed hazard reporting
tax_calculation_api Economic nexus exposure — uncollected tax in nexus states (South Dakota v. Wayfair)
marketing_consent_api CCPA §1798.120 + FTC §255.5 — consent failure = deceptive practice risk

This workflow polls all five endpoints every 5 minutes. DOWN = immediate Slack alert to #platform-incidents + incident log. DEGRADED = warning alert. Each alert includes the specific regulatory annotation so on-call engineers know exactly what's at stake — not just 'API is down' but 'this is a PCI DSS 6.4.3 RTO event'.

{
  "name": "RetailTech SaaS \u2014 Retail Platform API Health Monitor",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 5
            }
          ]
        }
      },
      "id": "b1",
      "name": "Every 5 Minutes",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "\nconst endpoints = [\n  { name: 'payment_processing_api',   url: 'https://api.yourplatform.io/health/payments',\n    annotation: 'PCI DSS Req 6.4.3 integrity monitoring + 4-hr RTO for Level 1 merchants' },\n  { name: 'product_catalog_api',      url: 'https://api.yourplatform.io/health/catalog',\n    annotation: 'ADA Title III / WCAG 2.1 AA \u2014 catalog outage = accessibility barrier = $4k/visit statutory damages risk' },\n  { name: 'order_fulfillment_api',    url: 'https://api.yourplatform.io/health/fulfillment',\n    annotation: 'CPSC 15 USC \u00a72064(b)(3) recall integration + state consumer protection SLA obligations' },\n  { name: 'tax_calculation_api',      url: 'https://api.yourplatform.io/health/tax',\n    annotation: 'STATE_SALES_TAX_NEXUS: failure = uncollected tax = nexus exposure in economic nexus states' },\n  { name: 'marketing_consent_api',    url: 'https://api.yourplatform.io/health/consent',\n    annotation: 'CCPA \u00a71798.120 opt-out / FTC Endorsement Rule \u00a7255.5 \u2014 consent failure = deceptive practice risk' }\n];\nreturn endpoints;\n"
      },
      "id": "b2",
      "name": "Build Endpoint List",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "url": "={{ $json.url }}",
        "options": {
          "timeout": 10000,
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "b3",
      "name": "Ping Endpoint",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        680,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "\nconst results = [];\nfor (const item of $input.all()) {\n  const status = item.json.$response?.statusCode || 0;\n  const endpoint = item.json;\n  let severity = 'OK';\n  if (status === 0 || status >= 500) severity = 'DOWN';\n  else if (status >= 400) severity = 'DEGRADED';\n  if (severity !== 'OK') {\n    results.push({\n      endpoint: endpoint.name || 'unknown',\n      url: endpoint.url || '',\n      status_code: status,\n      severity,\n      annotation: endpoint.annotation || '',\n      ts: new Date().toISOString()\n    });\n  }\n}\nif (results.length === 0) return [{ all_ok: true }];\nreturn results;\n"
      },
      "id": "b4",
      "name": "Evaluate Health",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        900,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.all_ok }}",
              "value2": true
            }
          ]
        }
      },
      "id": "b5",
      "name": "All OK?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        1120,
        300
      ]
    },
    {
      "parameters": {
        "select": "defined",
        "outputsMax": 2,
        "output": {
          "rules": {
            "values": [
              {
                "outputKey": "DOWN",
                "conditions": {
                  "options": {
                    "caseSensitive": true,
                    "leftValue": "",
                    "typeValidation": "strict"
                  },
                  "conditions": [
                    {
                      "leftValue": "={{ $json.severity }}",
                      "rightValue": "DOWN",
                      "operator": {
                        "operation": "equals",
                        "type": "string"
                      }
                    }
                  ]
                }
              },
              {
                "outputKey": "DEGRADED",
                "conditions": {
                  "options": {},
                  "conditions": [
                    {
                      "leftValue": "={{ $json.severity }}",
                      "rightValue": "DEGRADED",
                      "operator": {
                        "operation": "equals",
                        "type": "string"
                      }
                    }
                  ]
                }
              }
            ]
          }
        }
      },
      "id": "b6",
      "name": "Route by Severity",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [
        1340,
        400
      ]
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": "#platform-incidents",
        "text": "\ud83d\udd34 *CRITICAL: {{ $json.endpoint }} is DOWN*\nStatus: {{ $json.status_code }}\nCompliance Risk: {{ $json.annotation }}\nTimestamp: {{ $json.ts }}"
      },
      "id": "b7",
      "name": "Slack \u2014 DOWN Alert",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [
        1560,
        300
      ]
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": "#platform-incidents",
        "text": "\u26a0\ufe0f *DEGRADED: {{ $json.endpoint }}*\nStatus: {{ $json.status_code }}\nCompliance Note: {{ $json.annotation }}\nTimestamp: {{ $json.ts }}"
      },
      "id": "b8",
      "name": "Slack \u2014 DEGRADED Alert",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [
        1560,
        500
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_SHEET_ID",
          "mode": "id"
        },
        "sheetName": "platform_incidents",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "endpoint": "={{ $json.endpoint }}",
            "status_code": "={{ $json.status_code }}",
            "severity": "={{ $json.severity }}",
            "annotation": "={{ $json.annotation }}",
            "ts": "={{ $json.ts }}"
          }
        }
      },
      "id": "b9",
      "name": "Log Incident",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        1560,
        400
      ]
    }
  ],
  "connections": {
    "Every 5 Minutes": {
      "main": [
        [
          {
            "node": "Build Endpoint List",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Endpoint List": {
      "main": [
        [
          {
            "node": "Ping Endpoint",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ping Endpoint": {
      "main": [
        [
          {
            "node": "Evaluate Health",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Evaluate Health": {
      "main": [
        [
          {
            "node": "All OK?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "All OK?": {
      "main": [
        [],
        [
          {
            "node": "Route by Severity",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Severity": {
      "main": [
        [
          {
            "node": "Slack \u2014 DOWN Alert",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log Incident",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Slack \u2014 DEGRADED Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 3: PCI DSS / CCPA / ADA / CPSC / FTC Compliance Deadline Tracker

RetailTech SaaS vendors need to track compliance deadlines for their own platform and help customers track theirs. Missed deadlines compound: a missed PCI DSS quarterly scan is a $5,000–$100,000 per month fine from the acquiring bank. A missed CCPA privacy policy update is a $7,500 per intentional violation.

This workflow runs weekdays at 8 AM, reads a Google Sheet of compliance deadlines, classifies urgency, and routes alerts to the deadline owner.

12 tracked deadline types:

Deadline Type Regulatory Basis
PCI_DSS_LEVEL1_ANNUAL_ROC PCI DSS Req 12.3.2 — annual Report on Compliance by qualified QSA
PCI_DSS_QUARTERLY_SCAN PCI DSS Req 11.3.2 — external ASV vulnerability scan, quarterly
CCPA_PRIVACY_POLICY_ANNUAL CCPA §1798.130(a)(5) — policy must reflect current practices within 12 months
CCPA_DSAR_RESPONSE_CALENDAR CCPA §1798.130(a)(2) — 45-day response window (extendable 45 days)
ADA_WCAG_21_AUDIT_ANNUAL ADA Title III DOJ 2024 final rule — WCAG 2.1 AA for public-facing ecomm
CPSC_RECALL_READINESS_ANNUAL 15 USC §2064(b)(3) — marketplace recall response procedures
FTC_ENDORSEMENT_GUIDE_REVIEW FTC 16 CFR Part 255 (updated 2023) — material disclosure review
STATE_SALES_TAX_NEXUS_FILING South Dakota v. Wayfair (2018) — economic nexus filing by state deadline
GDPR_ART28_DPA_RENEWAL GDPR Art. 28 — data processing agreement with EU customer controllers
SOC2_TYPE2_RENEWAL AICPA SOC 2 — Type II audit for enterprise procurement requirements
ISO27001_SURVEILLANCE ISO/IEC 27001:2022 — annual surveillance audit between 3-year cycles
ANNUAL_PENTEST PCI DSS Req 11.4.3 + SOC 2 CC7.1 — annual pentest of cardholder data environment
{
  "name": "RetailTech SaaS \u2014 PCI/CCPA/ADA/CPSC/FTC Compliance Deadline Tracker",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1-5"
            }
          ]
        }
      },
      "id": "c1",
      "name": "Weekdays 8 AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "YOUR_SHEET_ID",
          "mode": "id"
        },
        "sheetName": "compliance_deadlines",
        "options": {
          "headerRow": 1
        }
      },
      "id": "c2",
      "name": "Read Deadlines from Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "\nconst today = new Date();\nconst alerts = [];\nfor (const item of $input.all()) {\n  const r = item.json;\n  if (!r.deadline_date || !r.deadline_type) continue;\n  const daysLeft = Math.ceil((new Date(r.deadline_date) - today) / 86400000);\n  let severity = null;\n  if      (daysLeft <  0)  severity = 'OVERDUE';\n  else if (daysLeft <= 7)  severity = 'CRITICAL';\n  else if (daysLeft <= 21) severity = 'URGENT';\n  else if (daysLeft <= 45) severity = 'WARNING';\n  else if (daysLeft <= 90) severity = 'NOTICE';\n  if (!severity) continue;\n  const typeAnnotations = {\n    PCI_DSS_LEVEL1_ANNUAL_ROC:        'PCI DSS Req 12.3.2 \u2014 Level 1 merchants require annual Report on Compliance by QSA',\n    PCI_DSS_QUARTERLY_SCAN:           'PCI DSS Req 11.3.2 \u2014 external vulnerability scan by ASV, quarterly',\n    CCPA_PRIVACY_POLICY_ANNUAL:       'CCPA \u00a71798.130(a)(5) \u2014 privacy policy must reflect practices within 12 months',\n    CCPA_DSAR_RESPONSE_CALENDAR:      'CCPA \u00a71798.130(a)(2) \u2014 45-day response window, extendable 45 days with notice',\n    ADA_WCAG_21_AUDIT_ANNUAL:         'ADA Title III \u2014 DOJ 2024 final rule 28 CFR Part 36 mandates WCAG 2.1 AA for ecomm',\n    CPSC_RECALL_READINESS_ANNUAL:     '15 USC \u00a72064(b)(3) \u2014 marketplace operators must report known defects within 24 hours',\n    FTC_ENDORSEMENT_GUIDE_REVIEW:     'FTC 16 CFR Part 255 \u2014 updated 2023 Endorsement Guides require material disclosure review',\n    STATE_SALES_TAX_NEXUS_FILING:     'South Dakota v. Wayfair (2018) \u2014 economic nexus threshold filings by state deadline',\n    GDPR_ART28_DPA_RENEWAL:           'GDPR Art. 28 \u2014 data processing agreement renewal with EU customer controllers',\n    SOC2_TYPE2_RENEWAL:               'AICPA SOC 2 \u2014 Type II audit renewal for enterprise retailer procurement requirements',\n    ISO27001_SURVEILLANCE:            'ISO/IEC 27001:2022 \u2014 annual surveillance audit between 3-year recertification cycles',\n    ANNUAL_PENTEST:                   'PCI DSS Req 11.4.3 + SOC 2 CC7.1 \u2014 annual penetration test of cardholder data environment'\n  };\n  const alert_key = `${r.deadline_type}_${r.deadline_date}`;\n  if (r.alert_sent_for === alert_key) continue; // dedup\n  alerts.push({\n    deadline_type: r.deadline_type,\n    deadline_date: r.deadline_date,\n    days_left: daysLeft,\n    severity,\n    owner_email: r.owner_email,\n    owner_slack: r.owner_slack,\n    regulatory_note: typeAnnotations[r.deadline_type] || r.deadline_type,\n    row_index: r._rowIndex,\n    alert_key\n  });\n}\nif (alerts.length === 0) return [{ no_alerts: true }];\nreturn alerts;\n"
      },
      "id": "c3",
      "name": "Classify Urgency",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        680,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.no_alerts }}",
              "value2": true
            }
          ]
        }
      },
      "id": "c4",
      "name": "Any Alerts?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        900,
        300
      ]
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": "#compliance-deadlines",
        "text": "{{ $json.severity === 'OVERDUE' ? '\ud83d\udd34 OVERDUE' : $json.severity === 'CRITICAL' ? '\ud83d\udd34 CRITICAL' : $json.severity === 'URGENT' ? '\ud83d\udfe0 URGENT' : $json.severity === 'WARNING' ? '\ud83d\udfe1 WARNING' : '\ud83d\udd35 NOTICE' }} *{{ $json.deadline_type }}*\nDue: {{ $json.deadline_date }} ({{ $json.days_left }} days)\nRegulatory note: {{ $json.regulatory_note }}\nOwner: {{ $json.owner_slack }}"
      },
      "id": "c5",
      "name": "Slack Alert",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [
        1120,
        200
      ]
    },
    {
      "parameters": {
        "sendTo": "={{ $json.owner_email }}",
        "subject": "[{{ $json.severity }}] Compliance deadline: {{ $json.deadline_type }} due {{ $json.deadline_date }}",
        "emailType": "html",
        "message": "<p><strong>{{ $json.severity }}: {{ $json.deadline_type }}</strong></p><p>Due date: {{ $json.deadline_date }} ({{ $json.days_left }} days remaining)</p><p>Regulatory note: {{ $json.regulatory_note }}</p>"
      },
      "id": "c6",
      "name": "Email Owner",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        1120,
        400
      ]
    }
  ],
  "connections": {
    "Weekdays 8 AM": {
      "main": [
        [
          {
            "node": "Read Deadlines from Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Deadlines from Sheets": {
      "main": [
        [
          {
            "node": "Classify Urgency",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Urgency": {
      "main": [
        [
          {
            "node": "Any Alerts?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Any Alerts?": {
      "main": [
        [],
        [
          {
            "node": "Slack Alert",
            "type": "main",
            "index": 0
          },
          {
            "node": "Email Owner",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 4: Retail Compliance Incident Pipeline

When a compliance incident hits — a cardholder data breach, a CCPA request for data deletion under duress, an ADA demand letter, a CPSC recall report, an FTC enforcement inquiry — every hour counts. This pipeline ingests the incident via webhook, classifies it, computes the regulatory response deadline, fires Slack alerts to #compliance-incidents, logs to an incident register, and responds to the triggering system immediately.

8 incident types with response windows:

Incident Type Response Window Regulatory Basis
PCI_DSS_CARDHOLDER_DATA_BREACH 72h PCI DSS Req 12.10.4 — acquiring bank notification + state AG breach notification laws
CCPA_DATA_BREACH_72HR 72h CCPA §1798.82 — California AG notification for 500+ consumer breaches
ADA_TITLE_III_DEMAND_LETTER 72h $4,000/visit statutory damages under CA Unruh Act — immediate legal review
CPSC_PRODUCT_RECALL_NOTICE 24h 15 USC §2064(b)(3) — 24h CPSC Section 15(b) report for substantial hazards
FTC_ENDORSEMENT_VIOLATION 48h FTC Act §5 / 16 CFR Part 255 — $51,744/violation civil penalty
GDPR_ARTICLE_83_FINE_NOTICE 72h GDPR Art. 33 — 72h supervisory authority notification
STATE_AG_CONSUMER_COMPLAINT 48h State consumer protection — 48h acknowledgment to avoid AG escalation
MARKETPLACE_SELLER_FRAUD_ALERT 4h INFORM Consumers Act (15 USC §45f) — seller verification obligations
{
  "name": "RetailTech SaaS \u2014 Retail Compliance Incident Pipeline",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "retail-compliance-incident",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "d1",
      "name": "Compliance Incident Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "\nconst d = $json.body;\nconst incidentType = d.incident_type;\nconst responseWindows = {\n  PCI_DSS_CARDHOLDER_DATA_BREACH:   { hours: 72,  label: 'PCI DSS Req 12.10.4 \u2014 72h to Visa/Mastercard acquiring bank + state AG notification laws (most states 30\u201372h)' },\n  CCPA_DATA_BREACH_72HR:            { hours: 72,  label: 'CCPA \u00a71798.82 (as amended by CPRA) \u2014 72h notice to California AG for breaches of 500+ consumers' },\n  ADA_TITLE_III_DEMAND_LETTER:      { hours: 72,  label: 'ADA Title III \u2014 72h legal review required; $4,000/visit statutory damages + attorney fees under California Unruh Act' },\n  CPSC_PRODUCT_RECALL_NOTICE:       { hours: 24,  label: '15 USC \u00a72064(b)(3) \u2014 24h CPSC Section 15(b) report for marketplace operators aware of substantial product hazards' },\n  FTC_ENDORSEMENT_VIOLATION:        { hours: 48,  label: 'FTC Act \u00a75 / 16 CFR Part 255 \u2014 48h to remediate deceptive endorsement; $51,744/violation civil penalty' },\n  GDPR_ARTICLE_83_FINE_NOTICE:      { hours: 72,  label: 'GDPR Art. 33 \u2014 72h supervisory authority notification for EU consumer data breach' },\n  STATE_AG_CONSUMER_COMPLAINT:      { hours: 48,  label: 'State consumer protection AG \u2014 48h acknowledgment recommended for CFPB/state AG escalation avoidance' },\n  MARKETPLACE_SELLER_FRAUD_ALERT:   { hours: 4,   label: 'Internal SLA: 4h marketplace fraud containment; INFORM Consumers Act (15 USC \u00a745f) seller verification obligations' }\n};\nconst cfg = responseWindows[incidentType] || { hours: 48, label: 'Standard 48h response window' };\nconst deadline = new Date(Date.now() + cfg.hours * 3600000).toISOString();\nreturn [{\n  incident_id: `INC-${Date.now()}`,\n  incident_type: incidentType,\n  severity: d.severity || (cfg.hours <= 4 ? 'CRITICAL' : cfg.hours <= 24 ? 'HIGH' : 'MEDIUM'),\n  description: d.description || '',\n  reporter: d.reporter_email || 'unknown',\n  response_hours: cfg.hours,\n  response_deadline: deadline,\n  regulatory_label: cfg.label,\n  ts: new Date().toISOString()\n}];\n"
      },
      "id": "d2",
      "name": "Classify Incident + Set Deadline",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": "#compliance-incidents",
        "text": "\ud83d\udea8 *COMPLIANCE INCIDENT: {{ $json.incident_type }}*\nID: {{ $json.incident_id }}\nSeverity: {{ $json.severity }}\nResponse deadline: {{ $json.response_deadline }}\nRegulatory obligation: {{ $json.regulatory_label }}\nReporter: {{ $json.reporter }}"
      },
      "id": "d3",
      "name": "Slack \u2014 Incident Alert",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [
        680,
        200
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_SHEET_ID",
          "mode": "id"
        },
        "sheetName": "compliance_incidents",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "incident_id": "={{ $json.incident_id }}",
            "incident_type": "={{ $json.incident_type }}",
            "severity": "={{ $json.severity }}",
            "response_deadline": "={{ $json.response_deadline }}",
            "regulatory_label": "={{ $json.regulatory_label }}",
            "ts": "={{ $json.ts }}"
          }
        }
      },
      "id": "d4",
      "name": "Log to Incident Register",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        680,
        400
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "{\"status\":\"logged\",\"incident_id\":\"{{ $json.incident_id }}\",\"response_deadline\":\"{{ $json.response_deadline }}\"}"
      },
      "id": "d5",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        460,
        100
      ]
    }
  ],
  "connections": {
    "Compliance Incident Webhook": {
      "main": [
        [
          {
            "node": "Classify Incident + Set Deadline",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Incident + Set Deadline": {
      "main": [
        [
          {
            "node": "Slack \u2014 Incident Alert",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log to Incident Register",
            "type": "main",
            "index": 0
          },
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 5: Weekly RetailTech SaaS KPI Dashboard

Every Monday at 8 AM, this workflow pulls platform metrics and compliance incident counts from Google Sheets, calculates week-over-week deltas, builds an HTML table, and emails the CEO with CISO BCC.

The CISO BCC is intentional: PCI DSS Req 12.4.2 requires that executive management review security metrics and compliance posture. A weekly email with CISO in BCC creates an auditable record that executive oversight is happening — without requiring a separate meeting.

KPIs reported: MRR, GMV, active retailers, compliance incident count, API uptime %, week-over-week delta with ⚠️ flags on >10% swings.

{
  "name": "RetailTech SaaS \u2014 Weekly RetailTech KPI Dashboard",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1"
            }
          ]
        }
      },
      "id": "e1",
      "name": "Monday 8 AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "YOUR_SHEET_ID",
          "mode": "id"
        },
        "sheetName": "platform_metrics",
        "options": {
          "headerRow": 1
        }
      },
      "id": "e2",
      "name": "Read Platform Metrics",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        460,
        200
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "YOUR_SHEET_ID",
          "mode": "id"
        },
        "sheetName": "compliance_incidents",
        "options": {
          "headerRow": 1
        }
      },
      "id": "e3",
      "name": "Read Compliance Incidents",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        460,
        400
      ]
    },
    {
      "parameters": {
        "mode": "combine",
        "mergeByFields": {
          "values": [
            {
              "field1": "week",
              "field2": "week"
            }
          ]
        },
        "options": {}
      },
      "id": "e4",
      "name": "Merge Metrics",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 2,
      "position": [
        680,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "\nconst rows = $input.all().map(i => i.json);\nconst latest = rows[rows.length - 1] || {};\nconst prev    = rows[rows.length - 2] || {};\nconst pct = (a, b) => b ? (((a-b)/b)*100).toFixed(1)+'%' : 'N/A';\nconst flag = v => parseFloat(v) > 10 ? ' \u26a0\ufe0f' : '';\nconst mrr     = latest.mrr_usd || 0;\nconst mrrPrev = prev.mrr_usd || 0;\nconst gmv     = latest.gmv_usd || 0;\nconst cIncidents = rows.filter(r => r.week === latest.week && r.incident_type).length;\nconst html = [\n  '<h2>RetailTech SaaS \u2014 Weekly KPI Report</h2>',\n  '<table border=\"1\" cellpadding=\"6\">',\n  '<tr><th>Metric</th><th>This Week</th><th>Prev Week</th><th>WoW</th></tr>',\n  `<tr><td>MRR ($)</td><td>${mrr}</td><td>${mrrPrev}</td><td>${pct(mrr,mrrPrev)}${flag(pct(mrr,mrrPrev))}</td></tr>`,\n  `<tr><td>GMV ($)</td><td>${gmv}</td><td>${prev.gmv_usd||0}</td><td>${pct(gmv,prev.gmv_usd||0)}</td></tr>`,\n  `<tr><td>Active Retailers</td><td>${latest.active_retailers||0}</td><td>${prev.active_retailers||0}</td><td>${pct(latest.active_retailers||0,prev.active_retailers||0)}</td></tr>`,\n  `<tr><td>PCI Incidents</td><td colspan=\"3\">${cIncidents} this week</td></tr>`,\n  `<tr><td>API Uptime</td><td>${latest.api_uptime_pct||'\u2014'}%</td><td>${prev.api_uptime_pct||'\u2014'}%</td><td>\u2014</td></tr>`,\n  '</table>',\n  '<p style=\"font-size:11px;color:#666\">BCC: CISO \u2014 PCI DSS Req 12.4.2 weekly review obligation for Level 1 merchants</p>'\n].join('');\nreturn [{ html, week: latest.week }];\n"
      },
      "id": "e5",
      "name": "Build KPI HTML",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        900,
        300
      ]
    },
    {
      "parameters": {
        "sendTo": "ceo@yourplatform.io",
        "subject": "RetailTech SaaS Weekly KPI \u2014 {{ $json.week }}",
        "emailType": "html",
        "message": "={{ $json.html }}"
      },
      "id": "e6",
      "name": "Email CEO + CISO",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        1120,
        300
      ]
    }
  ],
  "connections": {
    "Monday 8 AM": {
      "main": [
        [
          {
            "node": "Read Platform Metrics",
            "type": "main",
            "index": 0
          },
          {
            "node": "Read Compliance Incidents",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Platform Metrics": {
      "main": [
        [
          {
            "node": "Merge Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Compliance Incidents": {
      "main": [
        [
          {
            "node": "Merge Metrics",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge Metrics": {
      "main": [
        [
          {
            "node": "Build KPI HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build KPI HTML": {
      "main": [
        [
          {
            "node": "Email CEO + CISO",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

n8n vs Zapier / Make for RetailTech SaaS

Requirement n8n (self-hosted) Zapier / Make
PCI DSS cardholder data Stays in your VPC — no scope expansion External CSP = PCI DSS scope expansion finding
CCPA data processing You control data residency, deletion Third party = additional CCPA data map entry
ADA incident audit trail Git-versioned JSON = immutable evidence Proprietary logs, limited export
CPSC recall 24h SLA Self-hosted: no third-party latency risk Dependent on Zapier/Make uptime
FTC enforcement defense Complete execution logs on your infrastructure Partial logs, may require legal hold to vendor
Cost at scale Flat-rate self-hosted Per-task pricing compounds with high-volume retail events

Get the complete FlowKit RetailTech bundle

These five workflows — plus 10 more production-ready n8n automations for sales, ops, and growth — are available in the FlowKit n8n Automation Templates bundle at stripeai.gumroad.com.

Individual templates from $12. Full bundle (15 templates) at $97.


Tags: n8n, ecommerce, automation, compliance

Top comments (0)