Generador de certificados
Este es unContent Creation, Multimodal AIflujo de automatización del dominio deautomatización que contiene 25 nodos.Utiliza principalmente nodos como If, Code, Gmail, Slack, Webhook. Generar certificados con VerifiEmail y HTMLcsstoImg y enviarlos por Gmail
- •Cuenta de Google y credenciales de API de Gmail
- •Bot Token de Slack o URL de Webhook
- •Punto final de HTTP Webhook (n8n generará automáticamente)
- •Credenciales de API de Google Sheets
Nodos utilizados (25)
Categoría
{
"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": "Guía de Configuración de Credenciales",
"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": "Información Disparador 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": "Solicitud de Certificado Disparador 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": "Información de Validación de Correo electrónico",
"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": "Lógica de Validación",
"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": "Información de Combinación de Datos",
"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": "Información de Generación 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": "Generar Certificado 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": "Información de Conversión a Imagen",
"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": "Información de Envío por Correo electrónico",
"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": "Enviar Certificado por Email",
"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": "Información de Registro en Base de Datos",
"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": "Registrar en Hojas 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": "Información de Respuesta de Éxito",
"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": "Información de Manejo de Errores",
"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": "En Error del Flujo de Trabajo",
"type": "n8n-nodes-base.errorTrigger",
"position": [
-1680,
1216
],
"parameters": {},
"typeVersion": 1
},
{
"id": "e3cc736c-3a91-42d5-9037-5ffcad3e8cab",
"name": "Formatear Detalles del Error",
"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": "Enviar Alerta 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": "Resumen del Flujo de Trabajo",
"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": "Instrucciones de Prueba",
"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 a Imagen",
"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": "Detener y 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": "Código 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": "Code in JavaScript",
"type": "main",
"index": 0
}
],
[
{
"node": "Stop and Error",
"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
}
]
]
},
"Code in JavaScript": {
"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
}
]
]
},
"Solicitud de Certificado Webhook": {
"main": [
[
{
"node": "de9c267d-d005-43d4-bbb3-c33370f9493a",
"type": "main",
"index": 0
}
]
]
}
}
}¿Cómo usar este flujo de trabajo?
Copie el código de configuración JSON de arriba, cree un nuevo flujo de trabajo en su instancia de n8n y seleccione "Importar desde JSON", pegue la configuración y luego modifique la configuración de credenciales según sea necesario.
¿En qué escenarios es adecuado este flujo de trabajo?
Avanzado - Creación de contenido, IA Multimodal
¿Es de pago?
Este flujo de trabajo es completamente gratuito, puede importarlo y usarlo directamente. Sin embargo, tenga en cuenta que los servicios de terceros utilizados en el flujo de trabajo (como la API de OpenAI) pueden requerir un pago por su cuenta.
Flujos de trabajo relacionados recomendados
Jitesh Dugar
@jiteshdugarAI 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.
Compartir este flujo de trabajo