{
  "id": "D3xrD6bKnTDX7ufR",
  "name": "n8n Starter Lab",
  "tags": [],
  "nodes": [
    {
      "id": "ab1aa2d8-228a-4d55-9238-26896d76645c",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -192,
        848
      ],
      "parameters": {
        "width": 816,
        "height": 320,
        "content": "## Live Event Lab QR Scan Attendance Booking\n\n### How it works\n\nThis workflow handles QR code-based event registration. Attendees scan a QR code, complete a registration form, and the form submits data to this webhook.\n\nThe workflow:\n- Validates required fields (name, email, eventId)\n- Checks Google Sheets for an existing registration\n- Returns a duplicate response if already registered\n- Otherwise saves the attendee, sends a Gmail confirmation, and returns success\n"
      },
      "typeVersion": 1
    },
    {
      "id": "9c83f1cf-b344-4dc0-87a9-15f54221d33f",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -192,
        1248
      ],
      "parameters": {
        "color": 7,
        "width": 1024,
        "height": 432,
        "content": "## Receive and validate request\n\nStarts from the webhook, validates the incoming payload, and branches invalid submissions to an immediate JSON error response."
      },
      "typeVersion": 1
    },
    {
      "id": "fced7f88-c7e6-4fe3-8ef9-16d014f9dd46",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        864,
        1120
      ],
      "parameters": {
        "color": 7,
        "width": 960,
        "height": 480,
        "content": "## Check existing registration\n\nReads the current Google Sheets rows, checks whether the submitted user is already present, and returns an already-registered response for duplicates."
      },
      "typeVersion": 1
    },
    {
      "id": "d158af51-7a4d-4d49-b8ce-a7c8c9440291",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1856,
        1216
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 368,
        "content": "## Save and notify user\n\nHandles the non-duplicate path by appending the new registration to Google Sheets and sending a confirmation email through Gmail."
      },
      "typeVersion": 1
    },
    {
      "id": "5454cdac-a641-4ecf-b560-aae734fbcdf2",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2352,
        1200
      ],
      "parameters": {
        "color": 7,
        "width": 368,
        "height": 384,
        "content": "## Return success response\n\nFinishes the successful registration path by sending a JSON response back to the webhook caller."
      },
      "typeVersion": 1
    },
    {
      "id": "054d9668-5bf1-4af8-81b1-4bc59870d197",
      "name": "Check Input Validity",
      "type": "n8n-nodes-base.code",
      "position": [
        160,
        1376
      ],
      "parameters": {
        "jsCode": "const body = $input.first().json.body || {};\nconst name = body.name?.trim();\nconst email = body.email?.trim();\nconst role = body.role?.trim() || 'Participant';\nconst eventId = body.eventId || 'n8n-starter-lab-2026';\nconst day = body.day?.trim() || 'Day 1';\nconst sheetName = day.replace(' ', '');\n\nif (!name || !email) {\n  return [{ json: { valid: false, message: 'Name and email are required.' } }];\n}\n\nconst emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\nif (!emailRegex.test(email)) {\n  return [{ json: { valid: false, message: 'Please enter a valid email address.' } }];\n}\n\nconst now = new Date();\nconst dateStr = now.toLocaleDateString('en-GB', { timeZone: 'Europe/Berlin', day: '2-digit', month: 'long', year: 'numeric' });\nconst timeStr = now.toLocaleTimeString('en-GB', { timeZone: 'Europe/Berlin', hour: '2-digit', minute: '2-digit', hour12: false });\nconst nameUpper = name.toUpperCase();\n\nreturn [{\n  json: {\n    valid: true,\n    name,\n    nameUpper,\n    email,\n    role,\n    eventId,\n    day,\n    sheetName,\n    timestamp: now.toISOString(),\n    date: dateStr,\n    time: timeStr\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "34c8a187-2ab3-4a1a-884d-cb649f3fa2e6",
      "name": "Check Validity Status",
      "type": "n8n-nodes-base.if",
      "position": [
        384,
        1376
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-001",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.valid }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "f40569f3-d49a-4675-ac92-9911dc1c7f6c",
      "name": "Retrieve Sheet Data",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        912,
        1360
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $json.day.replace(' ', '') }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1fzBymGE1OozTAxpor_KeAo8COsFZ1lEHDo3C69rOnCk",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1fzBymGE1OozTAxpor_KeAo8COsFZ1lEHDo3C69rOnCk/edit?usp=drivesdk",
          "cachedResultName": "Training Participants"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4,
      "alwaysOutputData": true
    },
    {
      "id": "c497c859-221d-446d-866a-f3cfb8d2a215",
      "name": "Identify Duplicates",
      "type": "n8n-nodes-base.code",
      "position": [
        1136,
        1360
      ],
      "parameters": {
        "jsCode": "const incomingEmail = $('Check Input Validity').first().json.email.toLowerCase().trim();\nconst rows = $input.all();\n\n// If no rows returned, sheet is empty \u2014 no duplicate\nif (!rows || rows.length === 0) {\n  return [{ json: { isDuplicate: false } }];\n}\n\n// Check if any row has matching email\nconst found = rows.some(row => {\n  const rowEmail = (row.json.email || '').toLowerCase().trim();\n  return rowEmail === incomingEmail;\n});\n\nreturn [{ json: { isDuplicate: found } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "01e2acb5-c687-4658-991a-14131b0119b2",
      "name": "If Duplicate Exists",
      "type": "n8n-nodes-base.if",
      "position": [
        1344,
        1360
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "dup-cond-001",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.isDuplicate }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "f0596d41-da56-44aa-8df2-03da415736b0",
      "name": "Add New Entry to Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1920,
        1376
      ],
      "parameters": {
        "columns": {
          "value": {
            "date": "={{ $('Check Input Validity').first().json.date }}",
            "name": "={{ $('Check Input Validity').first().json.name }}",
            "role": "={{ $('Check Input Validity').first().json.role }}",
            "time": "={{ $('Check Input Validity').first().json.time }}",
            "email": "={{ $('Check Input Validity').first().json.email }}",
            "eventId": "={{ $('Check Input Validity').first().json.eventId }}"
          },
          "schema": [
            {
              "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": "role",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "role",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "eventId",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "eventId",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "time",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Check Input Validity').first().json.sheetName }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1fzBymGE1OozTAxpor_KeAo8COsFZ1lEHDo3C69rOnCk",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1fzBymGE1OozTAxpor_KeAo8COsFZ1lEHDo3C69rOnCk/edit?usp=drivesdk",
          "cachedResultName": "Training Participants"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "440fb12f-064d-4fdd-82a3-3289da15fb20",
      "name": "Dispatch Confirmation Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2144,
        1376
      ],
      "parameters": {
        "sendTo": "={{ $('Check Input Validity').first().json.email }}",
        "message": "=<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"></head><body style=\"margin:0;padding:0;background:#f0f7f6;font-family:'Helvetica Neue',Arial,sans-serif;\"><table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:#f0f7f6;padding:40px 0;\"><tr><td align=\"center\"><table width=\"560\" cellpadding=\"0\" cellspacing=\"0\" style=\"max-width:560px;width:100%;\"><tr><td style=\"background:#2B7B6D;border-radius:12px 12px 0 0;padding:24px 36px;\"><span style=\"color:#fff;font-size:18px;font-weight:700;letter-spacing:-0.3px;\">n8n Training</span></td></tr><tr><td style=\"background:#ffffff;padding:36px 36px 32px;border-radius:0 0 12px 12px;box-shadow:0 4px 20px rgba(0,0,0,0.08);\"><p style=\"margin:0 0 4px;font-size:15px;color:#6b7280;\">Hi <strong style=\"color:#111827;\">{{ $('Check Input Validity').first().json.nameUpper }}</strong>,</p><h1 style=\"margin:0 0 20px;font-size:22px;font-weight:700;color:#111827;letter-spacing:-0.4px;\">Your attendance is confirmed! \ud83c\udf89</h1><p style=\"margin:0 0 28px;font-size:15px;color:#4b5563;line-height:1.65;\">Thank you for registering for <strong>n8n Training</strong>. We are excited to have you join us for a hands-on session on building automations with n8n.</p><table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:#f0faf8;border:1.5px solid #a7d4cf;border-radius:10px;margin-bottom:28px;\"><tr><td style=\"padding:20px 24px;\"><p style=\"margin:0 0 14px;font-size:11px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:#2B7B6D;\">Registration Details</p><table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"><tr><td style=\"padding:5px 0;font-size:13px;color:#6b7280;width:70px;\">Event</td><td style=\"padding:5px 0;font-size:13px;font-weight:600;color:#111827;\">{{ $('Check Input Validity').first().json.eventId }}</td></tr><tr><td style=\"padding:5px 0;font-size:13px;color:#6b7280;\">Day</td><td style=\"padding:5px 0;font-size:13px;font-weight:600;color:#111827;\">{{ $('Check Input Validity').first().json.day }}</td></tr><tr><td style=\"padding:5px 0;font-size:13px;color:#6b7280;\">Role</td><td style=\"padding:5px 0;font-size:13px;font-weight:600;color:#111827;\">{{ $('Check Input Validity').first().json.role }}</td></tr><tr><td style=\"padding:5px 0;font-size:13px;color:#6b7280;\">Name</td><td style=\"padding:5px 0;font-size:13px;font-weight:600;color:#111827;\">{{ $('Check Input Validity').first().json.nameUpper }}</td></tr><tr><td style=\"padding:5px 0;font-size:13px;color:#6b7280;\">Date</td><td style=\"padding:5px 0;font-size:13px;font-weight:600;color:#111827;\">{{ $('Check Input Validity').first().json.date }}</td></tr><tr><td style=\"padding:5px 0;font-size:13px;color:#6b7280;\">Time</td><td style=\"padding:5px 0;font-size:13px;font-weight:600;color:#111827;\">{{ $('Check Input Validity').first().json.time }} (CET)</td></tr></table></td></tr></table><p style=\"margin:0 0 14px;font-size:15px;color:#4b5563;line-height:1.65;\">Get ready to explore real-world automation workflows, connect nodes, and build your first working pipeline \u2014 no prior experience needed.</p><p style=\"margin:0 0 32px;font-size:15px;color:#4b5563;line-height:1.65;\">More details about the session schedule and resources will be shared closer to the event date. Stay tuned!</p><hr style=\"border:none;border-top:1px solid #e5f4f2;margin:0 0 24px;\"><p style=\"margin:0 0 4px;font-size:12px;color:#9ca3af;\">If you have any questions, feel free to reply to this email.</p><p style=\"margin:0;font-size:13px;color:#4b5563;\">Best regards,<br><strong style=\"color:#2B7B6D;\"> Training Team</strong></p></td></tr></table></td></tr></table></body></html>\n",
        "options": {
          "senderName": "Training Attendance"
        },
        "subject": "=Attendance Confirmed - {{ $('Check Input Validity').first().json.eventId }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "9bbe4dda-4ae9-4ea1-9ec0-70eb49f380d5",
      "name": "Return Success Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        2464,
        1376
      ],
      "parameters": {
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/json"
              },
              {
                "name": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ]
          }
        },
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ success: true, message: 'Attendance registered! Confirmation email sent.', name: $('Check Input Validity').first().json.name, role: $('Check Input Validity').first().json.role, eventId: $('Check Input Validity').first().json.eventId, timestamp: $('Check Input Validity').first().json.timestamp }) }}"
      },
      "typeVersion": 1
    },
    {
      "id": "22e51da3-cd59-48f0-a817-9bc6d6cc9c61",
      "name": "Return Error Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        608,
        1504
      ],
      "parameters": {
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/json"
              },
              {
                "name": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ]
          }
        },
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ success: false, message: $json.message || 'Validation failed.' }) }}"
      },
      "typeVersion": 1
    },
    {
      "id": "e8b37bd3-2fc4-4e93-8eaf-2dff5699c062",
      "name": "Return Duplicate Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1584,
        1232
      ],
      "parameters": {
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/json"
              },
              {
                "name": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ]
          }
        },
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ success: false, duplicate: true, message: 'Attendance already marked for this email.' }) }}"
      },
      "typeVersion": 1
    },
    {
      "id": "9bac5983-5232-48bd-a3a1-c5b212996542",
      "name": "Receive Webhook Data",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -96,
        1376
      ],
      "parameters": {
        "path": "register",
        "options": {
          "allowedOrigins": "*"
        },
        "httpMethod": "POST",
        "responseMode": "responseNode",
        "authentication": "basicAuth"
      },
      "credentials": {
        "httpBasicAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "520206d0-0199-4fa4-9cc5-8d9958c19fe4",
  "connections": {
    "Identify Duplicates": {
      "main": [
        [
          {
            "node": "If Duplicate Exists",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Duplicate Exists": {
      "main": [
        [
          {
            "node": "Return Duplicate Response",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Add New Entry to Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retrieve Sheet Data": {
      "main": [
        [
          {
            "node": "Identify Duplicates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Input Validity": {
      "main": [
        [
          {
            "node": "Check Validity Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Receive Webhook Data": {
      "main": [
        [
          {
            "node": "Check Input Validity",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Validity Status": {
      "main": [
        [
          {
            "node": "Retrieve Sheet Data",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Return Error Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add New Entry to Sheets": {
      "main": [
        [
          {
            "node": "Dispatch Confirmation Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dispatch Confirmation Email": {
      "main": [
        [
          {
            "node": "Return Success Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}