If you're building EdTech or Education SaaS, you already know your customers' student records carry weight.
FERPA, COPPA, IDEA, and Title IV aren't just compliance checkboxes — they're procurement gates.
School district IT departments ask one question before signing: where does student data go?
This guide covers 5 import-ready n8n workflows for EdTech SaaS vendors, with full JSON.
Each maps to a real compliance obligation your customers are already managing.
Why EdTech SaaS Vendors Are Moving Automation Off Cloud iPaaS
The core issue is FERPA §99.31(a)(1) — the school official exception.
FERPA allows schools to share student records with 'school officials' without consent.
The Department of Education defines school officials as persons under the school's direct control.
Cloud iPaaS (Zapier, Make) is not under your customer's direct control.
When your automation workflow routes student record data through a cloud iPaaS:
- The iPaaS becomes a third-party recipient
- The school official exception does not apply
- Every data transit = unauthorized disclosure under §99.30
- OCR complaint = investigation, no grace period
This is an architecture decision, not a security incident.
Self-hosted n8n eliminates the disclosure by keeping automation inside your customer's infrastructure boundary.
FERPA §99.31 direct control test — the 5 tiers most at risk:
| Tier | Primary Regulation | Fastest Clock | Disclosure Risk |
|---|---|---|---|
| Enterprise SIS | FERPA §99.31 | IMMEDIATE OCR | School official exception fails |
| LMS SaaS | FERPA §99.10 | 45d inspection window | Grade data unauthorized disclosure |
| Special Ed | FERPA + IDEA | IMMEDIATE OCR | IEP records = highest sensitivity |
| Early Childhood | COPPA §312.5 | IMMEDIATE FTC | Under-13 = $50,120/day/violation |
| Financial Aid | Title IV HEA §484B | IMMEDIATE DOE audit | DOE subpoenas vendor directly |
The 7 Customer Tiers
{
"customer_tiers": [
{
"tier": "ENTERPRISE_STUDENT_INFO_SYSTEM",
"compliance_flags": [
"FERPA_SCHOOL_OFFICIAL",
"SOC2_REQUIRED"
],
"fastest_clock": "FERPA_UNAUTHORIZED_DISCLOSURE IMMEDIATE OCR"
},
{
"tier": "MIDMARKET_LMS_SAAS",
"compliance_flags": [
"FERPA_SCHOOL_OFFICIAL",
"SOC2_REQUIRED"
],
"fastest_clock": "FERPA_OCR_COMPLAINT_RESPONSE IMMEDIATE"
},
{
"tier": "EDTECH_ASSESSMENT_SAAS",
"compliance_flags": [
"FERPA_SCHOOL_OFFICIAL"
],
"fastest_clock": "FERPA_UNAUTHORIZED_DISCLOSURE IMMEDIATE OCR"
},
{
"tier": "SPECIAL_ED_COMPLIANCE_SAAS",
"compliance_flags": [
"FERPA_SCHOOL_OFFICIAL",
"IDEA_COVERED_INSTITUTION"
],
"fastest_clock": "FERPA_UNAUTHORIZED_DISCLOSURE IMMEDIATE \u2014 IEP records"
},
{
"tier": "HIGHER_ED_FINANCIAL_AID_SAAS",
"compliance_flags": [
"FERPA_SCHOOL_OFFICIAL",
"TITLE_IV_INSTITUTION"
],
"fastest_clock": "TITLE_IV_DOE_AUDIT_NOTICE IMMEDIATE upon notice"
},
{
"tier": "EARLY_CHILDHOOD_EDTECH",
"compliance_flags": [
"COPPA_COVERED_OPERATOR",
"FERPA_SCHOOL_OFFICIAL"
],
"fastest_clock": "COPPA_PARENTAL_CONSENT_VERIFICATION IMMEDIATE before collection"
},
{
"tier": "EDTECH_STARTUP",
"compliance_flags": [
"FERPA_SCHOOL_OFFICIAL"
],
"fastest_clock": "FERPA_UNAUTHORIZED_DISCLOSURE IMMEDIATE OCR"
}
]
}
Workflow 1 — Tier-Segmented Customer Onboarding Drip
Classifies new trials by tier and compliance flags. Sends Day 0 welcome with compliance-specific note, Day 3 feature email, Day 7 trial-end sequence.
The Day 0 note for EARLY_CHILDHOOD_EDTECH includes the COPPA §312.5 operator definition warning.
The Day 0 note for SPECIAL_ED_COMPLIANCE_SAAS includes the IDEA 34 CFR §300.612 IEP protection note.
{
"name": "EdTech SaaS \u2014 Tier-Segmented Customer Onboarding Drip",
"nodes": [
{
"id": "1",
"name": "Webhook \u2014 trial_started",
"type": "n8n-nodes-base.webhook",
"parameters": {
"path": "edtech-trial-started",
"responseMode": "onReceived"
},
"position": [
0,
0
]
},
{
"id": "2",
"name": "Code \u2014 Tier + Flag Classifier",
"type": "n8n-nodes-base.code",
"parameters": {
"jsCode": "\nconst d = $input.first().json;\nconst email = d.email || '';\nconst plan = (d.plan || '').toLowerCase();\nconst userCount = d.user_count || 0;\nconst product = (d.product_type || '').toLowerCase();\n\n// Tier classification\nlet tier = 'EDTECH_STARTUP';\nif (userCount >= 5000 || plan === 'enterprise') tier = 'ENTERPRISE_STUDENT_INFO_SYSTEM';\nelse if (product.includes('lms') && userCount >= 500) tier = 'MIDMARKET_LMS_SAAS';\nelse if (product.includes('assessment') || product.includes('quiz')) tier = 'EDTECH_ASSESSMENT_SAAS';\nelse if (product.includes('iep') || product.includes('special')) tier = 'SPECIAL_ED_COMPLIANCE_SAAS';\nelse if (product.includes('financial_aid') || product.includes('fafsa')) tier = 'HIGHER_ED_FINANCIAL_AID_SAAS';\nelse if (product.includes('k12') || product.includes('early') || d.min_age <= 13) tier = 'EARLY_CHILDHOOD_EDTECH';\n\n// Compliance flags\nconst flags = [];\nif (tier !== 'EDTECH_STARTUP') flags.push('FERPA_SCHOOL_OFFICIAL');\nif (d.min_age <= 13 || product.includes('k12') || product.includes('early')) flags.push('COPPA_COVERED_OPERATOR');\nif (product.includes('iep') || product.includes('special')) flags.push('IDEA_COVERED_INSTITUTION');\nif (product.includes('financial_aid') || product.includes('fafsa')) flags.push('TITLE_IV_INSTITUTION');\nif ((d.state || '').toUpperCase() === 'CA') flags.push('CA_SOPIPA_COVERED');\nif ((d.state || '').toUpperCase() === 'NY') flags.push('NY_ED_LAW_2D_COVERED');\nif (userCount >= 250 || tier === 'ENTERPRISE_STUDENT_INFO_SYSTEM') flags.push('SOC2_REQUIRED');\n\n// Tier-specific Day 0 note\nconst day0Notes = {\n ENTERPRISE_STUDENT_INFO_SYSTEM: 'FERPA \u00a799.31(a)(1) school official exception requires direct control \u2014 cloud iPaaS routing student records = unauthorized third-party disclosure. Self-hosted n8n eliminates this exposure.',\n MIDMARKET_LMS_SAAS: 'FERPA \u00a799.31 direct control test: automation workflows routing student activity data through cloud iPaaS fail the school official exception. Self-hosted n8n keeps data inside your perimeter.',\n EDTECH_ASSESSMENT_SAAS: 'FERPA \u00a799.30 consent requirement applies to assessment record disclosures. Cloud iPaaS as data processor = third-party disclosure without school official exception coverage.',\n SPECIAL_ED_COMPLIANCE_SAAS: 'IDEA 34 CFR \u00a7300.612 \u2014 IEP and eligibility records carry FERPA protection. Self-hosted n8n ensures special education data never transits external cloud infrastructure.',\n HIGHER_ED_FINANCIAL_AID_SAAS: 'Title IV HEA \u00a7484B financial aid data + FERPA protection. DOE program review subpoenas vendor directly \u2014 self-hosted n8n keeps financial aid workflow data inside your boundary.',\n EARLY_CHILDHOOD_EDTECH: 'COPPA 16 CFR \u00a7312.5 \u2014 operator definition follows data flow, not website ownership. Cloud iPaaS routing children's data = COPPA operator by architecture. FTC enforcement: $50,120/day per violation.',\n EDTECH_STARTUP: 'FERPA/COPPA compliance architecture decision: cloud iPaaS routing student records = third-party disclosure. Self-hosted n8n is the architecture that satisfies school district procurement requirements.'\n};\n\nreturn [{json: {\n ...d,\n tier,\n flags,\n day0_compliance_note: day0Notes[tier] || day0Notes.EDTECH_STARTUP\n}}];\n"
},
"position": [
250,
0
]
},
{
"id": "3",
"name": "Sheets \u2014 Log Trial",
"type": "n8n-nodes-base.googleSheets",
"parameters": {
"operation": "appendOrUpdate",
"documentId": "YOUR_SHEET_ID",
"sheetName": "trials",
"dataMode": "autoMapInputData"
},
"position": [
500,
0
]
},
{
"id": "4",
"name": "Gmail \u2014 Day 0 Welcome",
"type": "n8n-nodes-base.gmail",
"parameters": {
"operation": "send",
"toEmail": "={{ $json.email }}",
"subject": "Welcome to {{ $json.product_name }} \u2014 your automation setup guide",
"emailType": "html",
"message": "={{ '<p>Hi ' + $json.first_name + ',</p><p>You are now on the ' + $json.plan + ' trial.</p><p><strong>Compliance Note:</strong> ' + $json.day0_compliance_note + '</p><p>Your onboarding checklist is ready. Reply to this email with any questions.</p>' }}"
},
"position": [
750,
0
]
},
{
"id": "5",
"name": "Wait \u2014 3 days",
"type": "n8n-nodes-base.wait",
"parameters": {
"amount": 3,
"unit": "days"
},
"position": [
1000,
0
]
},
{
"id": "6",
"name": "Gmail \u2014 Day 3 Feature",
"type": "n8n-nodes-base.gmail",
"parameters": {
"operation": "send",
"toEmail": "={{ $json.email }}",
"subject": "The automation your {{ $json.tier.replace(/_/g, ' ').toLowerCase() }} customers ask about most",
"emailType": "html",
"message": "<p>Based on what similar vendors are automating: compliance deadline tracking, API health monitoring, and incident response pipelines. All run inside your infrastructure \u2014 no student data egressing to third-party servers.</p>"
},
"position": [
1250,
0
]
},
{
"id": "7",
"name": "Wait \u2014 4 days",
"type": "n8n-nodes-base.wait",
"parameters": {
"amount": 4,
"unit": "days"
},
"position": [
1500,
0
]
},
{
"id": "8",
"name": "Gmail \u2014 Day 7 Trial End",
"type": "n8n-nodes-base.gmail",
"parameters": {
"operation": "send",
"toEmail": "={{ $json.email }}",
"subject": "Your trial ends in 48 hours \u2014 3 things to do before then",
"emailType": "html",
"message": "<p>Export your workflow JSON, schedule a 15-min call with our team, and review the FERPA/COPPA self-hosting checklist we prepared for your tier.</p>"
},
"position": [
1750,
0
]
}
],
"connections": {
"Webhook \u2014 trial_started": {
"main": [
[
{
"node": "Code \u2014 Tier + Flag Classifier",
"type": "main",
"index": 0
}
]
]
},
"Code \u2014 Tier + Flag Classifier": {
"main": [
[
{
"node": "Sheets \u2014 Log Trial",
"type": "main",
"index": 0
}
]
]
},
"Sheets \u2014 Log Trial": {
"main": [
[
{
"node": "Gmail \u2014 Day 0 Welcome",
"type": "main",
"index": 0
}
]
]
},
"Gmail \u2014 Day 0 Welcome": {
"main": [
[
{
"node": "Wait \u2014 3 days",
"type": "main",
"index": 0
}
]
]
},
"Wait \u2014 3 days": {
"main": [
[
{
"node": "Gmail \u2014 Day 3 Feature",
"type": "main",
"index": 0
}
]
]
},
"Gmail \u2014 Day 3 Feature": {
"main": [
[
{
"node": "Wait \u2014 4 days",
"type": "main",
"index": 0
}
]
]
},
"Wait \u2014 4 days": {
"main": [
[
{
"node": "Gmail \u2014 Day 7 Trial End",
"type": "main",
"index": 0
}
]
]
}
}
}
Workflow 2 — FERPA / COPPA / IDEA / Title IV Deadline Tracker
Reads a Google Sheet of compliance deadlines daily. Classifies as OVERDUE / CRITICAL / URGENT / WARNING / NOTICE. Routes critical items to Slack #compliance-urgent and emails the owner.
12 deadline types covered:
| Deadline Type | Statute | Window |
|---|---|---|
| FERPA_RECORDS_INSPECTION_REQUEST | 34 CFR §99.10 | 45 calendar days |
| FERPA_AMENDMENT_REQUEST | 34 CFR §99.20 | 30 days (DOE guidance) |
| FERPA_OCR_COMPLAINT_RESPONSE | 34 CFR §99 | IMMEDIATE upon OCR notification |
| COPPA_PARENTAL_CONSENT_VERIFICATION | 16 CFR §312.5 | IMMEDIATE — before collection |
| COPPA_DATA_DELETION_REQUEST | 16 CFR §312.6 | 30 days |
| IDEA_IEP_ANNUAL_REVIEW | 34 CFR §300.324 | Annual |
| IDEA_ELIGIBILITY_EVALUATION | 34 CFR §300.301 | 60 calendar days |
| TITLE_IV_SAP_REVIEW | HEA §484 | Annual |
| CA_SOPIPA_DELETE_REQUEST | CA Ed Code §49073.1 | 30d on transfer/graduation |
| NY_ED_LAW_2D_DISCLOSURE | NY Ed Law §2-d | 30 days notification |
| SOC2_TYPE2_RENEWAL | — | Annual |
| ANNUAL_PENTEST | — | Annual |
{
"name": "EdTech SaaS \u2014 FERPA/COPPA/IDEA/Title IV Deadline Tracker",
"nodes": [
{
"id": "1",
"name": "Schedule \u2014 Daily 8AM",
"type": "n8n-nodes-base.scheduleTrigger",
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 8 * * *"
}
]
}
},
"position": [
0,
0
]
},
{
"id": "2",
"name": "Sheets \u2014 Read Deadlines",
"type": "n8n-nodes-base.googleSheets",
"parameters": {
"operation": "read",
"documentId": "YOUR_SHEET_ID",
"sheetName": "compliance_deadlines"
},
"position": [
250,
0
]
},
{
"id": "3",
"name": "Code \u2014 Urgency Classifier",
"type": "n8n-nodes-base.code",
"parameters": {
"jsCode": "\nconst today = new Date();\nconst urgent = [];\nfor (const row of $input.all()) {\n const d = row.json;\n const due = new Date(d.due_date);\n const days = Math.ceil((due - today) / 86400000);\n const deadlineTypes = [\n 'FERPA_RECORDS_INSPECTION_REQUEST', // 45d \u00a799.10\n 'FERPA_AMENDMENT_REQUEST', // 30d \u00a799.20\n 'FERPA_OCR_COMPLAINT_RESPONSE', // IMMEDIATE upon OCR notification\n 'COPPA_PARENTAL_CONSENT_VERIFICATION',// IMMEDIATE before collection \u00a7312.5\n 'COPPA_DATA_DELETION_REQUEST', // 30d \u00a7312.6\n 'IDEA_IEP_ANNUAL_REVIEW', // Annual \u00a7300.324\n 'IDEA_ELIGIBILITY_EVALUATION', // 60d \u00a7300.301\n 'TITLE_IV_SAP_REVIEW', // Annual\n 'CA_SOPIPA_DELETE_REQUEST', // 30d on transfer/graduation\n 'NY_ED_LAW_2D_DISCLOSURE', // 30d notification \u00a72-d\n 'SOC2_TYPE2_RENEWAL', // Annual\n 'ANNUAL_PENTEST' // Annual\n ];\n let urgency = 'UPCOMING';\n if (days < 0) urgency = 'OVERDUE';\n else if (days <= 3) urgency = 'CRITICAL';\n else if (days <= 7) urgency = 'URGENT';\n else if (days <= 14) urgency = 'WARNING';\n else if (days <= 30) urgency = 'NOTICE';\n if (days <= 30) urgent.push({...d, days_remaining: days, urgency});\n}\nreturn urgent.map(r => ({json: r}));\n"
},
"position": [
500,
0
]
},
{
"id": "4",
"name": "IF \u2014 Critical or Overdue",
"type": "n8n-nodes-base.if",
"parameters": {
"conditions": {
"options": {
"caseSensitive": false
},
"conditions": [
{
"leftValue": "={{ $json.urgency }}",
"operator": {
"type": "string",
"operation": "equals"
},
"rightValue": "CRITICAL"
},
{
"leftValue": "={{ $json.urgency }}",
"operator": {
"type": "string",
"operation": "equals"
},
"rightValue": "OVERDUE"
}
],
"combinator": "or"
}
},
"position": [
750,
0
]
},
{
"id": "5",
"name": "Slack \u2014 Critical Alert",
"type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#compliance-urgent",
"text": "={{ '\ud83d\udea8 ' + $json.urgency + ': ' + $json.deadline_type + ' \u2014 ' + $json.customer_name + ' \u2014 ' + $json.days_remaining + 'd remaining. Owner: ' + $json.owner }}"
},
"position": [
1000,
100
]
},
{
"id": "6",
"name": "Gmail \u2014 Owner Notice",
"type": "n8n-nodes-base.gmail",
"parameters": {
"operation": "send",
"toEmail": "={{ $json.owner_email }}",
"subject": "={{ '[' + $json.urgency + '] ' + $json.deadline_type + ' \u2014 ' + $json.days_remaining + ' days' }}",
"emailType": "html",
"message": "={{ '<p><strong>' + $json.deadline_type + '</strong></p><p>Customer: ' + $json.customer_name + '</p><p>Due: ' + $json.due_date + ' (' + $json.days_remaining + ' days remaining)</p><p>Action required: ' + $json.action_required + '</p>' }}"
},
"position": [
1000,
-100
]
},
{
"id": "7",
"name": "Sheets \u2014 Log",
"type": "n8n-nodes-base.googleSheets",
"parameters": {
"operation": "appendOrUpdate",
"documentId": "YOUR_SHEET_ID",
"sheetName": "deadline_audit"
},
"position": [
1250,
0
]
}
],
"connections": {
"Schedule \u2014 Daily 8AM": {
"main": [
[
{
"node": "Sheets \u2014 Read Deadlines",
"type": "main",
"index": 0
}
]
]
},
"Sheets \u2014 Read Deadlines": {
"main": [
[
{
"node": "Code \u2014 Urgency Classifier",
"type": "main",
"index": 0
}
]
]
},
"Code \u2014 Urgency Classifier": {
"main": [
[
{
"node": "IF \u2014 Critical or Overdue",
"type": "main",
"index": 0
}
]
]
},
"IF \u2014 Critical or Overdue": {
"main": [
[
{
"node": "Slack \u2014 Critical Alert",
"type": "main",
"index": 0
}
],
[
{
"node": "Gmail \u2014 Owner Notice",
"type": "main",
"index": 0
}
]
]
},
"Slack \u2014 Critical Alert": {
"main": [
[
{
"node": "Sheets \u2014 Log",
"type": "main",
"index": 0
}
]
]
},
"Gmail \u2014 Owner Notice": {
"main": [
[
{
"node": "Sheets \u2014 Log",
"type": "main",
"index": 0
}
]
]
}
}
}
Workflow 3 — EdTech API Health Monitor
Checks SIS, LMS, assessment, and financial aid API endpoints every 15 minutes.
Each endpoint is mapped to its compliance context:
-
sis_api→ FERPA §99 — downtime creates student records access rights violation window -
financial_aid_api→ Title IV HEA §484B — DOE program review exposure during outage -
coppa_consent_api→ COPPA §312.5 — outage creates collection-without-consent gap
{
"name": "EdTech SaaS \u2014 API Health Monitor (SIS / LMS / Assessment / Financial Aid)",
"nodes": [
{
"id": "1",
"name": "Schedule \u2014 Every 15min",
"type": "n8n-nodes-base.scheduleTrigger",
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "*/15 * * * *"
}
]
}
},
"position": [
0,
0
]
},
{
"id": "2",
"name": "Sheets \u2014 Endpoints",
"type": "n8n-nodes-base.googleSheets",
"parameters": {
"operation": "read",
"documentId": "YOUR_SHEET_ID",
"sheetName": "api_endpoints"
},
"position": [
250,
0
]
},
{
"id": "3",
"name": "HTTP \u2014 Check Endpoint",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "={{ $json.endpoint_url }}",
"method": "GET",
"timeout": 10000,
"continueOnFail": true
},
"position": [
500,
0
]
},
{
"id": "4",
"name": "Code \u2014 Classify Health",
"type": "n8n-nodes-base.code",
"parameters": {
"jsCode": "\n// EdTech API endpoints with FERPA/COPPA compliance context:\n// sis_api = student records FERPA \u00a799 \u2014 downtime = access rights violation window\n// lms_api = course/grade data FERPA \u00a799.10 \u2014 inspection right 45d window\n// assessment_api = FERPA \u00a799 assessment records\n// financial_aid_api = Title IV HEA \u00a7484B \u2014 DOE program review exposure\n// coppa_consent_api = COPPA \u00a7312.5 \u2014 consent verification before collection\nconst item = $input.first().json;\nconst statusCode = item.statusCode || 0;\nconst endpoint = item.endpoint_name || 'unknown';\nconst complianceNote = {\n sis_api: 'FERPA \u00a799 student records \u2014 downtime creates access rights violation window',\n lms_api: 'FERPA \u00a799.10 inspection right \u2014 45d statutory clock continues during outage',\n assessment_api: 'FERPA \u00a799 assessment records \u2014 grade data access rights unaffected by SLA',\n financial_aid_api: 'Title IV HEA \u00a7484B \u2014 DOE program review subpoena exposure during outage',\n coppa_consent_api: 'COPPA \u00a7312.5 \u2014 consent verification gap = collection without verifiable consent = FTC violation'\n}[endpoint] || 'Student data compliance dependency';\nconst healthy = statusCode >= 200 && statusCode < 300;\nreturn [{json: {endpoint, statusCode, healthy, complianceNote, checked_at: new Date().toISOString()}}];\n"
},
"position": [
750,
0
]
},
{
"id": "5",
"name": "IF \u2014 Down",
"type": "n8n-nodes-base.if",
"parameters": {
"conditions": {
"conditions": [
{
"leftValue": "={{ $json.healthy }}",
"operator": {
"type": "boolean",
"operation": "false"
}
}
]
}
},
"position": [
1000,
0
]
},
{
"id": "6",
"name": "Slack \u2014 API Down Alert",
"type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#platform-ops",
"text": "={{ '\ud83d\udd34 API DOWN: ' + $json.endpoint + ' (HTTP ' + $json.statusCode + ') \u2014 Compliance note: ' + $json.complianceNote }}"
},
"position": [
1250,
100
]
},
{
"id": "7",
"name": "Sheets \u2014 Health Log",
"type": "n8n-nodes-base.googleSheets",
"parameters": {
"operation": "appendOrUpdate",
"documentId": "YOUR_SHEET_ID",
"sheetName": "api_health_log"
},
"position": [
1250,
-100
]
}
],
"connections": {
"Schedule \u2014 Every 15min": {
"main": [
[
{
"node": "Sheets \u2014 Endpoints",
"type": "main",
"index": 0
}
]
]
},
"Sheets \u2014 Endpoints": {
"main": [
[
{
"node": "HTTP \u2014 Check Endpoint",
"type": "main",
"index": 0
}
]
]
},
"HTTP \u2014 Check Endpoint": {
"main": [
[
{
"node": "Code \u2014 Classify Health",
"type": "main",
"index": 0
}
]
]
},
"Code \u2014 Classify Health": {
"main": [
[
{
"node": "IF \u2014 Down",
"type": "main",
"index": 0
}
]
]
},
"IF \u2014 Down": {
"main": [
[
{
"node": "Slack \u2014 API Down Alert",
"type": "main",
"index": 0
}
],
[
{
"node": "Sheets \u2014 Health Log",
"type": "main",
"index": 0
}
]
]
}
}
}
Workflow 4 — Incident Pipeline
Webhook-triggered. Classifies 8 incident types. Routes to Slack and emails compliance lead.
FERPA_UNAUTHORIZED_DISCLOSURE is the fastest clock — no statutory notification window.
OCR complaint = investigation begins immediately. Each unauthorized disclosure = separate FERPA violation.
COPPA_FTC_ENFORCEMENT_NOTICE carries $50,120/day per violation, no grace period.
| Incident Type | Clock | Statute |
|---|---|---|
| FERPA_UNAUTHORIZED_DISCLOSURE | IMMEDIATE OCR | 34 CFR §99 |
| COPPA_FTC_ENFORCEMENT_NOTICE | IMMEDIATE | 16 CFR §312 |
| IDEA_STATE_COMPLAINT | 60d | 34 CFR §300.152 |
| TITLE_IV_DOE_AUDIT_NOTICE | IMMEDIATE upon notice | HEA §498 |
| CA_SOPIPA_BREACH | 72h | CA Ed Code |
| DATA_BREACH_STUDENT_PII | 72h | State breach law |
| PARENTAL_RIGHTS_COMPLAINT | 30d | 34 CFR §99.20 |
| SYSTEM_OUTAGE | SLA | — |
{
"name": "EdTech SaaS \u2014 Incident Pipeline (FERPA / COPPA / IDEA / Title IV)",
"nodes": [
{
"id": "1",
"name": "Webhook \u2014 Incident",
"type": "n8n-nodes-base.webhook",
"parameters": {
"path": "edtech-incident",
"responseMode": "onReceived"
},
"position": [
0,
0
]
},
{
"id": "2",
"name": "Code \u2014 Classify Incident",
"type": "n8n-nodes-base.code",
"parameters": {
"jsCode": "\nconst d = $input.first().json;\nconst type = d.incident_type || 'UNKNOWN';\n// 8 EdTech incident types with response clocks:\nconst clocks = {\n FERPA_UNAUTHORIZED_DISCLOSURE: {\n clock: 'IMMEDIATE',\n note: 'FERPA \u00a799 \u2014 no statutory notification window. OCR complaint triggers investigation immediately. Corrective action plan required. Each disclosure = separate violation.',\n actions: ['Halt data flow', 'Notify FERPA compliance officer', 'Document disclosure scope', 'Prepare OCR response', 'Notify affected school district']\n },\n COPPA_FTC_ENFORCEMENT_NOTICE: {\n clock: 'IMMEDIATE',\n note: 'COPPA 16 CFR \u00a7312 \u2014 FTC enforcement notice. $50,120/day per violation. No grace period. Consent verification and data deletion required immediately.',\n actions: ['Cease collection from under-13 without consent', 'Document consent verification gap', 'Prepare FTC response', 'Notify parent/guardian', 'Engage privacy counsel']\n },\n IDEA_STATE_COMPLAINT: {\n clock: '60d \u00a7300.152',\n note: 'IDEA 34 CFR \u00a7300.152 \u2014 state education agency must resolve within 60 calendar days. IEP/eligibility records are FERPA-protected with additional IDEA overlay.',\n actions: ['Pull IEP records', 'Notify special education director', 'Document corrective action', 'Submit state response within 60d']\n },\n TITLE_IV_DOE_AUDIT_NOTICE: {\n clock: 'IMMEDIATE upon notice',\n note: 'Title IV HEA \u00a7498 \u2014 DOE program review. Financial aid processing records subpoenaed directly from vendor. Self-hosted n8n = records stay in institution boundary.',\n actions: ['Preserve financial aid workflow logs', 'Notify Title IV coordinator', 'Engage compliance counsel', 'Prepare document production']\n },\n CA_SOPIPA_BREACH: {\n clock: '72h',\n note: 'CA SOPIPA \u2014 school operator breach notification to school/district within 72h. K-12 student data covered by SOPIPA and FERPA simultaneously.',\n actions: ['Notify affected school district within 72h', 'Identify student data scope', 'Assess FERPA parallel obligation', 'Engage CA privacy counsel']\n },\n DATA_BREACH_STUDENT_PII: {\n clock: '72h state law',\n note: 'State breach notification law \u2014 student PII (name + email + DOB + grades) triggers notification. FERPA parallel obligation may apply.',\n actions: ['Notify affected institutions', 'Assess FERPA parallel', 'Engage breach counsel', 'Prepare regulatory notifications']\n },\n PARENTAL_RIGHTS_COMPLAINT: {\n clock: '30d reasonable time \u00a799.20',\n note: 'FERPA \u00a799.20 \u2014 amendment request. Reasonable time standard, DOE guidance: 30 days. Refusal must include right to hearing notice.',\n actions: ['Pull student record', 'Notify FERPA records officer', 'Respond within 30d or notify hearing right', 'Document decision']\n },\n SYSTEM_OUTAGE: {\n clock: 'SLA',\n note: 'Platform SLA. FERPA \u00a799.10 inspection right \u2014 45d statutory clock continues during outage. Access rights obligations unaffected by technical issues.',\n actions: ['Notify customer success', 'Post status page update', 'Track FERPA \u00a799.10 access windows for affected customers']\n }\n};\nconst info = clocks[type] || clocks.SYSTEM_OUTAGE;\nreturn [{json: {\n ...d,\n incident_type: type,\n response_clock: info.clock,\n compliance_note: info.note,\n required_actions: info.actions,\n logged_at: new Date().toISOString()\n}}];\n"
},
"position": [
250,
0
]
},
{
"id": "3",
"name": "Sheets \u2014 Log Incident",
"type": "n8n-nodes-base.googleSheets",
"parameters": {
"operation": "appendOrUpdate",
"documentId": "YOUR_SHEET_ID",
"sheetName": "incident_log"
},
"position": [
500,
0
]
},
{
"id": "4",
"name": "Slack \u2014 Incident Alert",
"type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#compliance-incident",
"text": "={{ '\ud83d\udea8 EdTech Incident: ' + $json.incident_type + ' | Clock: ' + $json.response_clock + ' | ' + $json.compliance_note }}"
},
"position": [
750,
0
]
},
{
"id": "5",
"name": "Gmail \u2014 Compliance Lead",
"type": "n8n-nodes-base.gmail",
"parameters": {
"operation": "send",
"toEmail": "compliance@yourcompany.com",
"subject": "={{ '[INCIDENT] ' + $json.incident_type + ' \u2014 ' + $json.response_clock + ' clock' }}",
"emailType": "html",
"message": "={{ '<p><strong>Incident Type:</strong> ' + $json.incident_type + '</p><p><strong>Clock:</strong> ' + $json.response_clock + '</p><p><strong>Note:</strong> ' + $json.compliance_note + '</p><p><strong>Actions:</strong> ' + $json.required_actions.join(', ') + '</p>' }}"
},
"position": [
1000,
0
]
}
],
"connections": {
"Webhook \u2014 Incident": {
"main": [
[
{
"node": "Code \u2014 Classify Incident",
"type": "main",
"index": 0
}
]
]
},
"Code \u2014 Classify Incident": {
"main": [
[
{
"node": "Sheets \u2014 Log Incident",
"type": "main",
"index": 0
}
]
]
},
"Sheets \u2014 Log Incident": {
"main": [
[
{
"node": "Slack \u2014 Incident Alert",
"type": "main",
"index": 0
}
]
]
},
"Slack \u2014 Incident Alert": {
"main": [
[
{
"node": "Gmail \u2014 Compliance Lead",
"type": "main",
"index": 0
}
]
]
}
}
}
Workflow 5 — Weekly EdTech KPI Dashboard
Monday 8AM. Queries Postgres for account counts by tier, MRR, compliance flag distribution, and open incidents. Sends HTML report to CEO and one-liner to Slack.
{
"name": "EdTech SaaS \u2014 Weekly KPI Dashboard",
"nodes": [
{
"id": "1",
"name": "Schedule \u2014 Monday 8AM",
"type": "n8n-nodes-base.scheduleTrigger",
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 8 * * 1"
}
]
}
},
"position": [
0,
0
]
},
{
"id": "2",
"name": "Postgres \u2014 KPI Query",
"type": "n8n-nodes-base.postgres",
"parameters": {
"operation": "executeQuery",
"query": "\nSELECT\n COUNT(DISTINCT account_id) FILTER (WHERE tier = 'ENTERPRISE_STUDENT_INFO_SYSTEM') AS enterprise_sis_accounts,\n COUNT(DISTINCT account_id) FILTER (WHERE tier = 'MIDMARKET_LMS_SAAS') AS lms_accounts,\n COUNT(DISTINCT account_id) FILTER (WHERE tier = 'EARLY_CHILDHOOD_EDTECH') AS coppa_covered_accounts,\n COUNT(DISTINCT account_id) FILTER (WHERE tier = 'SPECIAL_ED_COMPLIANCE_SAAS') AS idea_covered_accounts,\n COUNT(DISTINCT account_id) FILTER (WHERE status = 'trial') AS active_trials,\n COUNT(DISTINCT account_id) FILTER (WHERE status = 'active') AS paying_accounts,\n SUM(mrr) AS total_mrr,\n AVG(mrr) AS avg_mrr,\n COUNT(DISTINCT account_id) FILTER (WHERE flags @> ARRAY['FERPA_SCHOOL_OFFICIAL']) AS ferpa_covered_accounts,\n COUNT(DISTINCT account_id) FILTER (WHERE flags @> ARRAY['COPPA_COVERED_OPERATOR']) AS coppa_accounts,\n COUNT(DISTINCT account_id) FILTER (WHERE flags @> ARRAY['IDEA_COVERED_INSTITUTION']) AS idea_accounts,\n COUNT(DISTINCT account_id) FILTER (WHERE flags @> ARRAY['TITLE_IV_INSTITUTION']) AS title_iv_accounts,\n COUNT(*) FILTER (WHERE event_type = 'FERPA_UNAUTHORIZED_DISCLOSURE' AND created_at >= NOW() - INTERVAL '7 days') AS ferpa_incidents_7d,\n COUNT(*) FILTER (WHERE event_type = 'COPPA_FTC_ENFORCEMENT_NOTICE' AND created_at >= NOW() - INTERVAL '7 days') AS coppa_incidents_7d\nFROM accounts LEFT JOIN events USING (account_id)\n"
},
"position": [
250,
0
]
},
{
"id": "3",
"name": "Code \u2014 Format Report",
"type": "n8n-nodes-base.code",
"parameters": {
"jsCode": "\nconst d = $input.first().json;\nconst html = `<h2>FlowKit EdTech SaaS \u2014 Weekly KPI</h2>\n<table border='1' cellpadding='6'>\n<tr><th>Metric</th><th>Value</th></tr>\n<tr><td>Enterprise SIS Accounts</td><td>${d.enterprise_sis_accounts}</td></tr>\n<tr><td>LMS Accounts</td><td>${d.lms_accounts}</td></tr>\n<tr><td>COPPA-Covered Accounts</td><td>${d.coppa_covered_accounts}</td></tr>\n<tr><td>IDEA-Covered Accounts</td><td>${d.idea_covered_accounts}</td></tr>\n<tr><td>Active Trials</td><td>${d.active_trials}</td></tr>\n<tr><td>Paying Accounts</td><td>${d.paying_accounts}</td></tr>\n<tr><td>Total MRR</td><td>$${Number(d.total_mrr||0).toLocaleString()}</td></tr>\n<tr><td>Avg MRR</td><td>$${Number(d.avg_mrr||0).toFixed(0)}</td></tr>\n<tr><th colspan='2'>Compliance Coverage</th></tr>\n<tr><td>FERPA-Covered Accounts</td><td>${d.ferpa_covered_accounts}</td></tr>\n<tr><td>COPPA-Covered Accounts</td><td>${d.coppa_accounts}</td></tr>\n<tr><td>IDEA-Covered Accounts</td><td>${d.idea_accounts}</td></tr>\n<tr><td>Title IV Accounts</td><td>${d.title_iv_accounts}</td></tr>\n<tr><td>FERPA Incidents (7d)</td><td>${d.ferpa_incidents_7d}</td></tr>\n<tr><td>COPPA Incidents (7d)</td><td>${d.coppa_incidents_7d}</td></tr>\n</table>`;\nreturn [{json: {...d, html_report: html}}];\n"
},
"position": [
500,
0
]
},
{
"id": "4",
"name": "Gmail \u2014 KPI Report",
"type": "n8n-nodes-base.gmail",
"parameters": {
"operation": "send",
"toEmail": "ceo@yourcompany.com",
"subject": "EdTech SaaS Weekly KPI \u2014 {{ $now.format('YYYY-MM-DD') }}",
"emailType": "html",
"message": "={{ $json.html_report }}"
},
"position": [
750,
0
]
},
{
"id": "5",
"name": "Slack \u2014 One-liner",
"type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#go-to-market",
"text": "={{ 'Weekly KPI: ' + $json.paying_accounts + ' paying / ' + $json.active_trials + ' trials / MRR $' + Number($json.total_mrr||0).toLocaleString() + ' | FERPA: ' + $json.ferpa_covered_accounts + ' accts | COPPA: ' + $json.coppa_accounts + ' accts | Incidents 7d: FERPA=' + $json.ferpa_incidents_7d + ' COPPA=' + $json.coppa_incidents_7d }}"
},
"position": [
1000,
0
]
}
],
"connections": {
"Schedule \u2014 Monday 8AM": {
"main": [
[
{
"node": "Postgres \u2014 KPI Query",
"type": "main",
"index": 0
}
]
]
},
"Postgres \u2014 KPI Query": {
"main": [
[
{
"node": "Code \u2014 Format Report",
"type": "main",
"index": 0
}
]
]
},
"Code \u2014 Format Report": {
"main": [
[
{
"node": "Gmail \u2014 KPI Report",
"type": "main",
"index": 0
}
]
]
},
"Gmail \u2014 KPI Report": {
"main": [
[
{
"node": "Slack \u2014 One-liner",
"type": "main",
"index": 0
}
]
]
}
}
}
Why Self-Hosted n8n Wins the EdTech Procurement Conversation
| Factor | Cloud iPaaS (Zapier/Make) | Self-Hosted n8n |
|---|---|---|
| FERPA §99.31 school official exception | Fails — iPaaS is third-party outside direct control | Passes — automation stays inside institution boundary |
| COPPA operator definition | iPaaS becomes COPPA operator by data flow | Student data never reaches external operator |
| IDEA IEP record protection | IEP data transits cloud vendor | IEP workflows run on-prem |
| Title IV DOE subpoena exposure | Vendor holds subpoenable records | Records stay in institution perimeter |
| CA SOPIPA school operator | iPaaS = additional school operator requiring district agreement | Single operator boundary |
| School district procurement | Requires FERPA DPA with cloud vendor + security review | n8n runs on district-controlled infra |
| Cost at scale | $299+/mo growing with task volume | One Docker container, unlimited workflows |
The FERPA §99.31 direct control test is a binary gate for school district procurement.
Cloud iPaaS fails it by architecture. Self-hosted n8n passes it by architecture.
If you're building EdTech SaaS and your automation stack routes student records through cloud iPaaS,
you're one school district IT review away from losing the deal.
These workflows run on self-hosted n8n. Download the JSON, import, configure your Sheets IDs and API keys.
The complete FlowKit n8n Automation Template library (15 templates) is at stripeai.gumroad.com.
Top comments (0)