AutomationFlowsEmail & Gmail › Generate School Report Card Pdfs and Email Them to Parents with Gmail

Generate School Report Card Pdfs and Email Them to Parents with Gmail

ByJitesh Dugar @jiteshdugar on n8n.io

This n8n workflow automates the complete lifecycle of student report card generation — from fetching raw marks data to delivering polished PDF reports directly to parents via email.

Event trigger★★★★☆ complexity21 nodesData TableN8N Nodes HtmlcsstopdfGmail
Email & Gmail Trigger: Event Nodes: 21 Complexity: ★★★★☆ Added:

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

This workflow follows the Datatable → Gmail 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": "dT2rlVKbfgI3XzCg",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "School Report Card -> Email Parent Delivery",
  "tags": [],
  "nodes": [
    {
      "id": "214009eb-41d7-4ecc-b446-b3f421e4cf10",
      "name": "Run Workflow Manually",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -1312,
        576
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "7854bc56-4241-41b9-8249-5fab7391de4b",
      "name": "Loop Over Students",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -816,
        576
      ],
      "parameters": {
        "options": {}
      },
      "retryOnFail": false,
      "typeVersion": 3
    },
    {
      "id": "c86b339b-008e-490b-b745-3d521843122c",
      "name": "Skip Already Sent",
      "type": "n8n-nodes-base.if",
      "position": [
        -512,
        592
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "condition-skip-sent",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json['Status'] }}",
              "rightValue": "Sent"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "1150d65b-c682-4211-bda0-6f1bf92b0e02",
      "name": "Calculate Grade and Remarks",
      "type": "n8n-nodes-base.code",
      "position": [
        -256,
        480
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const item = $input.item.json;\n\n// \u2500\u2500\u2500 SUBJECT MARKS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst subjects = {\n  'Math':    parseInt(item['Math']    || 0),\n  'Science': parseInt(item['Science'] || 0),\n  'English': parseInt(item['English'] || 0),\n  'Hindi':   parseInt(item['Hindi']   || 0),\n  'SST':     parseInt(item['SST']     || 0)\n};\n\nconst maxPerSubject = parseInt(item['Max_Per_Subject'] || 100);\nconst totalSubjects = Object.keys(subjects).length;\nconst maxMarks      = maxPerSubject * totalSubjects;\nconst totalMarks    = Object.values(subjects).reduce((a, b) => a + b, 0);\nconst percentage    = ((totalMarks / maxMarks) * 100).toFixed(1);\n\n// \u2500\u2500\u2500 GRADE & COLOR \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nlet grade, gradeColor, gradeBg, remark;\n\nif (percentage >= 90) {\n  grade     = 'A+';\n  gradeColor = '#1b5e20';\n  gradeBg   = '#e8f5e9';\n  remark    = 'Outstanding performance! Truly exceptional dedication and hard work this term.';\n} else if (percentage >= 75) {\n  grade     = 'A';\n  gradeColor = '#1565c0';\n  gradeBg   = '#e3f2fd';\n  remark    = 'Excellent work. A consistent and hardworking student. Keep it up!';\n} else if (percentage >= 60) {\n  grade     = 'B';\n  gradeColor = '#e65100';\n  gradeBg   = '#fff3e0';\n  remark    = 'Good performance. Focused attention on weaker subjects will bring further improvement.';\n} else if (percentage >= 45) {\n  grade     = 'C';\n  gradeColor = '#6a1b9a';\n  gradeBg   = '#f3e5f5';\n  remark    = 'Satisfactory. Regular study habits and parental support will help improve results.';\n} else {\n  grade     = 'D';\n  gradeColor = '#b71c1c';\n  gradeBg   = '#ffebee';\n  remark    = 'Needs significant improvement. Please schedule a meeting with the class teacher at the earliest.';\n}\n\n// \u2500\u2500\u2500 SUBJECT RESULTS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst passMarkPct = 0.33; // 33% to pass each subject\nconst subjectResults = Object.entries(subjects).map(([name, marks]) => ({\n  name,\n  marks,\n  max: maxPerSubject,\n  pct: ((marks / maxPerSubject) * 100).toFixed(0),\n  pass: marks >= (maxPerSubject * passMarkPct),\n  passColor: marks >= (maxPerSubject * passMarkPct) ? '#2e7d32' : '#c62828',\n  passBg:    marks >= (maxPerSubject * passMarkPct) ? '#e8f5e9' : '#ffebee',\n  passLabel: marks >= (maxPerSubject * passMarkPct) ? 'Pass' : 'Fail'\n}));\n\n// \u2500\u2500\u2500 ATTENDANCE STYLING \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst attendance = parseFloat(item['Attendance'] || 0);\nconst attendanceColor = attendance >= 85 ? '#1b5e20' : attendance >= 75 ? '#e65100' : '#b71c1c';\n\nreturn {\n  ...item,\n  calc_subjects:        JSON.stringify(subjectResults),\n  calc_totalMarks:      totalMarks,\n  calc_maxMarks:        maxMarks,\n  calc_percentage:      parseFloat(percentage),\n  calc_grade:           grade,\n  calc_gradeColor:      gradeColor,\n  calc_gradeBg:         gradeBg,\n  calc_remark:          remark,\n  calc_attendance:      attendance,\n  calc_attendanceColor: attendanceColor,\n  calc_term:            item['Term'] || 'Term 2',\n  calc_year:            item['Academic_Year'] || '2024-25'\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "b7cd1bc0-d852-4175-9cbe-7f5ff610c051",
      "name": "Build HTML Report Card",
      "type": "n8n-nodes-base.code",
      "position": [
        0,
        480
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const d = $input.item.json;\nconst subjects = JSON.parse(d.calc_subjects);\n\n// \u2500\u2500\u2500 BUILD SUBJECT TABLE ROWS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst tableRows = subjects.map(s => `\n  <tr>\n    <td style=\"padding:10px 16px;border-bottom:1px solid #f0f0f0;font-weight:600;color:#333;font-size:13px\">${s.name}</td>\n    <td style=\"padding:10px 16px;border-bottom:1px solid #f0f0f0;text-align:center;font-size:13px;color:#555\">${s.marks} / ${s.max}</td>\n    <td style=\"padding:10px 16px;border-bottom:1px solid #f0f0f0;text-align:center;font-size:13px;font-weight:700;color:#333\">${s.pct}%</td>\n    <td style=\"padding:10px 16px;border-bottom:1px solid #f0f0f0;text-align:center\">\n      <span style=\"background:${s.passBg};color:${s.passColor};padding:4px 14px;border-radius:20px;font-size:11px;font-weight:800;letter-spacing:0.5px\">${s.passLabel}</span>\n    </td>\n  </tr>`).join('');\n\n// \u2500\u2500\u2500 FULL HTML REPORT CARD \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst html = `<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"UTF-8\">\n<style>\n  * { margin: 0; padding: 0; box-sizing: border-box; }\n  body { font-family: 'Segoe UI', 'Helvetica Neue', Arial, sans-serif; background: #fff; color: #333; -webkit-print-color-adjust: exact; }\n  .page { width: 210mm; min-height: 297mm; padding: 16mm 18mm; }\n\n  /* HEADER */\n  .header { text-align: center; padding-bottom: 16px; margin-bottom: 20px; border-bottom: 3px solid ${d.calc_gradeColor}; }\n  .school-emblem { width: 64px; height: 64px; border-radius: 50%; background: ${d.calc_gradeColor}; display: inline-flex; align-items: center; justify-content: center; font-size: 28px; margin-bottom: 10px; }\n  .school-name { font-size: 22px; font-weight: 900; color: ${d.calc_gradeColor}; letter-spacing: 0.5px; text-transform: uppercase; }\n  .school-sub { font-size: 12px; color: #888; margin-top: 3px; }\n  .report-badge { display: inline-block; margin-top: 12px; background: ${d.calc_gradeColor}; color: #fff; padding: 6px 24px; border-radius: 4px; font-size: 12px; font-weight: 800; letter-spacing: 2px; text-transform: uppercase; }\n  .report-term { font-size: 12px; color: #888; margin-top: 6px; }\n\n  /* STUDENT INFO */\n  .student-card { display: grid; grid-template-columns: 1fr 1fr; gap: 0; border: 1.5px solid ${d.calc_gradeColor}40; border-radius: 10px; overflow: hidden; margin: 18px 0; }\n  .info-cell { padding: 12px 16px; border-right: 1px solid #f0f0f0; border-bottom: 1px solid #f0f0f0; }\n  .info-cell:nth-child(2n) { border-right: none; }\n  .info-cell:nth-last-child(-n+2) { border-bottom: none; }\n  .info-lbl { font-size: 10px; color: #999; font-weight: 700; text-transform: uppercase; letter-spacing: 0.8px; }\n  .info-val { font-size: 14px; font-weight: 800; color: #222; margin-top: 3px; }\n\n  /* TABLE */\n  .section-lbl { font-size: 11px; font-weight: 800; color: ${d.calc_gradeColor}; text-transform: uppercase; letter-spacing: 1.5px; margin: 18px 0 10px; }\n  table { width: 100%; border-collapse: collapse; border-radius: 8px; overflow: hidden; border: 1px solid #e8e8e8; }\n  thead { background: ${d.calc_gradeColor}; }\n  th { padding: 11px 16px; text-align: left; font-size: 11px; font-weight: 800; color: #fff; letter-spacing: 0.8px; text-transform: uppercase; }\n  th:not(:first-child) { text-align: center; }\n  .total-row { background: ${d.calc_gradeBg}; }\n  .total-row td { font-weight: 800; font-size: 13px; padding: 12px 16px; }\n  .total-row td:not(:first-child) { text-align: center; }\n\n  /* GRADE SUMMARY */\n  .grade-strip { background: linear-gradient(135deg, ${d.calc_gradeColor}, ${d.calc_gradeColor}bb); color: #fff; border-radius: 10px; padding: 16px 20px; margin: 18px 0; display: flex; align-items: center; gap: 20px; }\n  .grade-letter { font-size: 56px; font-weight: 900; line-height: 1; opacity: 0.95; min-width: 60px; }\n  .grade-details .grade-pct { font-size: 26px; font-weight: 800; }\n  .grade-details .grade-sub { font-size: 12px; opacity: 0.85; margin-top: 2px; }\n\n  /* STATS ROW */\n  .stats-row { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; margin: 14px 0; }\n  .stat-card { background: #fafafa; border: 1px solid #eee; border-radius: 8px; padding: 12px 14px; text-align: center; }\n  .stat-val { font-size: 20px; font-weight: 900; color: ${d.calc_gradeColor}; }\n  .stat-lbl { font-size: 10px; color: #999; margin-top: 3px; text-transform: uppercase; font-weight: 700; letter-spacing: 0.5px; }\n\n  /* REMARK */\n  .remark-box { background: #fffde7; border-left: 4px solid #ffc107; padding: 12px 16px; border-radius: 0 8px 8px 0; margin: 14px 0; }\n  .remark-title { font-size: 10px; color: #888; font-weight: 800; text-transform: uppercase; letter-spacing: 0.8px; margin-bottom: 5px; }\n  .remark-text { font-size: 13px; color: #444; line-height: 1.65; font-style: italic; }\n\n  /* SIGNATURES */\n  .signatures { margin-top: 40px; display: grid; grid-template-columns: 1fr 1fr 1fr; text-align: center; }\n  .sig-block { border-top: 1px solid #ccc; padding-top: 8px; margin: 0 12px; font-size: 11px; color: #777; }\n\n  /* FOOTER */\n  .watermark { text-align: center; margin-top: 20px; font-size: 10px; color: #bbb; padding-top: 12px; border-top: 1px solid #f0f0f0; }\n</style>\n</head>\n<body>\n<div class=\"page\">\n\n  <!-- SCHOOL HEADER -->\n  <div class=\"header\">\n    <div class=\"school-emblem\">\ud83c\udf93</div>\n    <div class=\"school-name\">YOUR SCHOOL NAME</div>\n    <div class=\"school-sub\">Affiliated to CBSE, New Delhi &nbsp;|&nbsp; School Code: 00000</div>\n    <div class=\"school-sub\">123, School Road, Your City &nbsp;|&nbsp; Tel: +91 XXXXXXXXXX &nbsp;|&nbsp; user@example.com</div>\n    <div class=\"report-badge\">Student Progress Report</div>\n    <div class=\"report-term\">${d.calc_term} &nbsp;&bull;&nbsp; Academic Year ${d.calc_year}</div>\n  </div>\n\n  <!-- STUDENT INFO GRID -->\n  <div class=\"student-card\">\n    <div class=\"info-cell\">\n      <div class=\"info-lbl\">Student Name</div>\n      <div class=\"info-val\">${d['Student_Name'] || ''}</div>\n    </div>\n    <div class=\"info-cell\">\n      <div class=\"info-lbl\">Class &amp; Section</div>\n      <div class=\"info-val\">Class ${d['Class'] || ''} &mdash; Section ${d['Section'] || ''}</div>\n    </div>\n    <div class=\"info-cell\">\n      <div class=\"info-lbl\">Roll Number</div>\n      <div class=\"info-val\">${d['Roll_No'] || ''}</div>\n    </div>\n    <div class=\"info-cell\">\n      <div class=\"info-lbl\">Examination</div>\n      <div class=\"info-val\">${d.calc_term}, ${d.calc_year}</div>\n    </div>\n  </div>\n\n  <!-- MARKS TABLE -->\n  <div class=\"section-lbl\">\ud83d\udcca Subject-wise Performance</div>\n  <table>\n    <thead>\n      <tr>\n        <th>Subject</th>\n        <th>Marks Obtained</th>\n        <th>Percentage</th>\n        <th>Result</th>\n      </tr>\n    </thead>\n    <tbody>\n      ${tableRows}\n      <tr class=\"total-row\">\n        <td>Total / Grand Total</td>\n        <td style=\"text-align:center\">${d.calc_totalMarks} / ${d.calc_maxMarks}</td>\n        <td style=\"text-align:center\">${d.calc_percentage}%</td>\n        <td style=\"text-align:center\">\n          <span style=\"background:${d.calc_gradeBg};color:${d.calc_gradeColor};padding:4px 14px;border-radius:20px;font-size:11px;font-weight:800\">Grade ${d.calc_grade}</span>\n        </td>\n      </tr>\n    </tbody>\n  </table>\n\n  <!-- GRADE CARD -->\n  <div class=\"grade-strip\">\n    <div class=\"grade-letter\">${d.calc_grade}</div>\n    <div class=\"grade-details\">\n      <div class=\"grade-pct\">${d.calc_percentage}% &mdash; ${d.calc_totalMarks} / ${d.calc_maxMarks}</div>\n      <div class=\"grade-sub\">Overall Performance &bull; ${d.calc_term} ${d.calc_year}</div>\n    </div>\n  </div>\n\n  <!-- STATS -->\n  <div class=\"stats-row\">\n    <div class=\"stat-card\">\n      <div class=\"stat-val\" style=\"color:${d.calc_gradeColor}\">${d.calc_grade}</div>\n      <div class=\"stat-lbl\">Grade</div>\n    </div>\n    <div class=\"stat-card\">\n      <div class=\"stat-val\">${d.calc_percentage}%</div>\n      <div class=\"stat-lbl\">Overall %</div>\n    </div>\n    <div class=\"stat-card\">\n      <div class=\"stat-val\" style=\"color:${d.calc_attendanceColor}\">${d.calc_attendance}%</div>\n      <div class=\"stat-lbl\">Attendance</div>\n    </div>\n  </div>\n\n  <!-- REMARK -->\n  <div class=\"remark-box\">\n    <div class=\"remark-title\">\ud83d\udcdd Class Teacher's Remark</div>\n    <div class=\"remark-text\">${d.calc_remark}</div>\n  </div>\n\n  <!-- SIGNATURES -->\n  <div class=\"signatures\">\n    <div class=\"sig-block\">Class Teacher</div>\n    <div class=\"sig-block\">Exam Controller</div>\n    <div class=\"sig-block\">Principal</div>\n  </div>\n\n  <!-- WATERMARK -->\n  <div class=\"watermark\">\n    This is a system-generated report &bull; Generated on ${new Date().toLocaleDateString('en-IN', { day: 'numeric', month: 'long', year: 'numeric' })} &bull; For queries contact the school office\n  </div>\n\n</div>\n</body>\n</html>`;\n\n// Convert HTML string to binary so Gotenberg can receive it\nconst binaryData = await this.helpers.prepareBinaryData(\n  Buffer.from(html, 'utf8'),\n  'report_card.html',\n  'text/html'\n);\n\nreturn {\n  json: {\n    ...$input.item.json,\n    reportHtml: html // This sends the full HTML as a text property\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "e4bbd1c0-2b4b-4f6e-9c25-19b573956635",
      "name": "Get row(s)",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        -1072,
        576
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "Status",
              "condition": "isEmpty"
            }
          ]
        },
        "matchType": "allConditions",
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "zQlRqq5eqJ5cvvhT",
          "cachedResultUrl": "/projects/YORR4xM3FFToOHnO/datatables/zQlRqq5eqJ5cvvhT",
          "cachedResultName": "Jainam-Email-parent-Reportcard"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "7732d783-427b-4fb2-b791-9f1c0105e753",
      "name": "Convert HTML to PDF",
      "type": "n8n-nodes-htmlcsstopdf.htmlcsstopdf",
      "position": [
        256,
        480
      ],
      "parameters": {
        "html_content": "={{ $json.reportHtml }}",
        "dynamic_params": {
          "params": []
        }
      },
      "credentials": {
        "htmlcsstopdfApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7ee8ade0-1d98-4c82-8c35-7bb2b5131bc7",
      "name": "Update row(s)",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        992,
        480
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "Sent",
            "Sent_At": "={{ $now.toFormat('dd/MM/yyyy HH:mm:ss') }}",
            "Report_URL": "={{ $('Convert HTML to PDF').item.json.pdf_url }}"
          },
          "schema": [
            {
              "id": "Student_Name",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Student_Name",
              "defaultMatch": false
            },
            {
              "id": "Parent_Name",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Parent_Name",
              "defaultMatch": false
            },
            {
              "id": "Parent_WhatsApp",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Parent_WhatsApp",
              "defaultMatch": false
            },
            {
              "id": "Parent_Email",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Parent_Email",
              "defaultMatch": false
            },
            {
              "id": "Class",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Class",
              "defaultMatch": false
            },
            {
              "id": "Section",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Section",
              "defaultMatch": false
            },
            {
              "id": "Roll_No",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Roll_No",
              "defaultMatch": false
            },
            {
              "id": "Term",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Term",
              "defaultMatch": false
            },
            {
              "id": "Academic_Year",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Academic_Year",
              "defaultMatch": false
            },
            {
              "id": "Math",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Math",
              "defaultMatch": false
            },
            {
              "id": "Science",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Science",
              "defaultMatch": false
            },
            {
              "id": "English",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "English",
              "defaultMatch": false
            },
            {
              "id": "Hindi",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Hindi",
              "defaultMatch": false
            },
            {
              "id": "SST",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "SST",
              "defaultMatch": false
            },
            {
              "id": "Max_Per_Subject",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Max_Per_Subject",
              "defaultMatch": false
            },
            {
              "id": "Attendance",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Attendance",
              "defaultMatch": false
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false
            },
            {
              "id": "Report_URL",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Report_URL",
              "defaultMatch": false
            },
            {
              "id": "Sent_At",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Sent_At",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "filters": {
          "conditions": [
            {
              "keyName": "Roll_No",
              "keyValue": "={{ $('Loop Over Students').item.json.Roll_No }}"
            }
          ]
        },
        "options": {},
        "operation": "update",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "zQlRqq5eqJ5cvvhT",
          "cachedResultUrl": "/projects/YORR4xM3FFToOHnO/datatables/zQlRqq5eqJ5cvvhT",
          "cachedResultName": "Jainam-Email-parent-Reportcard"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "8f91fb96-bf7d-4f07-aa09-d92fb80e8d97",
      "name": "Send a message to Parents",
      "type": "n8n-nodes-base.gmail",
      "position": [
        496,
        480
      ],
      "parameters": {
        "sendTo": "={{ $('Get row(s)').item.json.Parent_Email }}",
        "message": "=Dear {{ $('Build HTML Report Card').item.json.Parent_Name }}, <br><br>\n  Please find attached the digital progress report for <b>{{ $('Build HTML Report Card').item.json.Student_Name }}</b> for <b>{{ $('Build HTML Report Card').item.json.Term }}</b>.<br><br>\n  <b>Quick Summary:</b><br>\n  - Grade: {{ $('Build HTML Report Card').item.json.calc_grade }}\n  - Score: {{ $('Build HTML Report Card').item.json.calc_percentage }}%<br><br>\n  You can view/download the report here: <a href={{ $json.pdf_url }}>View Report Card</a><br><br>\n  Regards,<br>\n  Principal<br>\n  School_Name",
        "options": {},
        "subject": "=Progress Report: {{ $('Build HTML Report Card').item.json.Student_Name }}, Class: {{ $('Get row(s)').item.json.Class }}{{ $('Get row(s)').item.json.Section }},{{ $('Get row(s)').item.json.Academic_Year }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "7a11d27e-bda9-4c5b-b3e3-2e9779bd5061",
      "name": "Wait 3 Seconds",
      "type": "n8n-nodes-base.wait",
      "position": [
        736,
        480
      ],
      "parameters": {
        "amount": 3
      },
      "typeVersion": 1.1
    },
    {
      "id": "50f67832-5d87-4007-be06-b6c97d0af6ce",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1392,
        384
      ],
      "parameters": {
        "color": "#786868",
        "height": 336,
        "content": "## \ud83c\udfac Manual Trigger\n\nStarts the workflow manually from the n8n editor.\n\n### \ud83d\udd39 Use Case:\n\u2022 Testing the workflow  \n\u2022 Debugging logic  \n\u2022 One-time execution  \n"
      },
      "typeVersion": 1
    },
    {
      "id": "27affd71-af3e-40bf-8733-44dd7d22316d",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1136,
        240
      ],
      "parameters": {
        "color": "#592744",
        "width": 256,
        "height": 480,
        "content": "## \ud83d\udce5 Fetch Student Records\n\nRetrieves student data from the Data Table.\n\n### \ud83d\udd39 What it does:\n\u2022 Fetches rows where **Status is empty**  \n\u2022 Filters only pending report cards  \n\n### \ud83c\udfaf Purpose:\n\u2022 Prevent duplicate processing  \n\u2022 Acts as the main data source  "
      },
      "typeVersion": 1
    },
    {
      "id": "ff5ffbe2-dd66-4407-8cf3-2fe39d752d63",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -864,
        240
      ],
      "parameters": {
        "color": "#293065",
        "height": 480,
        "content": "## \ud83d\udd01 Loop Through Students\n\nProcesses records one-by-one using batching.\n\n### \ud83d\udd39 What it does:\n\u2022 Splits data into individual items  \n\u2022 Runs workflow per student  \n\n### \ud83c\udfaf Purpose:\n\u2022 Avoid overload  \n\u2022 Improve reliability  \n\u2022 Enable controlled execution "
      },
      "typeVersion": 1
    },
    {
      "id": "d3a3b7fd-db54-41cd-a46f-139e75306b41",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -608,
        288
      ],
      "parameters": {
        "color": "#136267",
        "width": 256,
        "height": 432,
        "content": "## \ud83d\udeab Skip Already Sent Reports\n\nFilters out students whose reports are already sent.\n\n### \ud83d\udd39 Condition:\n\u2022 If Status \u2260 \"Sent\" \u2192 Continue  \n\u2022 Else \u2192 Skip  \n\n### \ud83c\udfaf Purpose:\n\u2022 Prevent duplicate emails  \n\u2022 Ensure idempotent workflow  \n"
      },
      "typeVersion": 1
    },
    {
      "id": "82c76e25-7254-471f-be0e-657888069296",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -320,
        112
      ],
      "parameters": {
        "color": "#21783B",
        "height": 512,
        "content": "## \ud83d\udcca Calculate Grades & Remarks\n\nCore logic for student evaluation.\n\n### \ud83d\udd39 What it calculates:\n\u2022 Subject-wise marks  \n\u2022 Total & percentage  \n\u2022 Grade (A+, A, B, C, D)  \n\u2022 Pass/Fail per subject  \n\u2022 Attendance status  \n\n### \ud83e\udde0 Extra:\n\u2022 Dynamic remarks based on performance  \n\u2022 Color coding for UI  "
      },
      "typeVersion": 1
    },
    {
      "id": "0b7b6a7b-2d49-408e-8721-f2ff01d7ff91",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -64,
        128
      ],
      "parameters": {
        "color": "#7E5207",
        "height": 496,
        "content": "## \ud83e\uddfe Generate HTML Report Card\n\nCreates a fully styled report card in HTML.\n\n### \ud83d\udd39 Includes:\n\u2022 School header  \n\u2022 Student details  \n\u2022 Subject table  \n\u2022 Grade summary  \n\u2022 Attendance & remarks  \n\n### \ud83c\udfaf Purpose:\n\u2022 Prepare printable design  \n\u2022 Maintain consistent layout  "
      },
      "typeVersion": 1
    },
    {
      "id": "858d4245-515e-4920-952e-05e90ed1ad9d",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        192,
        144
      ],
      "parameters": {
        "color": "#925D5D",
        "height": 480,
        "content": "## \ud83d\udcc4 Convert HTML to PDF\n\nTransforms HTML report into a PDF file.\n\n### \ud83d\udd39 What it does:\n\u2022 Sends HTML to PDF API  \n\u2022 Generates downloadable PDF link  \n\n### \ud83c\udfaf Purpose:\n\u2022 Shareable format  \n\u2022 Print-ready output  "
      },
      "typeVersion": 1
    },
    {
      "id": "bc86f0b2-36dd-4739-8440-d9453c104a1f",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        448,
        144
      ],
      "parameters": {
        "color": "#0A7894",
        "height": 480,
        "content": "## \ud83d\udce7 Send Email to Parents\n\nDelivers report card via email.\n\n### \ud83d\udd39 Includes:\n\u2022 Student name  \n\u2022 Grade & percentage  \n\u2022 PDF download link  \n\n### \ud83c\udfaf Purpose:\n\u2022 Notify parents instantly  \n\u2022 Digital delivery system  "
      },
      "typeVersion": 1
    },
    {
      "id": "47599e68-368f-4d01-b923-516bdc679fe5",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        704,
        208
      ],
      "parameters": {
        "color": "#20604D",
        "width": 208,
        "height": 416,
        "content": "## \u23f3 Delay Execution\n\nAdds a delay before next operation.\n\n### \ud83d\udd39 What it does:\n\u2022 Waits for 3 seconds  \n\n### \ud83c\udfaf Purpose:\n\u2022 Prevent API rate limits  \n\u2022 Avoid email throttling  "
      },
      "typeVersion": 1
    },
    {
      "id": "fd3b4e94-b16c-4fff-9fa5-ccaa55882c02",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        928,
        160
      ],
      "parameters": {
        "color": "#65485F",
        "height": 464,
        "content": "## \u2705 Update Student Record\n\nMarks report as sent in Data Table.\n\n### \ud83d\udd39 Updates:\n\u2022 Status \u2192 Sent  \n\u2022 Sent_At \u2192 Timestamp  \n\u2022 Report_URL \u2192 PDF link  \n\n### \ud83c\udfaf Purpose:\n\u2022 Track delivery  \n\u2022 Prevent reprocessing  "
      },
      "typeVersion": 1
    },
    {
      "id": "7da556a5-032b-46fa-b342-7939ac53a8c0",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1360,
        0
      ],
      "parameters": {
        "color": "#F7F7F7",
        "width": 720,
        "height": 192,
        "content": "## \ud83c\udf93 Report Card Automation\n\nThis workflow automatically generates and sends student report cards to parents.\n\nIt fetches student data, calculates performance (marks, percentage, grade, remarks), creates a professional PDF report card, emails it to parents, and updates the system to avoid duplicate sending.\n\n### \ud83d\udd04 Flow:\nFetch Data \u2192 Process Students \u2192 Generate Report \u2192 Convert to PDF \u2192 Send Email \u2192 Update Status"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "635f5484-b3e1-4e10-98e4-0af142a547ba",
  "connections": {
    "Get row(s)": {
      "main": [
        [
          {
            "node": "Loop Over Students",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update row(s)": {
      "main": [
        [
          {
            "node": "Loop Over Students",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 3 Seconds": {
      "main": [
        [
          {
            "node": "Update row(s)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Skip Already Sent": {
      "main": [
        [
          {
            "node": "Calculate Grade and Remarks",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Loop Over Students",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Students": {
      "main": [
        [],
        [
          {
            "node": "Skip Already Sent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert HTML to PDF": {
      "main": [
        [
          {
            "node": "Send a message to Parents",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run Workflow Manually": {
      "main": [
        [
          {
            "node": "Get row(s)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build HTML Report Card": {
      "main": [
        [
          {
            "node": "Convert HTML to PDF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send a message to Parents": {
      "main": [
        [
          {
            "node": "Wait 3 Seconds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Grade and Remarks": {
      "main": [
        [
          {
            "node": "Build HTML Report Card",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

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

About this workflow

This n8n workflow automates the complete lifecycle of student report card generation — from fetching raw marks data to delivering polished PDF reports directly to parents via email.

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

More Email & Gmail workflows → · Browse all categories →

Related workflows

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

Email & Gmail

Client Form → Draft → Approve → Sign → Deliver, fully automated

Jot Form Trigger, Gmail, Google Drive +4
Email & Gmail

This n8n template automates PDF translation into 1 or 2 target languages while maintaining professional formatting. Users submit PDFs via web form and receive translated documents via email with prese

Google Translate, HTTP Request, Form Trigger +2
Email & Gmail

Stage B. Uses executeWorkflowTrigger, postgres, dataTable, gmail. Event-driven trigger; 8 nodes.

Execute Workflow Trigger, Postgres, Data Table +1
Email & Gmail

This template automates the complete hiring pipeline for digital agencies managing applications across multiple job roles. When a candidate submits a Google Form with their CV, the system scores it wi

OpenRouter Chat, Output Parser Structured, Google Sheets +6
Email & Gmail

Automate WhatsApp communication for recruitment agencies with an interactive, structured customer experience. This workflow handles pricing inquiries, request submissions, tracking, complaints, and hu

HTTP Request, Google Sheets, Gmail +1