8
n8n 한국어amn8n.com

자격 증명 생성기

고급

이것은Content Creation, Multimodal AI분야의자동화 워크플로우로, 25개의 노드를 포함합니다.주로 If, Code, Gmail, Slack, Webhook 등의 노드를 사용하며. VerifiEmail과 HTMLcsstoImg를 사용하여 인증서를 생성并통해 Gmail을 통해 발송

사전 요구사항
  • Google 계정 및 Gmail API 인증 정보
  • Slack Bot Token 또는 Webhook URL
  • HTTP Webhook 엔드포인트(n8n이 자동으로 생성)
  • Google Sheets API 인증 정보
워크플로우 미리보기
노드 연결 관계를 시각적으로 표시하며, 확대/축소 및 이동을 지원합니다
워크플로우 내보내기
다음 JSON 구성을 복사하여 n8n에 가져오면 이 워크플로우를 사용할 수 있습니다
{
  "id": "",
  "meta": {
    "instanceId": ""
  },
  "name": "certificate generator",
  "tags": [
    {
      "id": "CWzMtsCN0QfPR9PC",
      "name": "certificates",
      "createdAt": "2025-10-04T15:28:20.021Z",
      "updatedAt": "2025-10-04T15:28:20.021Z"
    },
    {
      "id": "qFR253LK8bI8GX4z",
      "name": "automation",
      "createdAt": "2025-10-04T15:28:20.027Z",
      "updatedAt": "2025-10-04T15:28:20.027Z"
    }
  ],
  "nodes": [
    {
      "id": "8aef6c7c-9dfd-483b-9b66-ee9a21ad0e3b",
      "name": "자격 증명 설정 가이드",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3024,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 452,
        "height": 856,
        "content": "## 🔧 CREDENTIALS SETUP REQUIRED\n\nBefore activating this workflow, configure these credentials:\n\n1. VerifiEmail API\n   - Sign up at https://verifi.email\n   - Get your API key from dashboard\n   - In n8n: Add VerifiEmail credential\n   - Paste API key\n\n2. HTMLcsstoImage API\n   - Sign up at https://htmlcsstoimg.com\n   - Get User ID and API Key\n   - In n8n: Add HTMLcsstoImg credential\n   - Enter User ID and API Key\n\n3. Gmail OAuth2 (for sending emails)\n   - Go to Settings > Credentials\n   - Add Gmail OAuth2\n   - Follow Google OAuth setup\n   - Grant necessary permissions\n   - Test connection\n\n4. Google Sheets OAuth2 (for logging)\n   - Add Google Sheets OAuth2 credential\n   - Create spreadsheet: \"Certificates Log\"\n   - Add these column headers:\n     • Certificate ID\n     • Recipient Name\n     • Course\n     • Email\n     • Completion Date\n     • Generated At\n     • Certificate URL\n     • Status\n     • Instructor\n     • Duration\n\n✅ All credentials configured in this workflow"
      },
      "typeVersion": 1
    },
    {
      "id": "713712a6-27e5-482b-8994-eae001ab2785",
      "name": "Webhook 정보",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2512,
        64
      ],
      "parameters": {
        "color": 7,
        "width": 444,
        "height": 628,
        "content": "## 📥 WEBHOOK TRIGGER\n\nAccepts POST requests with certificate data\n\nRequired JSON format:\n```\n{\n  \"name\": \"John Doe\",\n  \"course\": \"Course Name\",\n  \"date\": \"2025-10-04\",\n  \"email\": \"user@example.com\"\n}\n```\nOptional fields:\n```\n{\n  \"instructor\": \"Jane Smith\",\n  \"duration\": \"40 hours\",\n  \"certificateId\": \"CERT-2025-001\"\n}\n```\n🔗 Your Webhook URL:\n```https://n8n.exildraw.com/webhook-test/certificate-generator```\n\n📝 All data is accessed via:\n{{ $json.body.fieldName }}"
      },
      "typeVersion": 1
    },
    {
      "id": "a8b8e036-c01d-4091-b817-43611a1e2643",
      "name": "증명서 요청 Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -2192,
        544
      ],
      "webhookId": "certificate-generator",
      "parameters": {
        "path": "certificate-generator",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "lastNode"
      },
      "typeVersion": 2
    },
    {
      "id": "3129040e-f390-4661-b17a-32b5c029cd8b",
      "name": "이메일 검증 정보",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2016,
        144
      ],
      "parameters": {
        "color": 7,
        "width": 380,
        "height": 548,
        "content": "## 📧 EMAIL VALIDATION STEP\n\nUses VerifiEmail API to validate recipient email\n\nChecks performed:\n✓ RFC Compliance (proper email format)\n✓ Valid MX Records (domain accepts email)\n✓ Spoof Detection (checks for fake domains)\n✓ Disposable Email Detection (blocks temp emails)\n\nOutput includes:\n• valid: true/false\n• provider: email service provider\n• MX records: mail server details\n\n⚠️ Credential Required: VerifiEmail API key\n\nNext Step: IF node validates this + other fields"
      },
      "typeVersion": 1
    },
    {
      "id": "5c1071ea-cc45-49b2-9498-083d3a09508b",
      "name": "검증 로직",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1584,
        80
      ],
      "parameters": {
        "color": 7,
        "width": 360,
        "height": 596,
        "content": "## ✅ VALIDATION CHECKPOINT\n\nChecks ALL required conditions:\n\nFrom Webhook:\n1. Name is not empty\n2. Course is not empty\n3. Date is not empty\n\nFrom VerifiEmail:\n4. Email validation = true\n\nAll conditions must pass (AND logic)\n\n✓ TRUE → Continue to Combine Data\n✗ FALSE → Stop workflow with error\n\nAccessing data:\n• Webhook: $('Certificate Request Webhook').item.json.body.field\n• Email: $json.valid"
      },
      "typeVersion": 1
    },
    {
      "id": "745c8af8-c38e-4257-968d-c8abeb30e5d3",
      "name": "데이터 결합 정보",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1184,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 380,
        "height": 716,
        "content": "## 🔄 COMBINE WEBHOOK + EMAIL DATA\n\nMerges certificate data with email validation results\n\nInput sources:\n• Webhook data: name, course, date, email, instructor, duration\n• Email validation: valid, provider, MX records\n\nOutput structure:\n```\n{\n  name, course, date, email,\n  instructor, duration, certificateId,\n  emailValidation: {\n    valid, provider, rfcCompliant,\n    validMxRecord, disposable\n  }\n}\n```\nPurpose: Creates clean data structure for HTML generation\n\n⚡ Auto-generates certificateId if not provided\n📅 Keeps original date format for later formatting"
      },
      "typeVersion": 1
    },
    {
      "id": "99566da7-4b33-4df0-a280-5a5db861bf13",
      "name": "HTML 생성 정보",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -768,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 380,
        "height": 724,
        "content": "## 🎨 HTML CERTIFICATE GENERATOR\n\nCreates beautiful HTML certificate with:\n\nDesign Features:\n• Purple gradient background (#667eea → #764ba2)\n• Professional typography (Playfair Display + Montserrat)\n• Gold achievement badge with star\n• Clean white certificate with borders\n• Signature section with instructor name\n• Formatted completion date\n\nDynamic Content:\n• Recipient name (large, centered)\n• Course name\n• Completion date (formatted as \"Month Day, Year\")\n• Certificate ID (bottom right)\n• Instructor name (or \"Program Director\" if not provided)\n• Duration (optional, shown if provided)\n\nAuto-generates:\n• Unique Certificate ID if not provided\n• Format: CERT-{timestamp}-{random}\n\nOutput: Complete HTML ready for image conversion"
      },
      "typeVersion": 1
    },
    {
      "id": "d2f582b7-9d23-43b9-bc05-e50f96ad122f",
      "name": "HTML 증명서 생성",
      "type": "n8n-nodes-base.code",
      "position": [
        -640,
        528
      ],
      "parameters": {
        "jsCode": "// Certificate HTML Generator\nconst items = $input.all();\n\nreturn items.map(item => {\n  const data = item.json;\n  \n  // Generate unique certificate ID if not provided\n  const certId = data.certificateId || `CERT-${Date.now()}-${Math.random().toString(36).substr(2, 9).toUpperCase()}`;\n  \n  // Format date nicely\n  const certDate = new Date(data.date).toLocaleDateString('en-US', {\n    year: 'numeric',\n    month: 'long',\n    day: 'numeric'\n  });\n  \n  // HTML Template with inline CSS\n  const htmlContent = `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Certificate of Completion</title>\n    <style>\n        @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;700&family=Montserrat:wght@300;400;600&display=swap');\n        \n        * {\n            margin: 0;\n            padding: 0;\n            box-sizing: border-box;\n        }\n        \n        body {\n            width: 1200px;\n            height: 850px;\n            font-family: 'Montserrat', sans-serif;\n            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n            display: flex;\n            justify-content: center;\n            align-items: center;\n            padding: 40px;\n        }\n        \n        .certificate {\n            background: white;\n            width: 100%;\n            height: 100%;\n            border: 20px solid #f8f9fa;\n            box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n            position: relative;\n            padding: 60px 80px;\n            display: flex;\n            flex-direction: column;\n            justify-content: space-between;\n        }\n        \n        .certificate::before {\n            content: '';\n            position: absolute;\n            top: 35px;\n            left: 35px;\n            right: 35px;\n            bottom: 35px;\n            border: 3px solid #667eea;\n            pointer-events: none;\n        }\n        \n        .header {\n            text-align: center;\n            margin-bottom: 20px;\n        }\n        \n        .logo {\n            width: 80px;\n            height: 80px;\n            margin: 0 auto 15px;\n            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n            border-radius: 50%;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            color: white;\n            font-size: 36px;\n            font-weight: bold;\n        }\n        \n        .title {\n            font-family: 'Playfair Display', serif;\n            font-size: 48px;\n            font-weight: 700;\n            color: #2d3748;\n            letter-spacing: 2px;\n            margin-bottom: 10px;\n        }\n        \n        .subtitle {\n            font-size: 18px;\n            color: #718096;\n            text-transform: uppercase;\n            letter-spacing: 3px;\n            font-weight: 300;\n        }\n        \n        .content {\n            text-align: center;\n            flex-grow: 1;\n            display: flex;\n            flex-direction: column;\n            justify-content: center;\n        }\n        \n        .presented-to {\n            font-size: 16px;\n            color: #718096;\n            text-transform: uppercase;\n            letter-spacing: 2px;\n            margin-bottom: 20px;\n        }\n        \n        .recipient-name {\n            font-family: 'Playfair Display', serif;\n            font-size: 56px;\n            font-weight: 700;\n            color: #667eea;\n            margin-bottom: 30px;\n            padding-bottom: 15px;\n            border-bottom: 3px solid #e2e8f0;\n            display: inline-block;\n            min-width: 500px;\n        }\n        \n        .description {\n            font-size: 18px;\n            color: #4a5568;\n            line-height: 1.8;\n            margin-bottom: 25px;\n            max-width: 700px;\n            margin-left: auto;\n            margin-right: auto;\n        }\n        \n        .course-name {\n            font-weight: 600;\n            color: #2d3748;\n            font-size: 24px;\n            margin: 20px 0;\n        }\n        \n        .footer {\n            display: flex;\n            justify-content: space-between;\n            align-items: flex-end;\n            margin-top: 40px;\n        }\n        \n        .signature-block {\n            text-align: center;\n            flex: 1;\n        }\n        \n        .signature-line {\n            border-top: 2px solid #2d3748;\n            width: 250px;\n            margin: 0 auto 10px;\n            padding-top: 5px;\n        }\n        \n        .signature-name {\n            font-weight: 600;\n            color: #2d3748;\n            font-size: 16px;\n            margin-bottom: 5px;\n        }\n        \n        .signature-title {\n            font-size: 14px;\n            color: #718096;\n        }\n        \n        .date-block {\n            text-align: center;\n        }\n        \n        .date-label {\n            font-size: 12px;\n            color: #718096;\n            text-transform: uppercase;\n            letter-spacing: 1px;\n            margin-bottom: 5px;\n        }\n        \n        .date-value {\n            font-weight: 600;\n            color: #2d3748;\n            font-size: 16px;\n        }\n        \n        .cert-id {\n            position: absolute;\n            bottom: 15px;\n            right: 25px;\n            font-size: 10px;\n            color: #a0aec0;\n            letter-spacing: 1px;\n        }\n        \n        .badge {\n            position: absolute;\n            top: 50px;\n            right: 60px;\n            width: 100px;\n            height: 100px;\n            background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%);\n            border-radius: 50%;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            box-shadow: 0 4px 15px rgba(255, 215, 0, 0.4);\n        }\n        \n        .badge::before {\n            content: '★';\n            font-size: 50px;\n            color: white;\n        }\n    </style>\n</head>\n<body>\n    <div class=\"certificate\">\n        <div class=\"badge\"></div>\n        \n        <div class=\"header\">\n            <div class=\"logo\">C</div>\n            <h1 class=\"title\">Certificate of Completion</h1>\n            <p class=\"subtitle\">This certifies that</p>\n        </div>\n        \n        <div class=\"content\">\n            <div class=\"recipient-name\">${data.name}</div>\n            \n            <p class=\"description\">\n                has successfully completed the course\n            </p>\n            \n            <div class=\"course-name\">${data.course}</div>\n            \n            ${data.duration ? `<p class=\"description\" style=\"margin-top: 10px; font-size: 16px;\">Duration: ${data.duration}</p>` : ''}\n        </div>\n        \n        <div class=\"footer\">\n            <div class=\"signature-block\">\n                <div class=\"signature-line\"></div>\n                <div class=\"signature-name\">${data.instructor || 'Program Director'}</div>\n                <div class=\"signature-title\">Instructor</div>\n            </div>\n            \n            <div class=\"date-block\">\n                <div class=\"date-label\">Date of Completion</div>\n                <div class=\"date-value\">${certDate}</div>\n            </div>\n        </div>\n        \n        <div class=\"cert-id\">Certificate ID: ${certId}</div>\n    </div>\n</body>\n</html>\n  `;\n  \n  return {\n    json: {\n      ...data,\n      certificateId: certId,\n      html: htmlContent,\n      formattedDate: certDate\n    }\n  };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "9e764afe-be0f-4a6a-b5fc-4e2213353ae4",
      "name": "이미지 변환 정보",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -320,
        -16
      ],
      "parameters": {
        "color": 7,
        "width": 380,
        "height": 704,
        "content": "## IMAGE CONVERSION\n\nConverts HTML certificate to high-quality PNG image\n\nService: HTMLcsstoImg API\nAPI Endpoint: hcti.io\n\nConfiguration:\n• Input: {{ $json.html }}\n• Google Fonts: Playfair Display|Montserrat\n• Output: PNG image\n• Resolution: 1200x850px\n\nAPI Response:\n```\n{\n  \"image_url\": \"https://hcti.io/v1/image/[id]\",\n  \"image_id\": \"[unique-id]\"\n}\n```\n⚠️ Credential Required: HTMLcsstoImg API\n   User ID + API Key configured\n\n✅ Direct shareable URL returned\n📥 Image hosted on HTMLcsstoImg servers\n🔗 Permanent URL (doesn't expire)"
      },
      "typeVersion": 1
    },
    {
      "id": "edac6e11-432b-42f2-9128-b39d4585bd5b",
      "name": "이메일 전송 정보",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        128,
        -128
      ],
      "parameters": {
        "color": 7,
        "width": 380,
        "height": 808,
        "content": "## CERTIFICATE EMAIL DELIVERY\n\nSends professional congratulatory email via Gmail\n\nEmail Contents:\n• Subject: \"🎓 Congratulations! Your Certificate of Completion\"\n• HTML-formatted message with:\n  - Gradient header with recipient name\n  - Course details in styled box\n  - Download button with certificate URL\n  - Certificate details table\n  - Call-to-action to share on LinkedIn\n  - Professional footer\n\nData Sources:\n• Recipient: From \"Code in JavaScript\" node\n• Certificate details: From \"Generate HTML Certificate\" node\n• Certificate URL: From \"HTML/CSS to Image\" node\n\nVariables used:\n• {{ $('Generate HTML Certificate').item.json.name }}\n• {{ $('Generate HTML Certificate').item.json.course }}\n• {{ $('Generate HTML Certificate').item.json.certificateId }}\n• {{ $json.image_url }}\n\n⚠️ Credential: Gmail OAuth2"
      },
      "typeVersion": 1
    },
    {
      "id": "7acdd47b-7014-4e0f-8b44-5acf326412be",
      "name": "증명서 이메일 전송",
      "type": "n8n-nodes-base.gmail",
      "position": [
        288,
        528
      ],
      "webhookId": "",
      "parameters": {
        "sendTo": "={{ $('Code in JavaScript').item.json.email }}",
        "message": "=<!DOCTYPE html>\n<html>\n<head>\n    <style>\n        body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; margin: 0; padding: 0; }\n        .container { max-width: 600px; margin: 0 auto; }\n        .header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); \n                  color: white; padding: 40px 20px; text-align: center; }\n        .header h1 { margin: 0; font-size: 28px; }\n        .content { background: #f8f9fa; padding: 40px 30px; }\n        .content p { margin: 15px 0; }\n        .course-box { background: white; border-left: 4px solid #667eea; \n                      padding: 20px; margin: 25px 0; border-radius: 5px; }\n        .course-box h2 { color: #667eea; margin: 0 0 10px 0; font-size: 22px; }\n        .button { background: #667eea; color: white; padding: 15px 30px; \n                  text-decoration: none; border-radius: 5px; display: inline-block; \n                  margin: 20px 0; font-weight: bold; }\n        .button:hover { background: #5568d3; }\n        .details { background: white; padding: 20px; border-radius: 5px; margin: 20px 0; }\n        .details ul { list-style: none; padding: 0; }\n        .details li { padding: 8px 0; border-bottom: 1px solid #e2e8f0; }\n        .details li:last-child { border-bottom: none; }\n        .details strong { color: #667eea; }\n        .footer { text-align: center; padding: 30px 20px; color: #718096; font-size: 14px; }\n        .footer p { margin: 5px 0; }\n    </style>\n</head>\n<body>\n    <div class=\"container\">\n        <div class=\"header\">\n            <h1>🎉 Congratulations, {{ $('Generate HTML Certificate').item.json.name }} !</h1>\n            <p style=\"margin: 10px 0 0 0; font-size: 16px;\">You've successfully completed your course</p>\n        </div>\n        \n        <div class=\"content\">\n            <p>We're thrilled to celebrate your achievement! You have successfully completed:</p>\n            \n            <div class=\"course-box\">\n                <h2>{{ $('Generate HTML Certificate').item.json.course }}</h2>\n                <p style=\"margin: 0; color: #718096;\">Completion Date: {{ $('Generate HTML Certificate').item.json.formattedDate }}</p>\n            </div>\n            \n            <p>Your certificate is ready and available for download. Share your accomplishment with your professional network!</p>\n            \n            <div style=\"text-align: center;\">\n                <a href=\"{{ $json.image_url }}\" class=\"button\">📥 Download Certificate</a>\n            </div>\n            \n            <div class=\"details\">\n                <p><strong>📋 Certificate Details:</strong></p>\n                <ul>\n                    <li><strong>Certificate ID:</strong>{{ $('Generate HTML Certificate').item.json.certificateId }} </li>\n                    <li><strong>Recipient:</strong>{{ $('Generate HTML Certificate').item.json.name }} </li>\n                    <li><strong>Course:</strong> {{ $('Generate HTML Certificate').item.json.course }}</li>\n                    <li><strong>Completion Date:</strong> {{ $('Generate HTML Certificate').item.json.formattedDate }}</li>\n                    {{ $json.duration ? '<li><strong>Duration:</strong> ' + $json.duration + '</li>' : '' }}\n                    {{ $json.instructor ? '<li><strong>Instructor:</strong> ' + $json.instructor + '</li>' : '' }}\n                </ul>\n            </div>\n            \n            <p><strong>💼 Share Your Success:</strong></p>\n            <p>Don't forget to add this certificate to your LinkedIn profile and share it with your network to showcase your new skills!</p>\n            \n            <p style=\"margin-top: 30px; color: #718096; font-size: 14px;\">If you have any questions about your certificate, please don't hesitate to reach out to our support team.</p>\n        </div>\n        \n        <div class=\"footer\">\n            <p><strong>Thank you for learning with us!</strong></p>\n            <p>© 2025 Your Organization. All rights reserved.</p>\n            <p style=\"font-size: 12px; margin-top: 15px;\">Certificate verification available at: yoursite.com/verify</p>\n        </div>\n    </div>\n</body>\n</html>",
        "options": {},
        "subject": "🎓 Congratulations! Your Certificate of Completion"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "",
          "name": "Gmail OAuth2"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "5820e8bb-2f26-4d04-8445-61ea99902d35",
      "name": "데이터베이스 로깅 정보",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        592,
        -176
      ],
      "parameters": {
        "color": 7,
        "width": 380,
        "height": 864,
        "content": "## GOOGLE SHEETS LOGGING\n\nRecords every certificate to spreadsheet for tracking\n\nSpreadsheet: \"Certificates Log\"\nSheet: Sheet1\n\nLogged Data (10 columns):\n1. Certificate ID - Unique identifier\n2. Recipient Name - Full name\n3. Course - Course/program name\n4. Email - Recipient email\n5. Completion Date - Original date from webhook\n6. Generated At - Current timestamp (ISO format)\n7. Certificate URL - Direct image link\n8. Status - Always \"Sent\"\n9. Instructor - Instructor name\n10. Duration - Course duration\n\nData Sources:\n• Most fields: From \"Generate HTML Certificate\" node\n• Certificate URL: From \"HTML/CSS to Image\" node\n• Generated At: {{ $now.toISO() }}\n\nOperation: Append or Update Row\nMatch Column: Certificate ID (prevents duplicates)\n\n⚠️ Credential: Google Sheets OAuth2 \n\nPurpose: Audit trail, analytics, certificate verification"
      },
      "typeVersion": 1
    },
    {
      "id": "37b407ee-9362-402a-b708-5c821e1e1116",
      "name": "Google 시트에 기록",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        752,
        528
      ],
      "parameters": {
        "columns": {
          "value": {
            "Email": "={{ $('Generate HTML Certificate').item.json.email }}",
            "Course": "={{ $('Generate HTML Certificate').item.json.course }}",
            "Status": "Sent",
            "Duration": "={{ $('Generate HTML Certificate').item.json.duration }}",
            "Instructor": "={{ $('Generate HTML Certificate').item.json.instructor }}",
            "Generated At": "={{ $now.toISO() }}",
            "Certificate ID": "={{ $('Generate HTML Certificate').item.json.certificateId }}",
            "Recipient Name": "={{ $('Generate HTML Certificate').item.json.name }}",
            "Certificate URL": "={{ $('HTML/CSS to Image').item.json.image_url }}",
            "Completion Date ": "={{ $('Generate HTML Certificate').item.json.date }}"
          },
          "schema": [
            {
              "id": "Certificate ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Certificate ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Recipient Name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Recipient Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Course",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Course",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Email",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Completion Date ",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Completion Date ",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Generated At",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Generated At",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Certificate URL",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Certificate URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Instructor",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Instructor",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Duration",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Duration",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Certificate ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": "Certificates Log"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "",
          "name": "Google Sheets OAuth2"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "559bc397-1e64-4f9e-91e0-69f01a2de564",
      "name": "성공 응답 정보",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1024,
        352
      ],
      "parameters": {
        "color": 7,
        "width": 716,
        "height": 624,
        "content": "## ⚠️ RECOMMENDED ADDITION\n\nMISSING: Success Response to Webhook\n\nCurrently, workflow completes but doesn't send response back to webhook caller.\n\nTo add:\n1. Add \"Respond to Webhook\" node\n   After \"Log to Google Sheets\"\n\n2. Configure response:\n   Response Code: 200\n   Response Body:\n   ```\n    {\n     \"success\": true,\n     \"message\": \"Certificate generated successfully!\",\n     \"certificateId\": \"{{ $('Generate HTML Certificate').item.json.certificateId }}\",\n     \"recipientEmail\": \"{{ $('Generate HTML Certificate').item.json.email }}\",\n     \"certificateUrl\": \"{{ $('HTML/CSS to Image').item.json.image_url }}\",\n     \"generatedAt\": \"{{ $now.toISO() }}\"\n   }\n    ```\nWhy it's important:\n• API integrations need confirmation\n• Useful for tracking in external systems\n• Provides certificate URL to caller\n• Better for automation workflows\n\nWithout it: Workflow succeeds but caller gets no response"
      },
      "typeVersion": 1
    },
    {
      "id": "bd541a23-e982-4b64-82e2-30aea3e7601d",
      "name": "오류 처리 정보",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2032,
        1088
      ],
      "parameters": {
        "color": 7,
        "width": 472,
        "height": 564,
        "content": "## ⚠️ ERROR HANDLING\n\nWorkflow includes error catching and notification\n\nError Trigger:\n• Catches any workflow errors\n• Activates separate error flow\n\nError Processing:\n1. Format Error Details (Code node)\n   - Extracts error message\n   - Identifies failed node\n   - Captures user data (name, email, course)\n   - Logs execution ID and timestamp\n\n2. Send Slack Alert (DISABLED)\n   - Would notify admin team\n   - Enable if you have Slack integration\n   - Shows error details and user info\n\nNote: Slack alert currently disabled\nTo enable: Configure Slack credential and enable node\n\nMissing: Error response to webhook caller\nRecommendation: Add \"Respond to Webhook\" node after error trigger to return 500 status code"
      },
      "typeVersion": 1
    },
    {
      "id": "429a6ac5-d269-4176-ac81-d65117df54dd",
      "name": "워크플로우 오류 시",
      "type": "n8n-nodes-base.errorTrigger",
      "position": [
        -1680,
        1216
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "e3cc736c-3a91-42d5-9037-5ffcad3e8cab",
      "name": "오류 세부 정보 형식화",
      "type": "n8n-nodes-base.code",
      "position": [
        -1472,
        1216
      ],
      "parameters": {
        "jsCode": "// Extract error information\nconst errorData = $input.first().json;\n\nreturn [{\n  json: {\n    errorMessage: errorData.error?.message || 'Unknown error occurred',\n    errorNode: errorData.node?.name || 'Unknown node',\n    timestamp: new Date().toISOString(),\n    workflowId: $workflow.id,\n    workflowName: $workflow.name,\n    executionId: $execution.id,\n    userData: {\n      name: errorData.json?.name || 'N/A',\n      email: errorData.json?.email || 'N/A',\n      course: errorData.json?.course || 'N/A'\n    },\n    fullError: JSON.stringify(errorData, null, 2)\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "c1ec5078-2bc7-4f7d-b413-fa0ca7691a15",
      "name": "Slack 경고 전송",
      "type": "n8n-nodes-base.slack",
      "notes": "Optional: Enable if you want Slack notifications",
      "disabled": true,
      "position": [
        -1280,
        1216
      ],
      "webhookId": "",
      "parameters": {
        "text": "=🚨 **Certificate Generation Failed**\n\n**Error:** {{ $json.errorMessage }}\n**Node:** {{ $json.errorNode }}\n**Time:** {{ $json.timestamp }}\n\n**User Details:**\n• Name: {{ $json.userData.name }}\n• Email: {{ $json.userData.email }}\n• Course: {{ $json.userData.course }}\n\n**Execution ID:** {{ $json.executionId }}\n**Workflow:** {{ $json.workflowName }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C12345",
          "cachedResultName": "alerts"
        },
        "otherOptions": {}
      },
      "typeVersion": 2.2
    },
    {
      "id": "7d756f81-a0ac-4b11-bf07-dded65a5070d",
      "name": "워크플로우 개요",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3376,
        800
      ],
      "parameters": {
        "color": 7,
        "width": 444,
        "height": 760,
        "content": "## COMPLETE WORKFLOW STRUCTURE\n\nMain Certificate Generation Flow:\n\n1. 📥 Webhook Trigger\n   Receives JSON with certificate data\n\n2. 📧 Verifi Email\n   Validates email address with VerifiEmail API\n\n3. ✅ IF Validation\n   Checks: email valid + name/course/date not empty\n\n4. 🔄 Combine Data (Code node)\n   Merges webhook data + email validation\n\n5. 🎨 Generate HTML Certificate (Code node)\n   Creates beautiful HTML certificate\n\n6. 🖼️ HTML/CSS to Image\n   Converts HTML → PNG image\n\n7. 📧 Send Email (Gmail)\n   Delivers certificate to recipient\n\n8. 📊 Log to Google Sheets\n   Records certificate details\n\nError Flow (Separate):\n• On Workflow Error → Format Error → Slack Alert\n\nStatus: ✅ Fully functional workflow\nMissing: Success response to webhook (recommended)\n\nAll credentials configured and tested"
      },
      "typeVersion": 1
    },
    {
      "id": "7c4dc0c1-ae2b-4985-91de-a40356e28158",
      "name": "테스트 지침",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2896,
        832
      ],
      "parameters": {
        "color": 7,
        "width": 568,
        "height": 856,
        "content": "## 🧪 TESTING YOUR WORKFLOW\n\nWebhook URL:\nhttps://n8n.exildraw.com/webhook-test/certificate-generator\n\nTest with cURL:\n```\ncurl -X POST https://n8n.exildraw.com/webhook-test/certificate-generator \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"Test User\",\n    \"course\": \"Advanced JavaScript Programming\",\n    \"date\": \"2025-10-04\",\n    \"email\": \"test@gmail.com\",\n    \"instructor\": \"Jane Smith\",\n    \"duration\": \"40 hours\"\n  }'\n```\nTest with Postman:\n1. Method: POST\n2. URL: [webhook URL above]\n3. Headers: Content-Type = application/json\n4. Body: Use JSON from curl example\n\nExpected Results:\n✅ Email validation passes\n✅ HTML certificate generated\n✅ Image created at HTMLcsstoImg\n✅ Email sent to recipient\n✅ Entry logged in Google Sheets\n✅ All nodes execute successfully\n\nValidation Test (should fail):\n• Missing required field\n• Invalid email format\n• Disposable email address\n\n⚠️ Note: Use real email addresses for testing\nexample.com won't pass MX record validation\n\nCurrent Status: All nodes configured and ready\nNext: Add \"Respond to Webhook\" for success response"
      },
      "typeVersion": 1
    },
    {
      "id": "b9b4ba6f-35ca-4733-9d0a-0e2a7da5b56e",
      "name": "HTML/CSS to Image",
      "type": "n8n-nodes-htmlcsstoimage.htmlCssToImage",
      "position": [
        -160,
        528
      ],
      "parameters": {
        "html_content": "={{ $json.html }}"
      },
      "credentials": {
        "htmlcsstoimgApi": {
          "id": "",
          "name": "Htmlcsstoimg API"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "de9c267d-d005-43d4-bbb3-c33370f9493a",
      "name": "Verifi Email",
      "type": "n8n-nodes-verifiemail.verifiEmail",
      "position": [
        -1872,
        544
      ],
      "parameters": {
        "email": "={{ $json.body.email }}"
      },
      "credentials": {
        "verifiEmailApi": {
          "id": "",
          "name": "VerifiEmail API"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "646e0a7b-2cdc-48c0-8f15-5b7755e9e484",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        -1392,
        544
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "a39497f8-8b8a-4b3a-bc5f-3e8738fa4cbb",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $('Certificate Request Webhook').item.json.body.name }}",
              "rightValue": ""
            },
            {
              "id": "37e0295c-18b2-4d3f-998b-e9d669dd90f7",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $('Certificate Request Webhook').item.json.body.course }}",
              "rightValue": ""
            },
            {
              "id": "6ecb1197-b460-402e-99a1-00edb0d5150c",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $('Certificate Request Webhook').item.json.body.date }}",
              "rightValue": ""
            },
            {
              "id": "3828fe0a-7c6a-449a-b380-c754717e0731",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.valid }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "9b7e5242-b3da-4552-b088-d585ffe7bcb0",
      "name": "Stop and Error",
      "type": "n8n-nodes-base.stopAndError",
      "position": [
        -1040,
        784
      ],
      "parameters": {
        "errorMessage": "=\"Missing required fields: name, course, date, or valid email\""
      },
      "typeVersion": 1
    },
    {
      "id": "b57d3884-9846-4b1f-9b00-ec378ea386ac",
      "name": "Code in JavaScript",
      "type": "n8n-nodes-base.code",
      "position": [
        -1056,
        528
      ],
      "parameters": {
        "jsCode": "// Combine Webhook Data with Email Validation\n// Get the current item (from IF node - email validation data)\nconst emailData = $input.item.json;\n\n// Get the original webhook data from the first node\nconst webhookData = $('Certificate Request Webhook').first().json.body;\n\n// Combine everything\nreturn {\n  json: {\n    // Certificate data from webhook\n    name: webhookData.name,\n    course: webhookData.course,\n    date: webhookData.date,\n    email: webhookData.email,\n    instructor: webhookData.instructor || 'Program Director',\n    duration: webhookData.duration || '',\n    certificateId: webhookData.certificateId || '',\n    \n    // Email validation results (optional - for logging/debugging)\n    emailValidation: {\n      valid: emailData.valid,\n      provider: emailData.details.mx.provider,\n      rfcCompliant: emailData.details.rfcCompliant,\n      validMxRecord: emailData.details.validMxRecord,\n      disposable: emailData.details.disposable\n    }\n  }\n};"
      },
      "typeVersion": 2
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "",
  "connections": {
    "646e0a7b-2cdc-48c0-8f15-5b7755e9e484": {
      "main": [
        [
          {
            "node": "b57d3884-9846-4b1f-9b00-ec378ea386ac",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "9b7e5242-b3da-4552-b088-d585ffe7bcb0",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "de9c267d-d005-43d4-bbb3-c33370f9493a": {
      "main": [
        [
          {
            "node": "646e0a7b-2cdc-48c0-8f15-5b7755e9e484",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "b9b4ba6f-35ca-4733-9d0a-0e2a7da5b56e": {
      "main": [
        [
          {
            "node": "7acdd47b-7014-4e0f-8b44-5acf326412be",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "429a6ac5-d269-4176-ac81-d65117df54dd": {
      "main": [
        [
          {
            "node": "e3cc736c-3a91-42d5-9037-5ffcad3e8cab",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "b57d3884-9846-4b1f-9b00-ec378ea386ac": {
      "main": [
        [
          {
            "node": "d2f582b7-9d23-43b9-bc05-e50f96ad122f",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "e3cc736c-3a91-42d5-9037-5ffcad3e8cab": {
      "main": [
        [
          {
            "node": "c1ec5078-2bc7-4f7d-b413-fa0ca7691a15",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "7acdd47b-7014-4e0f-8b44-5acf326412be": {
      "main": [
        [
          {
            "node": "37b407ee-9362-402a-b708-5c821e1e1116",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "d2f582b7-9d23-43b9-bc05-e50f96ad122f": {
      "main": [
        [
          {
            "node": "b9b4ba6f-35ca-4733-9d0a-0e2a7da5b56e",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "a8b8e036-c01d-4091-b817-43611a1e2643": {
      "main": [
        [
          {
            "node": "de9c267d-d005-43d4-bbb3-c33370f9493a",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
자주 묻는 질문

이 워크플로우를 어떻게 사용하나요?

위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.

이 워크플로우는 어떤 시나리오에 적합한가요?

고급 - 콘텐츠 제작, 멀티모달 AI

유료인가요?

이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.

워크플로우 정보
난이도
고급
노드 수25
카테고리2
노드 유형11
난이도 설명

고급 사용자를 위한 16+개 노드의 복잡한 워크플로우

저자
Jitesh Dugar

Jitesh Dugar

@jiteshdugar

AI Automation Specialist - OpenAI, CRM & Automation Expert with a solid understanding of various tools that include Zapier, Make, Zoho CRM, Hubspot, Google Sheets, Airtable, Pipedrive, Google Analytics, and more.

외부 링크
n8n.io에서 보기

이 워크플로우 공유

카테고리

카테고리: 34