AutomationFlowsAI & RAG › Grade Cold Outreach Emails and Generate an HTML Report with Openai Gpt-4.1

Grade Cold Outreach Emails and Generate an HTML Report with Openai Gpt-4.1

ByisaWOW @isawow on n8n.io

This workflow collects a cold email subject and body via an n8n Form, validates the input, and uses OpenAI to generate a structured A–F quality grade with copy and deliverability feedback, returning the results as a formatted HTML report. Receives a submission from an n8n Form…

Event trigger★★★★☆ complexityAI-powered14 nodesForm TriggerFormOpenAI ChatOutput Parser StructuredChain Llm
AI & RAG Trigger: Event Nodes: 14 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #17456 — we link there as the canonical source.

This workflow follows the Chainllm → Form Trigger recipe pattern — see all workflows that pair these two integrations.

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 →

Download .json
{
  "id": "GrLYTa5SoowLJQo0",
  "name": "Cold Email Outreach Quality Auditor",
  "tags": [],
  "nodes": [
    {
      "id": "954c7eb1-9370-4e45-8816-75349296d64a",
      "name": "Form Trigger",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        496,
        272
      ],
      "parameters": {
        "options": {
          "buttonLabel": "Grade My Email",
          "appendAttribution": false
        },
        "formTitle": "Cold Email Outreach Quality Auditor",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Email Subject Line",
              "requiredField": true
            },
            {
              "fieldType": "textarea",
              "fieldLabel": "Email Body",
              "requiredField": true
            },
            {
              "fieldLabel": "What are you selling or offering (optional, improves accuracy)"
            },
            {
              "fieldLabel": "Target audience or persona (optional, e.g. SaaS founders, HR managers)"
            }
          ]
        },
        "formDescription": "Paste your cold email subject line and body below. Get an instant A-F grade covering spam risk, personalization, subject line strength, value proposition, and call-to-action clarity, plus specific fixes and rewritten subject line options."
      },
      "typeVersion": 2.2
    },
    {
      "id": "3c868e97-5779-4c93-baa3-8498c19e2aa0",
      "name": "Map Form Fields",
      "type": "n8n-nodes-base.code",
      "position": [
        736,
        272
      ],
      "parameters": {
        "jsCode": "const item = $input.first().json;\n\nreturn [{\n  json: {\n    subject: (item['Email Subject Line'] || '').trim(),\n    body: (item['Email Body'] || '').trim(),\n    offer: item['What are you selling or offering (optional, improves accuracy)'] || 'Not specified',\n    audience: item['Target audience or persona (optional, e.g. SaaS founders, HR managers)'] || 'Not specified'\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "c18c48e9-7847-424b-86d2-57a176c8420a",
      "name": "Long Enough?",
      "type": "n8n-nodes-base.if",
      "position": [
        960,
        272
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond1",
              "operator": {
                "type": "number",
                "operation": "gte"
              },
              "leftValue": "={{ $json.body.length }}",
              "rightValue": 50
            },
            {
              "id": "cond2",
              "operator": {
                "type": "number",
                "operation": "gte"
              },
              "leftValue": "={{ $json.subject.length }}",
              "rightValue": 1
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "e3181d37-3c6c-4e70-b16a-90e109203458",
      "name": "Too Short - Error Response",
      "type": "n8n-nodes-base.form",
      "position": [
        1200,
        432
      ],
      "parameters": {
        "operation": "completion",
        "respondWith": "showText",
        "responseText": "<html><body style=\"font-family:-apple-system,Segoe UI,Roboto,sans-serif;padding:60px;text-align:center;color:#111827;\"><h2>Please add a subject line and a full email body</h2><p style=\"color:#6b7280;\">The email body needs at least 50 characters so the grader has enough to work with. Go back and try again.</p></body></html>"
      },
      "typeVersion": 1
    },
    {
      "id": "c894123f-6318-4ea6-bda6-d7af4a5ae7cd",
      "name": "Prepare Input",
      "type": "n8n-nodes-base.code",
      "position": [
        1200,
        176
      ],
      "parameters": {
        "jsCode": "const item = $input.first().json;\nlet body = item.body;\nlet truncated = false;\n\nif (body.length > 6000) {\n  body = body.slice(0, 6000);\n  truncated = true;\n}\n\nreturn [{\n  json: {\n    subject: item.subject,\n    body: body,\n    offer: item.offer,\n    audience: item.audience,\n    truncated\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "8fc59467-3258-4d35-83df-0d7c53363d49",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        1536,
        400
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {
          "temperature": 0.2
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "2e1b99fe-ed9f-4516-b4f6-284a18485491",
      "name": "Grade Schema",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        1696,
        400
      ],
      "parameters": {
        "jsonSchemaExample": "{\n  \"overall_score\": 62,\n  \"grade_letter\": \"C\",\n  \"spam_risk\": {\n    \"level\": \"Medium\",\n    \"trigger_words\": [\n      \"free\",\n      \"act now\",\n      \"guarantee\"\n    ],\n    \"note\": \"A few classic spam-trigger phrases could hurt inbox placement.\"\n  },\n  \"subject_line\": {\n    \"score\": 5,\n    \"feedback\": \"Too generic and slightly clickbait-y; doesn't hint at relevance to the recipient.\"\n  },\n  \"personalization\": {\n    \"score\": 3,\n    \"feedback\": \"No reference to the recipient's company, role, or a specific trigger event.\"\n  },\n  \"value_proposition\": {\n    \"score\": 7,\n    \"feedback\": \"Clear benefit stated but buried in the second paragraph.\"\n  },\n  \"cta\": {\n    \"score\": 6,\n    \"feedback\": \"Ask is clear but asks for too much time (30-minute call) on a first touch.\"\n  },\n  \"tone\": {\n    \"score\": 8,\n    \"feedback\": \"Professional and human, not overly salesy.\"\n  },\n  \"length_feedback\": \"At 180 words this is slightly long for a first-touch cold email; aim for under 120.\",\n  \"top_fixes\": [\n    \"Add one specific, researched detail about the recipient or their company in the first line\",\n    \"Move the core benefit to the first two sentences\",\n    \"Replace the 30-minute call ask with a lower-friction yes/no question\",\n    \"Remove 'guarantee' and 'act now' to reduce spam risk\"\n  ],\n  \"rewritten_subject_lines\": [\n    \"quick question about {{company}}'s onboarding flow\",\n    \"saw {{company}}'s recent launch - one idea\",\n    \"{{firstName}}, worth a 2-min look?\"\n  ]\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "8b2f6f40-0e3a-4e66-92ea-3dfc3a0d9146",
      "name": "Grade Email",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        1536,
        176
      ],
      "parameters": {
        "text": "=You are a cold email deliverability and copywriting expert who grades outbound sales emails the way an experienced SDR trainer would.\n\nWhat is being offered: {{ $json.offer }}\nTarget audience/persona: {{ $json.audience }}\n\nGrade the EMAIL below and respond with:\n1. overall_score: 0-100\n2. grade_letter: A, B, C, D, or F\n3. spam_risk: an object with level (Low, Medium, High), trigger_words (array of actual spammy words/phrases found in this email, empty array if none), and a one-sentence note\n4. subject_line: an object with score (0-10) and feedback on curiosity, clarity, and length\n5. personalization: an object with score (0-10) and feedback on whether this reads as genuinely researched or mass-blasted\n6. value_proposition: an object with score (0-10) and feedback on whether the benefit to the recipient is clear and early in the email\n7. cta: an object with score (0-10) and feedback on whether the call-to-action is low-friction and specific\n8. tone: an object with score (0-10) and feedback on whether it sounds human and professional rather than salesy or robotic\n9. length_feedback: one sentence on word count appropriateness for a first-touch cold email\n10. top_fixes: an array of 3-5 specific, actionable fixes ranked by impact\n11. rewritten_subject_lines: an array of exactly 3 alternative subject lines that would perform better\n\nRules:\n- Base every score strictly on the text given, do not assume details not present\n- Be specific and quote short fragments from the email when giving feedback\n- Keep every feedback string under 200 characters\n\nSUBJECT LINE:\n{{ $json.subject }}\n\nEMAIL BODY:\n{{ $json.body }}",
        "batching": {},
        "messages": {
          "messageValues": []
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.7
    },
    {
      "id": "e40f6462-23d2-4fc8-a938-07d7caf224e9",
      "name": "Build HTML Report",
      "type": "n8n-nodes-base.code",
      "position": [
        2032,
        176
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json.output;\n\nconst gradeColor = (g) => {\n  const map = { A: '#16a34a', B: '#65a30d', C: '#ca8a04', D: '#ea580c', F: '#dc2626' };\n  return map[g] || '#6b7280';\n};\n\nconst riskColor = (level) => {\n  const map = { Low: '#16a34a', Medium: '#ca8a04', High: '#dc2626' };\n  return map[level] || '#6b7280';\n};\n\nconst scoreBar = (label, score, feedback) => `\n  <div style=\"margin-bottom:16px;\">\n    <div style=\"display:flex;justify-content:space-between;margin-bottom:4px;\">\n      <span style=\"font-weight:600;color:#111827;font-size:14px;\">${label}</span>\n      <span style=\"font-weight:600;color:#111827;font-size:14px;\">${score}/10</span>\n    </div>\n    <div style=\"background:#e5e7eb;border-radius:999px;height:8px;overflow:hidden;margin-bottom:6px;\">\n      <div style=\"background:${score >= 7 ? '#16a34a' : score >= 4 ? '#ca8a04' : '#dc2626'};height:8px;width:${score*10}%;\"></div>\n    </div>\n    <p style=\"color:#6b7280;font-size:13px;margin:0;\">${feedback}</p>\n  </div>\n`;\n\nconst fixesHtml = (data.top_fixes || []).map((f, i) => `\n  <li style=\"margin-bottom:8px;color:#111827;font-size:14px;\"><strong>${i+1}.</strong> ${f}</li>\n`).join('');\n\nconst subjectsHtml = (data.rewritten_subject_lines || []).map(s => `\n  <div style=\"background:#f3f4f6;border-radius:6px;padding:10px 14px;margin-bottom:8px;font-size:14px;color:#111827;font-family:monospace;\">${s}</div>\n`).join('');\n\nconst triggerWordsHtml = (data.spam_risk.trigger_words || []).length\n  ? (data.spam_risk.trigger_words || []).map(w => `<span style=\"background:#fee2e2;color:#991b1b;padding:2px 8px;border-radius:999px;font-size:12px;margin-right:6px;display:inline-block;margin-bottom:6px;\">${w}</span>`).join('')\n  : '<span style=\"color:#16a34a;font-size:13px;\">No obvious spam-trigger words found</span>';\n\nconst html = `\n<!DOCTYPE html>\n<html>\n<head><meta charset=\"utf-8\"><title>Cold Email Grade Report</title></head>\n<body style=\"font-family:-apple-system,Segoe UI,Roboto,sans-serif;background:#f9fafb;margin:0;padding:32px;\">\n  <div style=\"max-width:680px;margin:0 auto;\">\n    <h1 style=\"color:#111827;font-size:22px;margin-bottom:4px;\">Cold Email Grade Report</h1>\n    <p style=\"color:#6b7280;font-size:13px;margin-top:0;\">AI-generated content grade. Real deliverability also depends on domain reputation, SPF/DKIM/DMARC setup, and sending volume.</p>\n\n    <div style=\"background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:20px;margin:20px 0;display:flex;align-items:center;gap:20px;\">\n      <div style=\"font-size:40px;font-weight:800;color:${gradeColor(data.grade_letter)};width:70px;text-align:center;\">${data.grade_letter}</div>\n      <div>\n        <div style=\"font-weight:600;color:#111827;\">Overall Score: ${data.overall_score}/100</div>\n        <div style=\"margin-top:6px;\"><span style=\"background:${riskColor(data.spam_risk.level)};color:#fff;font-size:12px;font-weight:600;padding:3px 10px;border-radius:999px;\">Spam Risk: ${data.spam_risk.level}</span></div>\n      </div>\n    </div>\n\n    <div style=\"background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:20px;margin:20px 0;\">\n      <div style=\"font-weight:600;color:#111827;margin-bottom:8px;\">Spam Trigger Words</div>\n      <div>${triggerWordsHtml}</div>\n      <p style=\"color:#6b7280;font-size:13px;margin-top:8px;\">${data.spam_risk.note}</p>\n    </div>\n\n    <div style=\"background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:20px;margin:20px 0;\">\n      <div style=\"font-weight:600;color:#111827;margin-bottom:12px;\">Score Breakdown</div>\n      ${scoreBar('Subject Line', data.subject_line.score, data.subject_line.feedback)}\n      ${scoreBar('Personalization', data.personalization.score, data.personalization.feedback)}\n      ${scoreBar('Value Proposition', data.value_proposition.score, data.value_proposition.feedback)}\n      ${scoreBar('Call to Action', data.cta.score, data.cta.feedback)}\n      ${scoreBar('Tone', data.tone.score, data.tone.feedback)}\n      <p style=\"color:#6b7280;font-size:13px;margin:0;\"><strong>Length:</strong> ${data.length_feedback}</p>\n    </div>\n\n    <div style=\"background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:20px;margin:20px 0;\">\n      <div style=\"font-weight:600;color:#111827;margin-bottom:10px;\">Top Fixes</div>\n      <ol style=\"padding-left:18px;margin:0;\">${fixesHtml}</ol>\n    </div>\n\n    <div style=\"background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:20px;margin:20px 0;\">\n      <div style=\"font-weight:600;color:#111827;margin-bottom:10px;\">Subject Line Alternatives</div>\n      ${subjectsHtml}\n    </div>\n\n    <p style=\"color:#9ca3af;font-size:12px;margin-top:30px;\">Generated by an AI cold email grader. Content quality only - not a technical deliverability test (DNS/SPF/DKIM not checked).</p>\n  </div>\n</body>\n</html>`;\n\nreturn [{ json: { html } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "95a24c9e-5c5f-4ef4-9099-f6c2c3cd995f",
      "name": "Send Report",
      "type": "n8n-nodes-base.form",
      "position": [
        2304,
        176
      ],
      "parameters": {
        "operation": "completion",
        "respondWith": "showText",
        "responseText": "={{ $json.html }}"
      },
      "typeVersion": 1
    },
    {
      "id": "3c79865a-8249-4bc7-923f-b27f56fbc303",
      "name": "Sticky Note - Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -112,
        -192
      ],
      "parameters": {
        "width": 492,
        "height": 1028,
        "content": "Cold outreach emails often get ignored or land in spam because the subject line is weak, the pitch is generic, or the ask is too big. This workflow gives SDRs, founders, and outreach agencies an instant quality check on any cold email before it goes out, so problems get caught before they cost you a reply rate.\n\n### How it works\n1. The **Form Trigger** collects the subject line, email body, offer, and target audience.\n2. **Map Form Fields** cleans up the raw form input into simple fields.\n3. **Long Enough?** checks the body has at least 50 characters before continuing.\n4. **Prepare Input** trims very long emails so the AI call stays within limits.\n5. **Grade Email** sends the text to an AI model, which returns scores for spam risk, subject line, personalization, value proposition, CTA, and tone.\n6. **Build HTML Report** turns those scores into a readable scorecard, which **Send Report** displays back to the user.\n\n### Setup steps\n1. Open node 7, OpenAI Chat Model, and connect your own OpenAI credential. You can swap it for any other chat model node if you prefer another provider.\n2. Activate the workflow.\n3. Open the Form Trigger production URL and submit a test email to confirm the report renders correctly.\n\n### Customization\nAdjust the 50 character minimum in the Long Enough? node if you want stricter or looser validation. Edit the prompt in Grade Email to change scoring criteria or tone of feedback."
      },
      "typeVersion": 1
    },
    {
      "id": "7c2db8f7-70dc-41a9-b0a4-2f58a8c5e47d",
      "name": "Sticky Note - Section 1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        432,
        64
      ],
      "parameters": {
        "color": 7,
        "width": 992,
        "height": 558,
        "content": "## 1. Trigger, Validate and Prepare\nCollects the subject and body from a form, checks the body is at least 50 characters, and trims very long text before sending it to the AI."
      },
      "typeVersion": 1
    },
    {
      "id": "d43a7479-83fa-48a3-8e2e-8c14af662179",
      "name": "Sticky Note - Section 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1472,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 421,
        "height": 830,
        "content": "## 2. AI Grading\nSends the email to an AI model using a structured schema, so the response always includes scores for spam risk, subject line, personalization, value, CTA, and tone."
      },
      "typeVersion": 1
    },
    {
      "id": "ea568b87-6d94-4249-951a-eb999dcc7270",
      "name": "Sticky Note - Section 3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1984,
        16
      ],
      "parameters": {
        "color": 7,
        "width": 508,
        "height": 398,
        "content": "## 3. Report and Delivery\nTurns the AI scores into a formatted HTML scorecard and sends it straight back to whoever submitted the form."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "86b7223a-a0e6-4b8b-b2c1-783bf11aca1f",
  "nodeGroups": [],
  "connections": {
    "Grade Email": {
      "main": [
        [
          {
            "node": "Build HTML Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Form Trigger": {
      "main": [
        [
          {
            "node": "Map Form Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Grade Schema": {
      "ai_outputParser": [
        [
          {
            "node": "Grade Email",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Long Enough?": {
      "main": [
        [
          {
            "node": "Prepare Input",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Too Short - Error Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Input": {
      "main": [
        [
          {
            "node": "Grade Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Map Form Fields": {
      "main": [
        [
          {
            "node": "Long Enough?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build HTML Report": {
      "main": [
        [
          {
            "node": "Send Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Grade Email",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow collects a cold email subject and body via an n8n Form, validates the input, and uses OpenAI to generate a structured A–F quality grade with copy and deliverability feedback, returning the results as a formatted HTML report. Receives a submission from an n8n Form…

Source: https://n8n.io/workflows/17456/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Host Your Own AI Deep Research Agent with n8n, Apify and OpenAI. Uses outputParserStructured, lmChatOpenAi, formTrigger, chainLlm. Event-driven trigger; 87 nodes.

Output Parser Structured, OpenAI Chat, Form Trigger +7
AI & RAG

This template attempts to replicate OpenAI's DeepResearch feature which, at time of writing, is only available to their pro subscribers.

Output Parser Structured, OpenAI Chat, Form Trigger +8
AI & RAG

My workflow 53. Uses formTrigger, httpRequest, lmChatOpenAi, form. Event-driven trigger; 74 nodes.

Form Trigger, HTTP Request, OpenAI Chat +15
AI & RAG

This workflow serves as a comprehensive "Workflow Nodes SEO & Documentation Generator". It uses AI to analyze, rename, and document n8n workflows, offering a streamlined way to optimize workflow reada

Form Trigger, n8n, Output Parser Autofixing +11
AI & RAG

This workflow streamlines how employees request equipment/items and how those requests reach the Procurement team. It validates the employee by enrollment number, detects whether a manager exists, and

Form Trigger, Form, OpenAI Chat +4