[
  {
    "id": "deliverCheck01",
    "name": "Email deliverability checker",
    "nodes": [
      {
        "parameters": {
          "httpMethod": "POST",
          "path": "deliverability-check",
          "responseMode": "responseNode",
          "options": {}
        },
        "id": "t1",
        "name": "Check a domain (webhook)",
        "type": "n8n-nodes-base.webhook",
        "typeVersion": 2,
        "position": [
          0,
          0
        ],
        "webhookId": "deliverability-hook"
      },
      {
        "parameters": {
          "jsCode": "// Build the DNS lookups needed to check a sending domain\nconst body = $input.first().json.body || {};\nconst domain = String(body.domain || '').trim().toLowerCase().replace(/^https?:\\/\\//, '').replace(/\\/.*$/, '');\nif (!/^[a-z0-9.-]+\\.[a-z]{2,}$/.test(domain)) throw new Error('Send a valid domain, for example {\"domain\":\"example.com\"}');\n\n// DKIM selectors are chosen by your email provider. These are common defaults; add your own via \"selectors\".\nconst selectors = Array.isArray(body.selectors) && body.selectors.length ? body.selectors : ['google', 'selector1', 'selector2', 'default', 'k1', 's1'];\n\nconst lookups = [\n  { kind: 'spf',   domain, name: domain,              type: 'TXT' },\n  { kind: 'dmarc', domain, name: `_dmarc.${domain}`,  type: 'TXT' },\n  { kind: 'mx',    domain, name: domain,              type: 'MX'  },\n  ...selectors.map(s => ({ kind: 'dkim', domain, selector: s, name: `${s}._domainkey.${domain}`, type: 'TXT' })),\n];\nreturn lookups.map(json => ({ json }));"
        },
        "id": "c1",
        "name": "Build lookups",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          260,
          0
        ]
      },
      {
        "parameters": {
          "url": "=https://dns.google/resolve?name={{ encodeURIComponent($json.name) }}&type={{ $json.type }}",
          "options": {
            "response": {
              "response": {
                "neverError": true
              }
            },
            "timeout": 15000
          }
        },
        "id": "h1",
        "name": "Look up DNS",
        "type": "n8n-nodes-base.httpRequest",
        "typeVersion": 4.2,
        "position": [
          520,
          0
        ]
      },
      {
        "parameters": {
          "jsCode": "// Read the DNS answers and grade SPF, DKIM, DMARC and MX\nconst queries = $('Build lookups').all().map(i => i.json);\nconst clean = t => String(t).replace(/\"\\s+\"/g, '').replace(/^\"|\"$/g, '');\nconst found = { spf: [], dmarc: [], mx: [], dkim: [] };\nlet domain = '';\n\n$input.all().forEach((item, i) => {\n  const q = queries[i];\n  domain = q.domain;\n  const answers = (item.json.Answer || []).map(a => clean(a.data));\n  if (q.kind === 'dkim') { if (answers.some(a => /p=[A-Za-z0-9+\\/=]{20,}/.test(a))) found.dkim.push(q.selector); }\n  else found[q.kind].push(...answers);\n});\n\nconst checks = [];\nconst add = (check, status, detail) => checks.push({ check, status, detail });\n\n// SPF: exactly one record, ending in -all or ~all\nconst spf = found.spf.filter(t => /^v=spf1/i.test(t));\nif (spf.length === 0) add('SPF', 'fail', 'No SPF record found');\nelse if (spf.length > 1) add('SPF', 'fail', `${spf.length} SPF records found; a domain must have exactly one`);\nelse {\n  const rule = (spf[0].match(/([+\\-~?]?)all\\b/i) || [])[1];\n  add('SPF', rule === '-' || rule === '~' ? 'pass' : 'warn', rule === '-' || rule === '~' ? `Found: ${spf[0]}` : `Record does not end in -all or ~all: ${spf[0]}`);\n  const lookupCount = (spf[0].match(/\\b(include:|a\\b|mx\\b|ptr\\b|exists:|redirect=)/gi) || []).length;\n  if (lookupCount > 10) add('SPF lookups', 'warn', `About ${lookupCount} lookup mechanisms; SPF allows at most 10`);\n}\n\n// DMARC: record present, policy stronger than none, reporting address set\nconst dmarc = found.dmarc.filter(t => /^v=DMARC1/i.test(t));\nif (!dmarc.length) add('DMARC', 'fail', `No DMARC record at _dmarc.${domain}`);\nelse {\n  const policy = (dmarc[0].match(/\\bp=(\\w+)/i) || [])[1] || '';\n  if (policy === 'quarantine' || policy === 'reject') add('DMARC', 'pass', `Policy p=${policy}`);\n  else add('DMARC', 'warn', policy === 'none' ? 'Policy is p=none (monitoring only)' : 'Policy not recognized');\n  if (!/\\brua=/i.test(dmarc[0])) add('DMARC reports', 'warn', 'No rua= address for aggregate reports');\n}\n\n// DKIM: at least one selector with a non-empty key (an empty p= means the key was revoked)\nif (found.dkim.length) add('DKIM', 'pass', `Key found for selector(s): ${found.dkim.join(', ')}`);\nelse add('DKIM', 'warn', 'No DKIM key found for the selectors tried. Check your email provider for the right selector');\n\n// MX: the domain must be able to receive replies\nif (found.mx.length) add('MX', 'pass', `${found.mx.length} mail server record(s)`);\nelse add('MX', 'fail', 'No MX records, so replies cannot reach this domain');\n\nconst fails = checks.filter(c => c.status === 'fail').length;\nconst warns = checks.filter(c => c.status === 'warn').length;\nconst score = Math.max(0, 100 - fails * 25 - warns * 10);\nreturn [{ json: { domain, score, checks } }];"
        },
        "id": "e1",
        "name": "Grade the results",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          780,
          0
        ]
      },
      {
        "parameters": {
          "respondWith": "json",
          "responseBody": "={{ $json }}",
          "options": {}
        },
        "id": "r1",
        "name": "Respond with results",
        "type": "n8n-nodes-base.respondToWebhook",
        "typeVersion": 1.4,
        "position": [
          1060,
          -110
        ]
      },
      {
        "parameters": {
          "fromEmail": "checker@360automate.test",
          "toEmail": "team@360automate.test",
          "subject": "={{ 'Email deliverability check: ' + $json.domain + ' (' + $json.score + '/100)' }}",
          "emailFormat": "text",
          "text": "={{ 'Deliverability score for ' + $json.domain + ': ' + $json.score + '/100' + '\\n\\n' + $json.checks.map(c => c.status.toUpperCase().padEnd(5) + ' ' + c.check + ': ' + c.detail).join('\\n') }}",
          "options": {
            "appendAttribution": false
          }
        },
        "id": "m1",
        "name": "Email the report",
        "type": "n8n-nodes-base.emailSend",
        "typeVersion": 2.1,
        "position": [
          1060,
          110
        ],
        "credentials": {
          "smtp": {
            "id": "mailpitSmtp01",
            "name": "Local Mailpit SMTP"
          }
        }
      }
    ],
    "connections": {
      "Check a domain (webhook)": {
        "main": [
          [
            {
              "node": "Build lookups",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Build lookups": {
        "main": [
          [
            {
              "node": "Look up DNS",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Look up DNS": {
        "main": [
          [
            {
              "node": "Grade the results",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Grade the results": {
        "main": [
          [
            {
              "node": "Respond with results",
              "type": "main",
              "index": 0
            },
            {
              "node": "Email the report",
              "type": "main",
              "index": 0
            }
          ]
        ]
      }
    },
    "settings": {
      "executionOrder": "v1"
    },
    "active": false
  }
]