This workflow corresponds to n8n.io template #17048 — we link there as the canonical source.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"nodes": [
{
"id": "e744a32c-658e-403c-b4db-9f6197aa96e2",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2288,
-160
],
"parameters": {
"width": 480,
"height": 896,
"content": "## Check email DNS health across all Hostinger domains weekly\n\n### How it works\n\n1. A weekly schedule trigger fires every Monday at 8am and a config node sets the notification email address.\n2. All domains are fetched from Hostinger and split into individual items for per-domain processing.\n3. The DNS zone for each domain is retrieved and evaluated for the presence of MX, SPF, DMARC, and DKIM records, comparing against previous run data stored in workflow static data.\n4. If any email authentication issues are detected, an HTML alert email is built and sent to the configured recipient.\n\n### Setup steps\n\n- - [ ] Add your Hostinger API credentials to the **List Domains** and **Get DNS Zone** nodes.\n- - [ ] Configure the notification email address in the **Set - Config (Domain Health)** node (`notify_email` field).\n- - [ ] Add your SMTP or email provider credentials to the **Send Alert Email** node.\n- - [ ] Review the **Evaluate Email Auth Health** code node to ensure the MX/SPF/DMARC/DKIM logic matches your requirements.\n- - [ ] Activate the workflow so the Monday 8am schedule trigger begins firing.\n\n### Customization\n\nYou can adjust the schedule cadence (e.g. daily instead of weekly) in the trigger node. The health evaluation logic in **Evaluate Email Auth Health** can be extended to check additional DNS record types. The HTML template in **Build Alert Email** can be customised to match your branding."
},
"typeVersion": 1
},
{
"id": "392e2520-5be2-4bc2-9fda-af1e5b7768b1",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1728,
-112
],
"parameters": {
"color": 7,
"width": 432,
"height": 304,
"content": "## Schedule trigger and config\n\nFires every Monday at 8am and sets the notification email address used throughout the workflow."
},
"typeVersion": 1
},
{
"id": "e2b074a3-fa3b-48bb-bfa2-1969656cb33f",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1248,
-144
],
"parameters": {
"color": 7,
"width": 432,
"height": 320,
"content": "## Fetch and split domains\n\nRetrieves all domains from the Hostinger account and splits them into individual items for per-domain processing."
},
"typeVersion": 1
},
{
"id": "0ef157c2-ed5b-493e-bd6b-04a81d38768d",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-768,
-160
],
"parameters": {
"color": 7,
"width": 432,
"height": 336,
"content": "## Retrieve and evaluate DNS health\n\nFetches the DNS zone for each domain and evaluates the presence of MX, SPF, DMARC, and DKIM records against the previous run's state stored in workflow static data."
},
"typeVersion": 1
},
{
"id": "2fa2feec-1d1a-46b2-9ba7-9fef9bf76536",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-288,
-160
],
"parameters": {
"color": 7,
"width": 672,
"height": 336,
"content": "## Build and send alert email\n\nChecks whether any issues were detected, builds an HTML alert email table for affected domains, and sends the notification to the configured recipient."
},
"typeVersion": 1
},
{
"id": "c2d8c9ae-ee36-4be9-9eaf-c3709be4272a",
"name": "Every Monday at 8am",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-1680,
16
],
"parameters": {
"rule": {
"interval": [
{
"field": "weeks",
"triggerAtDay": [
1
],
"triggerAtHour": 8
}
]
}
},
"typeVersion": 1.2
},
{
"id": "761bafef-460f-4bfd-97ec-44b6bb4a5347",
"name": "Set Notification Email",
"type": "n8n-nodes-base.set",
"position": [
-1440,
16
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "h1",
"name": "notify_email",
"type": "string",
"value": "user@example.com"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "0492f842-3343-406c-a305-07dd170c7d60",
"name": "Fetch Hostinger Domains",
"type": "n8n-nodes-hostinger-api.hostingerApi",
"position": [
-1200,
16
],
"parameters": {
"resource": "domain"
},
"typeVersion": 1
},
{
"id": "13fdbd11-4fac-4eb3-9575-2fda0b6a9163",
"name": "Split Domain Records",
"type": "n8n-nodes-base.splitOut",
"position": [
-960,
16
],
"parameters": {
"options": {},
"fieldToSplitOut": "response.data"
},
"typeVersion": 1
},
{
"id": "88bfef40-d2f8-42de-9fab-0daae20721ee",
"name": "Fetch DNS Zone Data",
"type": "n8n-nodes-hostinger-api.hostingerApi",
"position": [
-720,
16
],
"parameters": {
"domain": "={{ $json.domain }}",
"resource": "dns",
"operation": "getDnsZone"
},
"typeVersion": 1
},
{
"id": "b90b7e1a-d255-4050-b54d-b98d022aab33",
"name": "Check MX SPF DMARC DKIM Health",
"type": "n8n-nodes-base.code",
"position": [
-480,
16
],
"parameters": {
"jsCode": "// Compare current MX/SPF/DMARC/DKIM presence against last run (workflow static data),\n// so we alert on NEW breakage, not just re-alert on a known problem every week.\n// This is genuinely stateful logic - no core node remembers prior executions, so Code earns its place here.\nconst staticData = $getWorkflowStaticData('global');\nif (!staticData.emailAuthState) staticData.emailAuthState = {};\n\nconst domain = $('Split Domain Records').item.json.domain;\n\nconst raw = $json;\nconst body = raw.response || raw;\nconst records = Array.isArray(body) ? body : (Array.isArray(body.data) ? body.data : []);\n\nconst isLive = (rec) => (rec.records || []).some(r => !r.is_disabled);\n\nconst mx = records.filter(r => r.type === 'MX' && isLive(r));\nconst spf = records.filter(r => r.type === 'TXT' && (r.name === '@' || r.name === domain)\n && (r.records || []).some(x => !x.is_disabled && (x.content || '').toLowerCase().includes('v=spf1')));\nconst dmarc = records.filter(r => r.type === 'TXT' && (r.name || '').toLowerCase() === '_dmarc'\n && (r.records || []).some(x => !x.is_disabled && (x.content || '').toLowerCase().includes('v=dmarc1')));\nconst dkim = records.filter(r => r.type === 'TXT' && (r.name || '').toLowerCase().includes('_domainkey') && isLive(r));\n\nconst current = { MX: mx.length > 0, SPF: spf.length > 0, DMARC: dmarc.length > 0, DKIM: dkim.length > 0 };\nconst previous = staticData.emailAuthState[domain] || {};\nconst problems = [];\n\nfor (const key of Object.keys(current)) {\n if (!current[key]) {\n const wasOkBefore = previous[key] !== false;\n problems.push({ record: key, isNew: wasOkBefore });\n }\n}\n\nstaticData.emailAuthState[domain] = current;\n\nreturn [{ json: { domain, hasIssues: problems.length > 0, problems } }];"
},
"typeVersion": 2
},
{
"id": "3fcc2230-9407-4497-9dad-16524fe4254f",
"name": "If Domain Has Issues",
"type": "n8n-nodes-base.if",
"position": [
-240,
16
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c2",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.hasIssues }}",
"rightValue": true
}
]
}
},
"typeVersion": 2
},
{
"id": "5f30c93e-4e1b-435a-a62a-8e7484b72736",
"name": "Build HTML Alert Email",
"type": "n8n-nodes-base.code",
"position": [
0,
0
],
"parameters": {
"jsCode": "// Templating a looped HTML table - this is what Code is genuinely good for; a Set expression\n// can't cleanly loop and build markup like this.\nconst domain = $json.domain;\nconst problems = $json.problems;\n\nconst descriptions = {\n MX: 'No active MX record - this domain cannot receive mail at all.',\n SPF: 'No valid SPF TXT record at the root - receiving servers may reject or spam-flag your mail.',\n DMARC: 'No valid DMARC TXT record at _dmarc - domain is exposed to spoofing and won\\'t get delivery reports.',\n DKIM: 'No DKIM (_domainkey) TXT record found - outgoing mail may fail signature checks.'\n};\n\nconst rows = problems.map(p => `\n<tr>\n <td>${p.record}</td>\n <td>${p.isNew ? '\\uD83C\\uDD95 New' : 'Ongoing'}</td>\n <td>${descriptions[p.record]}</td>\n</tr>`).join('');\n\nconst html = `\n<h2>Email authentication issue detected: ${domain}</h2>\n<p>The following DNS records affecting mail deliverability are missing or invalid:</p>\n<table border=\"1\" cellpadding=\"6\" cellspacing=\"0\" style=\"border-collapse:collapse\">\n<tr><th>Record</th><th>Status</th><th>Why it matters</th></tr>\n${rows}\n</table>\n<p>Add or fix these records in your DNS zone - this check will confirm the fix automatically next run.</p>\n`;\n\nreturn [{ json: { subject: `\\u26A0\\uFE0F Email authentication issue on ${domain}`, html } }];"
},
"typeVersion": 2
},
{
"id": "331566d0-d392-46e8-9c74-cc6b7acee57e",
"name": "Send Domain Alert Email",
"type": "n8n-nodes-base.emailSend",
"position": [
240,
0
],
"parameters": {
"html": "={{ $json.html }}",
"options": {},
"subject": "={{ $json.subject }}",
"toEmail": "={{ $('Set Notification Email').item.json.notify_email }}",
"fromEmail": "={{ $('Set Notification Email').item.json.notify_email }}"
},
"typeVersion": 2.1
}
],
"connections": {
"Every Monday at 8am": {
"main": [
[
{
"node": "Set Notification Email",
"type": "main",
"index": 0
}
]
]
},
"Fetch DNS Zone Data": {
"main": [
[
{
"node": "Check MX SPF DMARC DKIM Health",
"type": "main",
"index": 0
}
]
]
},
"If Domain Has Issues": {
"main": [
[
{
"node": "Build HTML Alert Email",
"type": "main",
"index": 0
}
]
]
},
"Split Domain Records": {
"main": [
[
{
"node": "Fetch DNS Zone Data",
"type": "main",
"index": 0
}
]
]
},
"Build HTML Alert Email": {
"main": [
[
{
"node": "Send Domain Alert Email",
"type": "main",
"index": 0
}
]
]
},
"Set Notification Email": {
"main": [
[
{
"node": "Fetch Hostinger Domains",
"type": "main",
"index": 0
}
]
]
},
"Fetch Hostinger Domains": {
"main": [
[
{
"node": "Split Domain Records",
"type": "main",
"index": 0
}
]
]
},
"Check MX SPF DMARC DKIM Health": {
"main": [
[
{
"node": "If Domain Has Issues",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow runs every Monday at 8am to check email-related DNS records (MX, SPF, DMARC, and DKIM) for all domains in your Hostinger account and sends an HTML alert email when it detects missing records, tracking prior results to highlight new breakage. Runs every Monday at…
Source: https://n8n.io/workflows/17048/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Amazon Product Price Tracker. Uses googleSheets, splitInBatches, httpRequest, emailSend. Scheduled trigger; 16 nodes.
Datetime Googlecalendar. Uses dateTime, noOp, googleCalendar, emailSend. Scheduled trigger; 13 nodes.
Generate Weekly Energy Consumption Reports with API, Email and Google Drive. Uses httpRequest, convertToFile, emailSend, googleDrive. Scheduled trigger; 12 nodes.
Weekly hiring‑manager snapshot from Breezy HR to email (pipeline, next‑week interviews, stuck). Uses httpRequest, emailSend. Scheduled trigger; 12 nodes.
This workflow runs every Monday morning and automatically diagnoses the health of your SQL Server database across 4 dimensions — slow queries, missing indexes, index fragmentation, and server wait sta