AutomationFlowsWeb Scraping › Hackathon Participant Badge Generator with Qr Code, PDF & Email Delivery

Hackathon Participant Badge Generator with Qr Code, PDF & Email Delivery

ByJitesh Dugar @jiteshdugar on n8n.io

Hackathon Participant Badge Generator with QR Code & Email Delivery

Webhook trigger★★★★☆ complexity15 nodesHTTP RequestGmailN8N Nodes VerifiemailN8N Nodes HtmlcsstopdfGoogle Sheets
Web Scraping Trigger: Webhook Nodes: 15 Complexity: ★★★★☆ Added:

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

This workflow follows the Gmail → Google Sheets 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": "",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "name": "Hackathon Participant Badge + QR Check-in (HTML \u2192 PDF \u2192 Gmail)",
  "tags": [],
  "nodes": [
    {
      "id": "97a3afd6-32bf-412d-9e20-91f5fee5a553",
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -448,
        304
      ],
      "parameters": {
        "path": "hackathon-badge",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 1
    },
    {
      "id": "072fe3df-815f-4eda-ad16-1624c76ff355",
      "name": "Validate Input",
      "type": "n8n-nodes-base.function",
      "position": [
        -272,
        304
      ],
      "parameters": {
        "functionCode": "// Extract only the actual payload from the webhook\nconst payload = $input.first().json.body;\n\n// Required fields\nconst required = [\"name\", \"email\", \"event\"];\nconst errors = [];\n\n// Check required fields\nrequired.forEach(field => {\n  if (!payload[field] || String(payload[field]).trim() === \"\") {\n    errors.push(`${field} is required`);\n  }\n});\n\n// Very basic email format check\nif (!payload.email || !payload.email.includes(\"@\") || !payload.email.includes(\".\")) {\n  errors.push(\"Invalid email format\");\n}\n\nif (errors.length > 0) {\n  throw new Error(\"VALIDATION_ERROR: \" + errors.join(\" | \"));\n}\n\n// Pass only the clean data forward\nreturn [{ json: payload }];"
      },
      "typeVersion": 1
    },
    {
      "id": "d95fcf37-9a52-4446-ad80-9c4a1a2f7111",
      "name": "Email Valid?",
      "type": "n8n-nodes-base.if",
      "position": [
        64,
        304
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.valid }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "74a8b677-e908-489b-939e-24c0b3a3d55f",
      "name": "Generate Badge ID & URL",
      "type": "n8n-nodes-base.function",
      "position": [
        432,
        288
      ],
      "parameters": {
        "functionCode": "const now = new Date();\nconst badgeId = `HACK-${now.getFullYear()}-${Math.floor(Date.now()/1000)}-${Math.random().toString(36).substr(2,6).toUpperCase()}`;\nconst verifyUrl = `https://yourdomain.com/verify-badge?id=${badgeId}`;\nconst issuedAt = now.toLocaleDateString('en-US', {\n  month: 'long',\n  day: 'numeric',\n  year: 'numeric'\n});\n\n// Direct QR code URL (no external node needed!)\nconst qrCodeUrl = `https://api.qrserver.com/v1/create-qr-code/?size=300x300&format=png&data=${encodeURIComponent(verifyUrl)}`;\n\nreturn [{\n  json: {\n    ...$json,\n    badgeId,\n    verifyUrl,\n    issuedAt,\n    qrCodeUrl\n  }\n}];"
      },
      "typeVersion": 1
    },
    {
      "id": "c0b3e254-c96e-41ff-ba17-009ef42095eb",
      "name": "Download PDF",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        928,
        288
      ],
      "parameters": {
        "url": "={{ $json.pdf_url }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "01483244-ac10-409b-89b7-a4fe552c939e",
      "name": "Send Badge via Gmail",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1120,
        288
      ],
      "parameters": {
        "sendTo": "={{ $('Validate Input').item.json.email }}",
        "message": "=<p style=\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 16px; color: #1f2937;\">\n  Hi <strong>{{ $('Validate Input').item.json.name.split(\" \")[0] }}</strong>,\n</p>\n\n<p style=\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 16px; color: #1f2937; line-height: 1.6;\">\n  Congratulations! Here is your <strong>official participant badge</strong> for:\n</p>\n\n<h2 style=\"text-align: center; color: #3b82f6; font-size: 28px; margin: 24px 0;\">\n  {{ $('Validate Input').item.json.event}}\n</h2>\n\n<table style=\"width: 100%; max-width: 600px; margin: 30px auto; background: #f8fafc; border-radius: 12px; padding: 20px; border: 1px solid #e2e8f0;\">\n  <tr>\n    <td style=\"padding: 8px 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 15px;\">\n      <strong>Badge ID:</strong>\n    </td>\n    <td style=\"font-family: monospace; background: #e2e8f0; padding: 6px 12px; border-radius: 8px; font-weight: bold;\">\n      {{ $('Generate Badge ID & URL').item.json.badgeId }}\n    </td>\n  </tr>\n  <tr>\n    <td style=\"padding: 8px 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 15px;\">\n      <strong>Verify Online:</strong>\n    </td>\n    <td>\n      <a href=\"{{ $('Generate Badge ID & URL').item.json.verifyUrl }}\" style=\"color: #3b82f6; text-decoration: none; font-weight: bold;\">Click here to verify</a>\n    </td>\n  </tr>\n</table>\n\n<p style=\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 16px; color: #1f2937; line-height: 1.6; text-align: center;\">\n  <strong>Print the attached PDF</strong> or show it on your phone at check-in.<br>\n  The QR code links directly to your verification page.\n</p>\n\n<hr style=\"border: none; border-top: 1px solid #e2e8f0; margin: 40px 0;\">\n\n<p style=\"text-align: center; color: #64748b; font-size: 14px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\">\n  See you at the hackathon!<br>\n  <strong>The {{ $('Validate Input').item.json.event }} Team</strong>\n</p>",
        "options": {
          "attachmentsUi": {
            "attachmentsBinary": [
              {}
            ]
          }
        },
        "subject": "=Your Official {{ $('Validate Input').item.json.event }} Badge"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "bdf8ffc3-04ba-4043-a27c-77114808573c",
      "name": "Success Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1456,
        288
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "c996c87f-9fb4-4321-b268-b5d1de4bee92",
      "name": "Verifi Email",
      "type": "n8n-nodes-verifiemail.verifiEmail",
      "position": [
        -96,
        304
      ],
      "parameters": {
        "email": "={{ $json.email }}"
      },
      "credentials": {
        "verifiEmailApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4a3dfcce-07f8-4302-a6f8-9d77d989c004",
      "name": "HTML to PDF",
      "type": "n8n-nodes-htmlcsstopdf.htmlcsstopdf",
      "position": [
        656,
        288
      ],
      "parameters": {
        "html_content": "=<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>{{ $('Webhook Trigger').item.json.body.event }} - Badge</title>\n  <style>\n    @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700;900&display=swap');\n    body { \n      margin: 0; \n      padding: 40px; \n      background: #0f172a; \n      font-family: 'Inter', 'Helvetica Neue', Arial, sans-serif; \n    }\n    .badge { \n      width: 1056px; \n      height: 816px; \n      margin: auto; \n      background: linear-gradient(135deg, #1e293b 0%, #334155 100%);\n      border-radius: 32px; \n      padding: 60px; \n      box-shadow: 0 30px 60px rgba(0,0,0,0.7); \n      position: relative; \n      color: white; \n      overflow: hidden;\n    }\n    .header { \n      font-size: 56px; \n      font-weight: 900; \n      text-align: center; \n      color: #60a5fa; \n      margin-bottom: 20px; \n      text-shadow: 0 4px 10px rgba(0,0,0,0.5);\n    }\n    .name { \n      font-size: 88px; \n      font-weight: 900; \n      text-align: center; \n      margin: 40px 0; \n      color: #e0f2fe; \n      letter-spacing: 2px;\n      text-shadow: 0 6px 15px rgba(0,0,0,0.6);\n    }\n    .details { \n      font-size: 36px; \n      line-height: 1.8; \n      margin: 40px 0; \n      text-align: center;\n    }\n    .qr { \n      position: absolute; \n      bottom: 80px; \n      right: 80px; \n      background: white; \n      padding: 16px; \n      border-radius: 16px; \n      box-shadow: 0 10px 30px rgba(0,0,0,0.4);\n    }\n    .logo { \n      position: absolute; \n      top: 60px; \n      left: 60px; \n      width: 220px; \n      border-radius: 12px;\n    }\n    .badge-id { \n      position: absolute; \n      bottom: 80px; \n      left: 80px; \n      font-size: 28px; \n      font-weight: bold; \n      opacity: 0.9; \n      background: rgba(255,255,255,0.1); \n      padding: 8px 16px; \n      border-radius: 8px;\n    }\n    .footer { \n      position: absolute; \n      bottom: 40px; \n      left: 0; \n      right: 0; \n      text-align: center; \n      font-size: 20px; \n      opacity: 0.7;\n    }\n  </style>\n</head>\n<body>\n  <div class=\"badge\">\n    <img src=\"https://yourdomain.com/logo-white.png\" class=\"logo\" alt=\"Event Logo\">\n    <div class=\"header\">{{ $('Webhook Trigger').item.json.body.event.toUpperCase()}}</div>\n    <div class=\"name\">{{ $('Webhook Trigger').item.json.body.name.toUpperCase()}}</div>\n    <div class=\"details\">\n      Team: <strong>{{ $('Webhook Trigger').item.json.body.team || 'Individual'}}</strong><br>\n      Role: <strong>{{ $('Webhook Trigger').item.json.body.role|| 'Participant'}}</strong><br><br>\n      Issued: <strong>{{ $json.issuedAt }}</strong>\n    </div>\n    <div class=\"badge-id\">Badge ID: {{ $json.badgeId }}</div>\n    <div class=\"qr\">\n      <img src=\"{{ $json.qrCodeUrl }}\" width=\"220\" height=\"220\" alt=\"QR Code\">\n    </div>\n    <div class=\"footer\">Scan to verify \u2022 Show at check-in</div>\n  </div>\n</body>\n</html>"
      },
      "credentials": {
        "htmlcsstopdfApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "416e76d5-1ff1-4d86-b937-dfcb69e3b9fa",
      "name": "Log to Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1296,
        288
      ],
      "parameters": {
        "columns": {
          "value": {
            "Name": "={{ $('Validate Input').item.json.name }}",
            "Role": "={{ $('Validate Input').item.json.role }}",
            "Team": "={{ $('Validate Input').item.json.team }}",
            "Email": "={{ $('Validate Input').item.json.email }}",
            "Event": "={{ $('Validate Input').item.json.event }}",
            "Status": "Sent",
            "PDF URL": "={{ $('HTML to PDF').item.json.pdf_url }}",
            "Badge ID": "={{ $('Generate Badge ID & URL').item.json.badgeId }}",
            "Timestamp": "={{ $now }}",
            "Verify URL": "={{ $('Generate Badge ID & URL').item.json.verifyUrl }}",
            "Issued Date": "={{ $('Generate Badge ID & URL').item.json.issuedAt }}"
          },
          "schema": [
            {
              "id": "Timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Email",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Team",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Team",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Role",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Role",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Event",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Event",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Badge ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Badge ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Verify URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Verify URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Issued Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Issued Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "PDF URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "PDF URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_DOCUMENT_ID/edit#gid=0",
          "cachedResultName": "YOUR_SHEET_NAME"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_DOCUMENT_ID",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_DOCUMENT_ID/edit?usp=drivesdk",
          "cachedResultName": "YOUR_DOCUMENT_NAME"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "2b2adffb-dac3-4cf1-9b3c-a5936a162f25",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -928,
        336
      ],
      "parameters": {
        "height": 192,
        "content": "### **CREDENTIALS REQUIRED**\n\n* VerifiEmail at verifi.email\n* HTML \u2192 PDF at https://pdfmunk.com\n* Gmail \n* Google Sheets\n\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "f18dafc5-23eb-454d-9ff3-8e5086f086ae",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -928,
        -112
      ],
      "parameters": {
        "width": 384,
        "height": 432,
        "content": "# Hackathon Badge Workflow\n\n### **HACKATHON BADGE WORKFLOW \u2013 FULLY AUTOMATED**\n\n**Webhook \u2192 Validate \u2192 Email Check \u2192 Generate ID + QR \u2192 HTML \u2192 PDF \u2192 Gmail + Sheets \u2192 Done**\n\n**What it does:**\n\n* Receives registration via POST\n* Blocks fake/disposable emails\n* Instantly creates PDF badge with QR\n* Emails inline preview + PDF attachment\n* Logs all details to Google Sheets\n"
      },
      "typeVersion": 1
    },
    {
      "id": "50b61d60-8bde-4a51-9394-ba98594c863c",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -320,
        -176
      ],
      "parameters": {
        "color": 5,
        "width": 672,
        "height": 640,
        "content": "# Validation Group \u2014 Validation & Safety\n\n## **VALIDATION & SAFETY (Blocks junk early)**\n\n**1. Validate Input**\n\n* Checks required fields: name, email, event\n* Rejects incomplete or invalid formats\n\n**2. VerifiEmail Node**\n\n* Real-time deliverability + disposable check\n* Only valid emails continue\n\n**3. Email Valid? (IF Node)**\n\n* **True:** continue\n* **False:** optional rejection path\n\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "e123be75-b60d-4b1d-9596-19231ac79923",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        384,
        -64
      ],
      "parameters": {
        "color": 5,
        "width": 432,
        "height": 496,
        "content": "#  Badge Creation Group \u2014 *Badge Creation*\n\n## BADGE CREATION \n\n**1. Generate Badge ID & URL**\n* Creates unique ID + verifyUrl\n* Generates QR code link\n* Sets formatted issue date\n\n**2. HTML to PDF**\n* Returns ready PDF URL\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "c8115329-27bd-443b-b175-43b1945af610",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        880,
        -112
      ],
      "parameters": {
        "color": 5,
        "width": 768,
        "height": 576,
        "content": "# Delivery & Logging Group\n\n## DELIVERY & LOGGING\n\n**1. Download PDF**\n* Fetches final badge PDF\n\n**2. Send Badge via Gmail**\n* Inline badge preview + PDF attachment\n* Polished HTML email\n\n**3. Log to Sheets**\n* One row: Name, Email, Team, Badge ID, Verify URL, PDF link\n\n**4. Success Response**\n* JSON confirmation returned to caller\n\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "",
  "connections": {
    "HTML to PDF": {
      "main": [
        [
          {
            "node": "Download PDF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download PDF": {
      "main": [
        [
          {
            "node": "Send Badge via Gmail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email Valid?": {
      "main": [
        [
          {
            "node": "Generate Badge ID & URL",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Verifi Email": {
      "main": [
        [
          {
            "node": "Email Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log to Sheets": {
      "main": [
        [
          {
            "node": "Success Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Input": {
      "main": [
        [
          {
            "node": "Verifi Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Validate Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Badge via Gmail": {
      "main": [
        [
          {
            "node": "Log to Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Badge ID & URL": {
      "main": [
        [
          {
            "node": "HTML to PDF",
            "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

Hackathon Participant Badge Generator with QR Code & Email Delivery

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

More Web Scraping workflows → · Browse all categories →

Related workflows

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

Web Scraping

Overview

HTTP Request, Gmail, Google Sheets +2
Web Scraping

Verified Corporate Training Certificate with CEUs – Fully Automated & Verifiable

Google Drive, Slack, Google Sheets +4
Web Scraping

A comprehensive n8n workflow template that completely automates the startup pitch deck submission process for accelerators, incubators, VC firms, and startup competitions. This workflow validates foun

Google Drive, Gmail, N8N Nodes Verifiemail +2
Web Scraping

Transform your event registration process with this comprehensive automation that eliminates manual certificate creation and ensures only verified attendees receive credentials.

HTTP Request, Gmail, Google Sheets +4
Web Scraping

This workflow automates the entire parent consent process for school field trips, replacing manual paper forms with a secure, verified, and legally compliant digital system.

Google Drive, Gmail, N8N Nodes Verifiemail +2