If your platform hosts user-generated video, audio, or social content, 2024–2025 is the year regulators caught up to the scale of the problem.
EU Digital Services Act Art.14 (illegal content): very large online platforms (VLOPs >45M EU users) had DSA obligations from August 2023. All platforms with EU users face DSA compliance by February 2024. Illegal content — CSAM, terrorist content, counterfeit goods — must be removed or disabled access expeditiously. In practice: 24 hours is the operational target for flagged illegal content.
DMCA §512 safe harbor requires expeditious removal after a valid takedown notice. The counter-notice window is 10–14 business days. Miss either window and you lose safe harbor protection — leaving you liable for statutory damages of $750–$150,000 per infringed work.
EU Copyright Directive Art.17 (formerly Art.13): platforms with >3 years operating + >10M EUR annual revenue must implement upload filters. Failure to prevent unauthorized uploads means the platform — not the user — is directly liable for infringement.
AVMSD Art.13 (Audiovisual Media Services Directive): VOD platforms must ensure at least 30% of their catalogue is European-origin content and must contribute financially to European production. National regulators (Ofcom, CSA, AGCOM) enforce this — sanctions vary by member state.
COPPA 16 CFR Part 312: any platform collecting PII from users under 13 must obtain verifiable parental consent first. FTC penalty: up to $51,744 per violation. CARU (Children's Advertising Review Unit) runs the industry self-regulatory program. Short-form video platforms with teen audiences face the highest exposure.
GDPR Art.22: automated profiling that produces legal or similarly significant effects requires opt-in consent or explicit legitimate basis. Recommendation engines and ad targeting systems that process behavioral data fall here.
Self-hosted n8n handles all five operational loops without routing user-generated content, PII, or rights-holder data through a third-party cloud iPaaS.
Who this is for
| Tier | Example | Primary pain |
|---|---|---|
| ENTERPRISE_STREAMING_SAAS | Netflix-scale VOD platforms | AVMSD 30% quota + DSA VLOP obligations |
| VIDEO_PLATFORM_SAAS | UGC video hosting platforms | DMCA §512 + Art.17 upload filter |
| PODCAST_PLATFORM_SAAS | Podcast hosting/distribution | DMCA §512 + FTC Endorsement Guides disclosure |
| LIVE_STREAMING_SAAS | Live streaming platforms | DSA Art.14 24h + COPPA teen audience |
| SHORT_FORM_VIDEO_SAAS | Short-form video apps | COPPA + DSA + GDPR Art.22 profiling |
| CREATOR_MONETIZATION_SAAS | Creator revenue/sponsorship platforms | FTC Endorsement Guides 16 CFR Part 255 |
| MEDIATECH_STARTUP | New media/content tech entrants | DMCA + COPPA baseline + DSA readiness |
Workflow 1 — DSA/DMCA Content Takedown & Notice Pipeline
Regulations: EU DSA Art.14 (24-hour illegal content removal) + DMCA §512 safe harbor (expeditious removal)
Why it matters: DSA Art.14 requires platforms to act on notices of illegal content. DMCA §512 requires expeditious removal after valid copyright notice and a 10-14 day counter-notice window. Both require documented receipt timestamps for compliance audit trails. Manual review queues miss deadlines under volume.
{
"name": "DSA/DMCA Content Takedown & Notice Pipeline",
"nodes": [
{
"id": "1",
"name": "Content Report Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
0,
0
],
"parameters": {
"path": "content-report",
"responseMode": "responseNode",
"httpMethod": "POST"
}
},
{
"id": "2",
"name": "Classify Report",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
200,
0
],
"parameters": {
"jsCode": "const b = $input.first().json.body || $input.first().json;\nconst type = b.report_type || 'unknown';\nconst contentId = b.content_id;\nconst reporterEmail = b.reporter_email;\nconst platformJurisdiction = b.jurisdiction || 'EU'; // EU triggers DSA\n\nlet deadline_hours = null;\nlet framework = null;\n\nif (platformJurisdiction === 'EU' && type === 'illegal_content') {\n framework = 'EU_DSA_ART14';\n deadline_hours = 24;\n} else if (type === 'copyright_dmca') {\n framework = 'DMCA_512';\n deadline_hours = 10 * 24; // 10 business days ~= conservative 10 days\n} else if (type === 'copyright_eu') {\n framework = 'EU_COPYRIGHT_DIR_ART17';\n deadline_hours = 24;\n} else {\n framework = 'INTERNAL_REVIEW';\n deadline_hours = 72;\n}\n\nconst receivedAt = new Date().toISOString();\nconst deadlineAt = new Date(Date.now() + deadline_hours*3600*1000).toISOString();\n\nreturn [{\n json: {\n content_id: contentId,\n reporter_email: reporterEmail,\n framework,\n deadline_hours,\n deadline_at: deadlineAt,\n received_at: receivedAt,\n jurisdiction: platformJurisdiction,\n report_type: type\n }\n}];"
}
},
{
"id": "3",
"name": "Slack Trust & Safety",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.1,
"position": [
400,
-100
],
"parameters": {
"authentication": "accessToken",
"resource": "message",
"operation": "post",
"channel": "#trust-safety",
"text": "\ud83d\udea8 CONTENT REPORT \u2014 {{ $json.framework }}\nContent ID: {{ $json.content_id }}\nFramework: {{ $json.framework }}\nDeadline: {{ $json.deadline_at }} ({{ $json.deadline_hours }}h)\nReceived: {{ $json.received_at }}\nAction required: Review + takedown decision before deadline"
}
},
{
"id": "4",
"name": "Gmail Reporter ACK",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
400,
100
],
"parameters": {
"operation": "send",
"sendTo": "={{ $json.reporter_email }}",
"subject": "Your report has been received \u2014 {{ $json.content_id }}",
"message": "<p>Thank you for your report. We have received your {{ $json.report_type }} notice regarding content ID {{ $json.content_id }}.</p><p>We will review your report and take action in accordance with {{ $json.framework === 'EU_DSA_ART14' ? 'EU DSA Article 14 (24-hour deadline)' : $json.framework === 'DMCA_512' ? 'DMCA \u00a7512 (expeditious removal)' : 'our content policy' }}.</p><p>Reference: {{ $json.content_id }}-{{ $json.received_at }}</p>",
"additionalFields": {
"isHtml": true
}
}
},
{
"id": "5",
"name": "Log to Postgres",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
600,
0
],
"parameters": {
"operation": "insert",
"schema": "public",
"table": "content_takedown_log",
"dataMode": "autoMapInputData"
}
}
],
"connections": {
"Content Report Webhook": {
"main": [
[
{
"node": "Classify Report",
"type": "main",
"index": 0
}
]
]
},
"Classify Report": {
"main": [
[
{
"node": "Slack Trust & Safety",
"type": "main",
"index": 0
},
{
"node": "Gmail Reporter ACK",
"type": "main",
"index": 0
}
]
]
},
"Slack Trust & Safety": {
"main": [
[
{
"node": "Log to Postgres",
"type": "main",
"index": 0
}
]
]
}
}
}
Import this: Copy the JSON above → n8n → Import from clipboard.
Workflow 2 — EU Copyright Directive Art.17 Upload Filter & Rights-Holder Notice
Regulation: EU Copyright Directive Art.17 (formerly Art.13) — upload filter obligation
Why it matters: Platforms with >3 years operating and >10M EUR annual revenue must implement upload filters and prevent unauthorized uploads. When a match is detected, the platform must hold the content and notify the rights holder. Liability shifts to the platform if the filter is absent.
{
"name": "EU Copyright Dir. Art.17 Upload Filter & Rights-Holder Notice",
"nodes": [
{
"id": "1",
"name": "Video Upload Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
0,
0
],
"parameters": {
"path": "video-uploaded",
"responseMode": "immediately",
"httpMethod": "POST"
}
},
{
"id": "2",
"name": "Check ContentID Database",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
200,
0
],
"parameters": {
"url": "={{ $vars.CONTENT_ID_API_URL }}/check",
"method": "POST",
"sendBody": true,
"contentType": "json",
"body": {
"video_id": "={{ $json.body.video_id }}",
"fingerprint": "={{ $json.body.audio_fingerprint }}"
}
}
},
{
"id": "3",
"name": "Copyright Match Check",
"type": "n8n-nodes-base.if",
"typeVersion": 2.1,
"position": [
400,
0
],
"parameters": {
"conditions": {
"options": {
"leftValue": "={{ $json.match_found }}",
"operation": "equal",
"rightValue": true
}
}
}
},
{
"id": "4",
"name": "Hold Content + Notify Rights Holder",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
600,
-100
],
"parameters": {
"jsCode": "const match = $input.first().json;\nconst videoId = $('Video Upload Webhook').first().json.body.video_id;\nconst uploaderId = $('Video Upload Webhook').first().json.body.uploader_id;\nconst rightsHolder = match.rights_holder_email;\nconst matchedWork = match.matched_work_title;\n\n// EU Copyright Directive Art.17: platform must prevent availability\n// unless license obtained OR uploader provides valid counter-notice\nconst holdUntil = new Date(Date.now() + 14*24*3600*1000).toISOString();\n\nreturn [{\n json: {\n video_id: videoId,\n uploader_id: uploaderId,\n rights_holder_email: rightsHolder,\n matched_work: matchedWork,\n hold_until: holdUntil,\n action: 'CONTENT_HELD_PENDING_REVIEW',\n regulation: 'EU Copyright Directive Art.17 \u2014 upload filter mandatory for platforms >3yr+>10M EUR revenue',\n counter_notice_deadline: holdUntil\n }\n}];"
}
},
{
"id": "5",
"name": "Slack Content Ops",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.1,
"position": [
800,
-200
],
"parameters": {
"authentication": "accessToken",
"resource": "message",
"operation": "post",
"channel": "#content-ops",
"text": "\u26a0\ufe0f Art.17 UPLOAD FILTER MATCH\nVideo: {{ $json.video_id }}\nMatched work: {{ $json.matched_work }}\nAction: Content held pending review\nHold until: {{ $json.hold_until }}\nRegulation: EU Copyright Directive Art.17"
}
},
{
"id": "6",
"name": "Gmail Rights Holder Notice",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
800,
0
],
"parameters": {
"operation": "send",
"sendTo": "={{ $json.rights_holder_email }}",
"subject": "Upload filter match notice \u2014 {{ $json.matched_work }}",
"message": "<p>Our automated upload filter has detected a potential match with your work: <strong>{{ $json.matched_work }}</strong>.</p><p>Video ID {{ $json.video_id }} has been held pending review as required under EU Copyright Directive Article 17.</p><p>The uploader has until {{ $json.hold_until }} to submit a counter-notice. You will be notified of the outcome.</p>",
"additionalFields": {
"isHtml": true
}
}
}
],
"connections": {
"Video Upload Webhook": {
"main": [
[
{
"node": "Check ContentID Database",
"type": "main",
"index": 0
}
]
]
},
"Check ContentID Database": {
"main": [
[
{
"node": "Copyright Match Check",
"type": "main",
"index": 0
}
]
]
},
"Copyright Match Check": {
"main": [
[
{
"node": "Hold Content + Notify Rights Holder",
"type": "main",
"index": 0
}
],
[]
]
},
"Hold Content + Notify Rights Holder": {
"main": [
[
{
"node": "Slack Content Ops",
"type": "main",
"index": 0
},
{
"node": "Gmail Rights Holder Notice",
"type": "main",
"index": 0
}
]
]
}
}
}
Workflow 3 — AVMSD 30% EU Content Quota & Commissioning Tracker
Regulation: EU AVMSD Art.13 — 30% European-origin content catalogue requirement
Why it matters: VOD platforms operating in EU member states must maintain at least 30% EU-origin content in their catalogue and contribute financially to European audiovisual production. National content regulators track this quarterly. Platforms that slip below 30% risk licensing conditions or financial levies.
{
"name": "AVMSD 30% EU Content Quota & Commissioning Tracker",
"nodes": [
{
"id": "1",
"name": "Weekly Monday 8AM",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
0,
0
],
"parameters": {
"rule": {
"interval": [
{
"field": "weeks",
"triggerAtDay": [
1
],
"triggerAtHour": 8
}
]
}
}
},
{
"id": "2",
"name": "Fetch Content Catalog",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.4,
"position": [
200,
0
],
"parameters": {
"operation": "read",
"documentId": "={{ $vars.CONTENT_CATALOG_SHEET_ID }}",
"sheetName": "Catalog",
"filtersUI": {}
}
},
{
"id": "3",
"name": "Calculate EU Quota",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
400,
0
],
"parameters": {
"jsCode": "const items = $input.all().map(i => i.json);\nconst total = items.length;\nconst euOrigin = items.filter(i => i.eu_origin === 'true' || i.eu_origin === true).length;\nconst euPct = total > 0 ? Math.round(euOrigin / total * 100) : 0;\n\n// AVMSD Art.13 + Recital 35: at least 30% of catalogue must be EU-origin\n// Applies to VOD services with >10M EUR annual revenue in EU\nconst AVMSD_THRESHOLD = 30;\nconst shortfall = Math.max(0, AVMSD_THRESHOLD - euPct);\nconst titlesNeeded = Math.max(0, Math.ceil(total * 0.30) - euOrigin);\n\nconst commissionQueue = items.filter(i => i.status === 'COMMISSION_PIPELINE' && i.eu_origin === 'true');\n\nreturn [{\n json: {\n total_titles: total,\n eu_origin_titles: euOrigin,\n eu_pct: euPct,\n avmsd_threshold_pct: AVMSD_THRESHOLD,\n compliant: euPct >= AVMSD_THRESHOLD,\n shortfall_pct: shortfall,\n titles_needed_to_comply: titlesNeeded,\n eu_titles_in_pipeline: commissionQueue.length,\n checked_at: new Date().toISOString()\n }\n}];"
}
},
{
"id": "4",
"name": "Quota Compliance Check",
"type": "n8n-nodes-base.if",
"typeVersion": 2.1,
"position": [
600,
0
],
"parameters": {
"conditions": {
"options": {
"leftValue": "={{ $json.compliant }}",
"operation": "equal",
"rightValue": false
}
}
}
},
{
"id": "5",
"name": "Slack Content Strategy Alert",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.1,
"position": [
800,
-100
],
"parameters": {
"authentication": "accessToken",
"resource": "message",
"operation": "post",
"channel": "#content-strategy",
"text": "\u26a0\ufe0f AVMSD EU CONTENT QUOTA SHORTFALL\nCurrent EU %: {{ $json.eu_pct }}% (minimum 30% required)\nShortfall: {{ $json.shortfall_pct }}%\nTitles needed to comply: {{ $json.titles_needed_to_comply }}\nEU titles in commission pipeline: {{ $json.eu_titles_in_pipeline }}\nRegulation: AVMSD Art.13 (Audiovisual Media Services Directive)\nRisk: NCA enforcement + potential service restriction in EU member states"
}
},
{
"id": "6",
"name": "Gmail CEO Report",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
800,
100
],
"parameters": {
"operation": "send",
"sendTo": "={{ $vars.CEO_EMAIL }}",
"subject": "Weekly AVMSD EU Content Quota Report \u2014 {{ $json.eu_pct }}% EU Origin",
"message": "<h3>AVMSD EU Content Quota \u2014 Weekly Report</h3><p>EU-origin titles: <strong>{{ $json.eu_origin_titles }}</strong> / {{ $json.total_titles }} total (<strong>{{ $json.eu_pct }}%</strong>)</p><p>AVMSD Art.13 minimum: <strong>30%</strong></p><p>Status: <strong>{{ $json.compliant ? '\u2705 COMPLIANT' : '\u26a0\ufe0f SHORTFALL \u2014 ' + $json.shortfall_pct + '% below threshold' }}</strong></p><p>Titles needed to reach 30%: {{ $json.titles_needed_to_comply }}</p><p>EU titles in commission pipeline: {{ $json.eu_titles_in_pipeline }}</p>",
"additionalFields": {
"isHtml": true
}
}
}
],
"connections": {
"Weekly Monday 8AM": {
"main": [
[
{
"node": "Fetch Content Catalog",
"type": "main",
"index": 0
}
]
]
},
"Fetch Content Catalog": {
"main": [
[
{
"node": "Calculate EU Quota",
"type": "main",
"index": 0
}
]
]
},
"Calculate EU Quota": {
"main": [
[
{
"node": "Quota Compliance Check",
"type": "main",
"index": 0
}
]
]
},
"Quota Compliance Check": {
"main": [
[
{
"node": "Slack Content Strategy Alert",
"type": "main",
"index": 0
}
],
[
{
"node": "Gmail CEO Report",
"type": "main",
"index": 0
}
]
]
}
}
}
Workflow 4 — COPPA Under-13 Detection & Parental Consent Pipeline
Regulations: COPPA 16 CFR Part 312 + CARU Self-Regulatory Program
Why it matters: Platforms directed to children or with actual knowledge of under-13 users must obtain verifiable parental consent before collecting PII. The FTC has levied penalties exceeding $150M against large platforms. Short-form video platforms are highest-risk. Manual review of age signals at scale is impossible — automated flagging is the only viable approach.
{
"name": "COPPA Under-13 Detection & Parental Consent Pipeline",
"nodes": [
{
"id": "1",
"name": "New Signup Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
0,
0
],
"parameters": {
"path": "new-signup",
"responseMode": "immediately",
"httpMethod": "POST"
}
},
{
"id": "2",
"name": "Age Verification Check",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
200,
0
],
"parameters": {
"jsCode": "const b = $input.first().json.body || $input.first().json;\nconst dob = b.date_of_birth; // YYYY-MM-DD\nconst userId = b.user_id;\nconst parentEmail = b.parent_email || null;\nconst selfReportedAge = parseInt(b.age || '99');\n\nlet ageRiskFlag = 'ADULT';\nlet ageYears = null;\n\nif (dob) {\n const birthDate = new Date(dob);\n const today = new Date();\n ageYears = (today - birthDate) / (365.25 * 24 * 3600 * 1000);\n if (ageYears < 13) ageRiskFlag = 'UNDER_13_CONFIRMED';\n else if (ageYears < 16) ageRiskFlag = 'TEEN_13_15';\n} else if (selfReportedAge < 13) {\n ageRiskFlag = 'UNDER_13_SELF_REPORTED';\n} else if (selfReportedAge < 16) {\n ageRiskFlag = 'TEEN_SIGNAL';\n}\n\n// COPPA 16 CFR Part 312: operators must obtain verifiable parental consent\n// before collecting PII from children under 13\n// CARU (Children's Advertising Review Unit) self-regulatory supplement\n\nreturn [{\n json: {\n user_id: userId,\n age_risk_flag: ageRiskFlag,\n age_years: ageYears ? Math.floor(ageYears) : null,\n parent_email: parentEmail,\n requires_parental_consent: ageRiskFlag.includes('UNDER_13'),\n regulation: 'COPPA 16 CFR Part 312 + CARU Self-Regulatory Program',\n action_required: ageRiskFlag.includes('UNDER_13') ? 'PAUSE_ACCOUNT_PENDING_PARENTAL_CONSENT' : 'STANDARD_ONBOARDING',\n flagged_at: new Date().toISOString()\n }\n}];"
}
},
{
"id": "3",
"name": "Under-13 Flag Check",
"type": "n8n-nodes-base.if",
"typeVersion": 2.1,
"position": [
400,
0
],
"parameters": {
"conditions": {
"options": {
"leftValue": "={{ $json.requires_parental_consent }}",
"operation": "equal",
"rightValue": true
}
}
}
},
{
"id": "4",
"name": "Pause Account + Notify Ops",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.1,
"position": [
600,
-100
],
"parameters": {
"authentication": "accessToken",
"resource": "message",
"operation": "post",
"channel": "#trust-safety-coppa",
"text": "\ud83d\udea8 COPPA FLAG \u2014 UNDER-13 SIGNUP\nUser ID: {{ $json.user_id }}\nAge flag: {{ $json.age_risk_flag }}\nAction: Account paused pending verifiable parental consent\nRegulation: COPPA 16 CFR Part 312\nFTC penalty: up to $51,744/violation\nParent email available: {{ $json.parent_email ? 'YES' : 'NO \u2014 manual review required' }}"
}
},
{
"id": "5",
"name": "Gmail Parental Consent Request",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
600,
100
],
"parameters": {
"operation": "send",
"sendTo": "={{ $json.parent_email || $vars.COPPA_REVIEW_EMAIL }}",
"subject": "Parental consent required for your child's account",
"message": "<p>We detected that a user on our platform may be under 13 years old. Under the Children's Online Privacy Protection Act (COPPA), we are required to obtain verifiable parental consent before collecting any personal information from children under 13.</p><p>To activate this account, a parent or guardian must complete our consent form: <a href='{{ $vars.COPPA_CONSENT_URL }}?uid={{ $json.user_id }}'>Click here to provide consent</a></p><p>If you did not create this account, please ignore this email and the account will be automatically deleted after 30 days.</p>",
"additionalFields": {
"isHtml": true
}
}
}
],
"connections": {
"New Signup Webhook": {
"main": [
[
{
"node": "Age Verification Check",
"type": "main",
"index": 0
}
]
]
},
"Age Verification Check": {
"main": [
[
{
"node": "Under-13 Flag Check",
"type": "main",
"index": 0
}
]
]
},
"Under-13 Flag Check": {
"main": [
[
{
"node": "Pause Account + Notify Ops",
"type": "main",
"index": 0
},
{
"node": "Gmail Parental Consent Request",
"type": "main",
"index": 0
}
],
[]
]
}
}
}
Workflow 5 — Weekly MediaTech Platform KPI Dashboard
Why it matters: DAU/MAU, watch hours, creator activity, revenue, and compliance event counts all in one weekly email. The BCC to Legal closes the governance gap — your legal team sees the same compliance numbers the CEO sees, every Monday.
{
"name": "Weekly MediaTech Platform KPI Dashboard",
"nodes": [
{
"id": "1",
"name": "Monday 8AM",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
0,
0
],
"parameters": {
"rule": {
"interval": [
{
"field": "weeks",
"triggerAtDay": [
1
],
"triggerAtHour": 8
}
]
}
}
},
{
"id": "2",
"name": "Fetch Platform Metrics",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
200,
0
],
"parameters": {
"operation": "executeQuery",
"query": "SELECT SUM(CASE WHEN event_date >= NOW() - INTERVAL '7 days' THEN 1 ELSE 0 END) AS dau_7d, COUNT(DISTINCT user_id) AS mau_30d, SUM(content_hours_watched) AS total_watch_hours_7d, COUNT(DISTINCT creator_id) AS active_creators_7d, SUM(revenue_usd) AS revenue_7d, SUM(CASE WHEN event_date >= NOW() - INTERVAL '14 days' AND event_date < NOW() - INTERVAL '7 days' THEN revenue_usd ELSE 0 END) AS revenue_prev_7d FROM platform_metrics_weekly WHERE event_date >= NOW() - INTERVAL '30 days'"
}
},
{
"id": "3",
"name": "Fetch Compliance Events",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
200,
200
],
"parameters": {
"operation": "executeQuery",
"query": "SELECT COUNT(CASE WHEN framework = 'EU_DSA_ART14' THEN 1 END) AS dsa_notices_7d, COUNT(CASE WHEN framework = 'DMCA_512' THEN 1 END) AS dmca_notices_7d, COUNT(CASE WHEN framework = 'EU_COPYRIGHT_DIR_ART17' THEN 1 END) AS art17_matches_7d, COUNT(CASE WHEN age_risk_flag LIKE '%UNDER_13%' THEN 1 END) AS coppa_flags_7d, MAX(eu_content_pct) AS current_eu_content_pct FROM compliance_events_log WHERE event_date >= NOW() - INTERVAL '7 days'"
}
},
{
"id": "4",
"name": "Merge Metrics",
"type": "n8n-nodes-base.merge",
"typeVersion": 3,
"position": [
400,
100
],
"parameters": {
"mode": "combine",
"combinationMode": "mergeByPosition"
}
},
{
"id": "5",
"name": "Build KPI Report",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
600,
100
],
"parameters": {
"jsCode": "const m = $input.first().json;\nconst prev = parseFloat(m.revenue_prev_7d || 0);\nconst curr = parseFloat(m.revenue_7d || 0);\nconst wowPct = prev > 0 ? Math.round((curr - prev) / prev * 100) : null;\nconst wowStr = wowPct !== null ? (wowPct >= 0 ? '+' : '') + wowPct + '%' : 'N/A';\n\nconst euPct = parseFloat(m.current_eu_content_pct || 0);\nconst avmsdFlag = euPct < 30 ? '\u26a0\ufe0f BELOW 30% AVMSD THRESHOLD' : '\u2705 AVMSD COMPLIANT';\n\nconst html = `<h2>MediaTech Platform \u2014 Weekly KPI Report</h2>\n<table border='1' cellpadding='6' style='border-collapse:collapse'>\n<tr><th>Metric</th><th>This Week</th><th>WoW</th></tr>\n<tr><td>DAU (7d)</td><td>${m.dau_7d}</td><td>\u2014</td></tr>\n<tr><td>MAU (30d)</td><td>${m.mau_30d}</td><td>\u2014</td></tr>\n<tr><td>Watch Hours (7d)</td><td>${Math.round(m.total_watch_hours_7d||0).toLocaleString()}</td><td>\u2014</td></tr>\n<tr><td>Active Creators</td><td>${m.active_creators_7d}</td><td>\u2014</td></tr>\n<tr><td>Revenue (7d)</td><td>$${parseFloat(curr).toFixed(0)}</td><td>${wowStr}</td></tr>\n<tr><td>DSA Art.14 Notices</td><td>${m.dsa_notices_7d||0}</td><td>\u2014</td></tr>\n<tr><td>DMCA \u00a7512 Notices</td><td>${m.dmca_notices_7d||0}</td><td>\u2014</td></tr>\n<tr><td>Art.17 Upload Matches</td><td>${m.art17_matches_7d||0}</td><td>\u2014</td></tr>\n<tr><td>COPPA Flags</td><td>${m.coppa_flags_7d||0}</td><td>\u2014</td></tr>\n<tr><td>EU Content % (AVMSD)</td><td>${euPct}%</td><td>${avmsdFlag}</td></tr>\n</table>`;\n\nreturn [{ json: { html, wowPct, avmsdFlag, eu_pct: euPct } }];"
}
},
{
"id": "6",
"name": "Gmail CEO + BCC Legal",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
800,
0
],
"parameters": {
"operation": "send",
"sendTo": "={{ $vars.CEO_EMAIL }}",
"subject": "Weekly MediaTech Platform KPI \u2014 {{ new Date().toISOString().split('T')[0] }}",
"message": "={{ $json.html }}",
"additionalFields": {
"isHtml": true,
"bccList": "={{ $vars.LEGAL_EMAIL }}"
}
}
}
],
"connections": {
"Monday 8AM": {
"main": [
[
{
"node": "Fetch Platform Metrics",
"type": "main",
"index": 0
},
{
"node": "Fetch Compliance Events",
"type": "main",
"index": 0
}
]
]
},
"Fetch Platform Metrics": {
"main": [
[
{
"node": "Merge Metrics",
"type": "main",
"index": 0
}
]
]
},
"Fetch Compliance Events": {
"main": [
[
{
"node": "Merge Metrics",
"type": "main",
"index": 1
}
]
]
},
"Merge Metrics": {
"main": [
[
{
"node": "Build KPI Report",
"type": "main",
"index": 0
}
]
]
},
"Build KPI Report": {
"main": [
[
{
"node": "Gmail CEO + BCC Legal",
"type": "main",
"index": 0
}
]
]
}
}
}
Why self-hosted n8n, not Zapier or Make?
| Concern | Zapier/Make | Self-hosted n8n |
|---|---|---|
| User-generated content PII | Routes through US cloud | Stays in your VPC |
| DMCA takedown audit trail | Vendor controls logs | Your Postgres, your retention |
| Art.17 rights-holder data | Third-party processor | You control processor chain |
| COPPA child PII | Data egress risk | Zero egress, on-prem |
| DSA Art.14 24h SLA | Zapier outage = missed deadline | On-prem uptime = your SLA |
| GDPR Art.28 DPA | Vendor DPA + sub-processors | Single DPA, no sub-processor chain |
Get the complete workflow bundle
These five workflows are part of the FlowKit n8n Automation Template Bundle — 14 production-ready workflows for $97.
Individual templates start at $12. The bundle includes MediaTech compliance, SaaS ops, email automation, CRM, reporting, and more.
Workflow JSON is import-ready. Replace $vars.PAYMENT_PAGE_URL, $vars.CONTENT_ID_API_URL, and other env variables with your actual values. All workflows self-host on n8n Community Edition.
Top comments (0)