Générateur de certificats
Ceci est unContent Creation, Multimodal AIworkflow d'automatisation du domainecontenant 25 nœuds.Utilise principalement des nœuds comme If, Code, Gmail, Slack, Webhook. Génération de certificats en utilisant VerifiEmail et HTMLcsstoImg, et envoi via Gmail
- •Compte Google et informations d'identification Gmail API
- •Token Bot Slack ou URL Webhook
- •Point de terminaison HTTP Webhook (généré automatiquement par n8n)
- •Informations d'identification Google Sheets API
Nœuds utilisés (25)
Catégorie
{
"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": "Guide de Configuration des Identifiants",
"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 Info",
"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": "Demande de Certificat 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": "Info Validation d'Email",
"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": "Logique de Validation",
"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": "Info Combinaison de Données",
"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": "Info Génération 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": "Générer un Certificat 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": "Info Conversion d'Image",
"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": "Info Livraison d'Email",
"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": "Envoyer l'Email du Certificat",
"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": "Info Journalisation en Base de Données",
"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": "Journaliser vers Google Sheets",
"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": "Info Réponse de Succès",
"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": "Info Gestion des Erreurs",
"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 Cas d'Erreur du Workflow",
"type": "n8n-nodes-base.errorTrigger",
"position": [
-1680,
1216
],
"parameters": {},
"typeVersion": 1
},
{
"id": "e3cc736c-3a91-42d5-9037-5ffcad3e8cab",
"name": "Formater les Détails de l'Erreur",
"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": "Envoyer une Alerte 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": "Vue d'Ensemble du Workflow",
"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": "Instructions de Test",
"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 vers 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 en 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
}
]
]
}
}
}Comment utiliser ce workflow ?
Copiez le code de configuration JSON ci-dessus, créez un nouveau workflow dans votre instance n8n et sélectionnez "Importer depuis le JSON", collez la configuration et modifiez les paramètres d'authentification selon vos besoins.
Dans quelles scénarios ce workflow est-il adapté ?
Avancé - Création de contenu, IA Multimodale
Est-ce payant ?
Ce workflow est entièrement gratuit et peut être utilisé directement. Veuillez noter que les services tiers utilisés dans le workflow (comme l'API OpenAI) peuvent nécessiter un paiement de votre part.
Workflows recommandés
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.
Partager ce workflow