AutomationFlowsAI & RAG › Process Customer Feedback with Openai, PDF Reports, Gmail & Slack Notifications

Process Customer Feedback with Openai, PDF Reports, Gmail & Slack Notifications

ByJitesh Dugar @jiteshdugar on n8n.io

Transform customer feedback into actionable insights automatically with AI analysis, professional PDF reports, personalized emails, and real-time team notifications. Overview Features Demo Prerequisites Quick Start Configuration Usage Troubleshooting License

Webhook trigger★★★★☆ complexityAI-powered26 nodesOpenAIGmailGoogle SheetsSlackN8N Nodes Htmlcsstopdf
AI & RAG Trigger: Webhook Nodes: 26 Complexity: ★★★★☆ AI nodes: yes Added:

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

This workflow follows the Gmail → Google Sheets recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "name": "Personalized Feedback Summary Generator",
  "tags": [
    {
      "id": "feedback",
      "name": "Feedback"
    },
    {
      "id": "ai",
      "name": "AI"
    },
    {
      "id": "automation",
      "name": "Automation"
    },
    {
      "id": "pdf",
      "name": "PDF Generation"
    },
    {
      "id": "email",
      "name": "Email"
    }
  ],
  "nodes": [
    {
      "id": "1673a4f2-dac3-4a2f-89cf-8c95d02c0fc6",
      "name": "Sticky Note - Credentials Setup1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -7264,
        -880
      ],
      "parameters": {
        "color": 7,
        "width": 389,
        "height": 656,
        "content": "## \ud83d\udd10 CREDENTIALS SETUP REQUIRED\n\n**Before running this workflow, configure these credentials:**\n\n1. **OpenAI Account**\n   - Model: GPT-3.5-turbo (cost-effective) or GPT-4 (more accurate)\n\n2. **Gmail OAuth2**\n   - Connect via OAuth2 in n8n credentials\n\n3. **Google Sheets OAuth2**\n   - Create spreadsheet: \"Feedback Log\"\n   - Add columns: Submission ID, Timestamp, Name, Email, Rating, Sentiment, Comments, Suggestions, AI Summary, PDF URL, PDF Available Until, Email Sent\n\n4. **Slack OAuth2**\n   - Required scopes: chat:write, channels:read\n\n5. **HTML to PDF API**\n   - Sign up at hhttps://pdfmunk.com\n\n6. VerifiEmail API\n   - GET API at https://verifi.email"
      },
      "typeVersion": 1
    },
    {
      "id": "4959077e-1f6a-43f8-ad2c-30bd0baa7c8f",
      "name": "Sticky Note - Trigger1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -6160,
        -864
      ],
      "parameters": {
        "color": 7,
        "width": 392,
        "height": 657,
        "content": "## STEP 1: WEBHOOK TRIGGER\n\n**Expected Input Format:**\n```json\n{\n  \"name\": \"Customer Name\",\n  \"email\": \"email@example.com\",\n  \"rating\": 1-5,\n  \"comments\": \"Feedback text\",\n  \"suggestions\": \"Improvement ideas\"\n}\n```\n\n**Test URL:**\nGenerated when you execute this node in test mode.\n\n**Production URL:**\nActivate workflow to get permanent webhook endpoint.\n\n**Integration:**\nConnect Google Forms, Typeform, or any service that can send POST requests to this webhook."
      },
      "typeVersion": 1
    },
    {
      "id": "14577162-c367-452e-8fcc-0d444ea3ce14",
      "name": "Webhook - Receive Feedback1",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -6032,
        -368
      ],
      "parameters": {
        "path": "feedback-submission",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "4457ca3c-f600-407e-aa74-562cbb6ffe4d",
      "name": "Sticky Note - Clean Data1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5744,
        -656
      ],
      "parameters": {
        "color": 7,
        "width": 392,
        "height": 449,
        "content": "## STEP 2: DATA CLEANING\n\n**Data Transformations:**\n- Empty name \u2192 \"Anonymous\"\n- Invalid email \u2192 empty string + flag false\n- Missing rating \u2192 0\n- Missing comments \u2192 \"No comments provided\"\n- Missing suggestions \u2192 \"No suggestions provided\"\n\n**Output:**\nClean, structured JSON ready for AI analysis with guaranteed data consistency."
      },
      "typeVersion": 1
    },
    {
      "id": "525aaa53-9a12-4379-8baa-47c91cabc443",
      "name": "Clean & Normalize Data1",
      "type": "n8n-nodes-base.code",
      "position": [
        -5664,
        -368
      ],
      "parameters": {
        "jsCode": "// Get input data\nconst items = $input.all();\nconst cleanedItems = [];\n\nfor (const item of items) {\n  const data = item.json;\n  \n  // Access the body object where the actual form data is\n  const formData = data.body || data;\n  \n  // Normalize and clean data\n  const cleaned = {\n    name: formData.name?.trim() || 'Anonymous',\n    email: formData.email?.trim().toLowerCase() || '',\n    rating: parseInt(formData.rating) || 0,\n    comments: formData.comments?.trim() || 'No comments provided',\n    suggestions: formData.suggestions?.trim() || 'No suggestions provided',\n    timestamp: new Date().toISOString(),\n    submissionId: `FB-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`\n  };\n  \n  // Validate email format\n  const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n  if (cleaned.email && !emailRegex.test(cleaned.email)) {\n    cleaned.email = '';\n    cleaned.hasValidEmail = false;\n  } else {\n    cleaned.hasValidEmail = true;\n  }\n  \n  cleanedItems.push({ json: cleaned });\n}\n\nreturn cleanedItems;"
      },
      "typeVersion": 2
    },
    {
      "id": "bee6365c-320f-426d-b90d-d421e15e2c4a",
      "name": "Sticky Note - AI Analysis1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5328,
        -816
      ],
      "parameters": {
        "color": 7,
        "width": 408,
        "height": 609,
        "content": "## STEP 3: AI ANALYSIS\n\n**AI Instructions:**\nAnalyzes feedback and returns JSON with:\n1. **sentiment**: \"Positive\" | \"Neutral\" | \"Negative\"\n2. **highlights**: Array of 2-3 key points\n3. **suggestions**: Array of actionable recommendations\n4. **summary**: 2-3 sentence executive summary\n\n**Input Data:**\n- Customer name\n- Rating (1-5)\n- Comments\n- Suggestions\n\n**Output:**\nJSON response in `message.content` field containing structured analysis.\n\n**Tip:** Use GPT-4 for more nuanced analysis if budget allows."
      },
      "typeVersion": 1
    },
    {
      "id": "7359e419-7366-4629-b78d-9b3e6eb8e963",
      "name": "Generate AI Summary1",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        -5168,
        -368
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-3.5-turbo",
          "cachedResultName": "GPT-3.5-TURBO"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "content": "=Analyze this feedback:\n\nName: {{ $json.name }}\nRating: {{ $json.rating }}/5\nComments: {{ $json.comments }}\nSuggestions: {{ $json.suggestions }}"
            },
            {
              "role": "system",
              "content": "=You are a feedback analysis assistant. You MUST respond with ONLY valid JSON, no other text.\n\nAnalyze the feedback and return this exact structure:\n{\n  \"sentiment\": \"Positive\" or \"Neutral\" or \"Negative\",\n  \"highlights\": [\"highlight 1\", \"highlight 2\", \"highlight 3\"],\n  \"suggestions\": [\"suggestion 1\", \"suggestion 2\"],\n  \"summary\": \"2-3 sentence summary\"\n}\n\nDo not include any text outside the JSON structure. Respond with valid JSON only."
            }
          ]
        }
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "176eab7c-6456-459b-b295-1b4952562509",
      "name": "Sticky Note - Parse Response1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4896,
        -816
      ],
      "parameters": {
        "color": 7,
        "width": 360,
        "height": 593,
        "content": "## STEP 4: PARSE AI RESPONSE\n\n**Key Operations:**\n1. **Extracts** AI response from `message.content`\n2. **Cleans** markdown code blocks if present\n3. **Parses** JSON string to object\n4. **Validates** required fields (sentiment, highlights, suggestions, summary)\n5. **Maps** sentiment to color codes:\n   - Positive \u2192 Green (#10b981)\n   - Neutral \u2192 Orange (#f59e0b)\n   - Negative \u2192 Red (#ef4444)\n6. **Ensures** arrays are properly formatted\n7. **Merges** AI analysis with original feedback data\n\n\n**Output:**\nComplete feedback object with AI analysis ready for HTML generation."
      },
      "typeVersion": 1
    },
    {
      "id": "c4deea1d-d92a-4afa-9cb1-ff35adb28393",
      "name": "Parse AI Response1",
      "type": "n8n-nodes-base.code",
      "position": [
        -4736,
        -368
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst outputItems = [];\n\nfor (let i = 0; i < items.length; i++) {\n  const feedbackData = $node[\"Clean & Normalize Data1\"].json;\n  const aiResponse = items[i].json.message.content;\n  \n  let aiAnalysis;\n  \n  try {\n    // Clean the response - remove any markdown code blocks if present\n    let cleanedResponse = aiResponse.trim();\n    \n    // Remove markdown code block markers if they exist\n    cleanedResponse = cleanedResponse.replace(/```json\\n?/g, '');\n    cleanedResponse = cleanedResponse.replace(/```\\n?/g, '');\n    cleanedResponse = cleanedResponse.trim();\n    \n    // Parse the JSON\n    aiAnalysis = JSON.parse(cleanedResponse);\n    \n    // Validate that we have all required fields\n    if (!aiAnalysis.sentiment || !aiAnalysis.highlights || !aiAnalysis.suggestions || !aiAnalysis.summary) {\n      throw new Error(\"Missing required fields in AI response\");\n    }\n    \n    console.log(\"Successfully parsed AI response as JSON\");\n    \n  } catch (e) {\n    console.log(\"Failed to parse as JSON, using fallback:\", e.message);\n    \n    // Fallback: Extract data intelligently from text\n    let sentiment = \"Neutral\";\n    const lowerResponse = aiResponse.toLowerCase();\n    \n    if (lowerResponse.includes(\"positive\") || \n        lowerResponse.includes(\"great\") || \n        lowerResponse.includes(\"excellent\")) {\n      sentiment = \"Positive\";\n    } else if (lowerResponse.includes(\"negative\") || \n               lowerResponse.includes(\"poor\") || \n               lowerResponse.includes(\"disappointed\")) {\n      sentiment = \"Negative\";\n    }\n    \n    // Create fallback structure\n    aiAnalysis = {\n      sentiment: sentiment,\n      highlights: [\n        feedbackData.comments || \"Customer provided feedback\",\n        `Rating: ${feedbackData.rating}/5 stars`\n      ],\n      suggestions: [feedbackData.suggestions || \"No specific suggestions\"],\n      summary: aiResponse.substring(0, 300)\n    };\n  }\n  \n  // Sentiment color mapping\n  const sentimentColors = {\n    'Positive': '#10b981',\n    'Neutral': '#f59e0b',\n    'Negative': '#ef4444'\n  };\n  \n  // Build output object\n  outputItems.push({\n    json: {\n      ...feedbackData,\n      aiSummary: aiAnalysis.summary,\n      sentiment: aiAnalysis.sentiment,\n      sentimentColor: sentimentColors[aiAnalysis.sentiment] || '#6b7280',\n      highlights: Array.isArray(aiAnalysis.highlights) ? aiAnalysis.highlights : [aiAnalysis.highlights],\n      aiSuggestions: Array.isArray(aiAnalysis.suggestions) ? aiAnalysis.suggestions : [aiAnalysis.suggestions]\n    }\n  });\n}\n\nreturn outputItems;"
      },
      "typeVersion": 2
    },
    {
      "id": "6c726077-7bca-4b66-864d-79074e097c2b",
      "name": "Sticky Note - HTML Generation1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4512,
        -1008
      ],
      "parameters": {
        "color": 7,
        "width": 424,
        "height": 785,
        "content": "## STEP 5: BUILD HTML REPORT\n\n**HTML Components:**\n1. **Header Section**\n   - Gradient purple background\n   - \"\ud83d\udcca Feedback Report\" title\n   - Subtitle: \"AI-Powered Customer Feedback Analysis\"\n\n2. **User Information**\n   - Customer name (28px, bold)\n   - Email address\n   - Unique submission ID\n\n3. **Rating & Sentiment**\n   - Visual star rating (\u2605\u2605\u2605\u2605\u2606)\n   - Rating text (X out of 5 stars)\n   - Color-coded sentiment badge\n\n4. **AI Summary Section**\n   - Executive summary paragraph\n   - Easy-to-read format\n\n5. **Original Feedback**\n   - Comments box\n   - Suggestions box\n\n**Output:**\nComplete HTML string in `htmlContent` field ready for PDF conversion."
      },
      "typeVersion": 1
    },
    {
      "id": "35382397-13ee-46d0-9eaf-41aae3e4cce9",
      "name": "Build HTML Report1",
      "type": "n8n-nodes-base.code",
      "position": [
        -4320,
        -368
      ],
      "parameters": {
        "jsCode": "const data = $json;\n\n// Generate star rating HTML\nconst starRating = '\u2605'.repeat(data.rating) + '\u2606'.repeat(5 - data.rating);\n\n// Format highlights list\nconst highlightsList = data.highlights.map(h => `<li>${h}</li>`).join('');\n\n// Format AI suggestions list\nconst suggestionsList = data.aiSuggestions.length > 0 \n  ? data.aiSuggestions.map(s => `<li>${s}</li>`).join('')\n  : '<li>No additional suggestions</li>';\n\n// Generate complete HTML document\nconst html = `\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>Feedback Report - ${data.name}</title>\n  <style>\n    * {\n      margin: 0;\n      padding: 0;\n      box-sizing: border-box;\n    }\n    \n    body {\n      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n      background: #f9fafb;\n      padding: 40px 20px;\n      line-height: 1.6;\n    }\n    \n    .container {\n      max-width: 800px;\n      margin: 0 auto;\n      background: white;\n      border-radius: 12px;\n      box-shadow: 0 4px 6px rgba(0, 0, 0, 0.07);\n      overflow: hidden;\n    }\n    \n    .header {\n      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n      color: white;\n      padding: 40px;\n      text-align: center;\n    }\n    \n    .header h1 {\n      font-size: 32px;\n      margin-bottom: 8px;\n      font-weight: 700;\n    }\n    \n    .header p {\n      opacity: 0.9;\n      font-size: 16px;\n    }\n    \n    .content {\n      padding: 40px;\n    }\n    \n    .user-info {\n      border-bottom: 2px solid #e5e7eb;\n      padding-bottom: 24px;\n      margin-bottom: 24px;\n    }\n    \n    .user-name {\n      font-size: 28px;\n      font-weight: 700;\n      color: #111827;\n      margin-bottom: 8px;\n    }\n    \n    .user-email {\n      color: #6b7280;\n      font-size: 15px;\n      margin-bottom: 8px;\n    }\n    \n    .submission-id {\n      color: #9ca3af;\n      font-size: 12px;\n      font-family: 'Courier New', monospace;\n    }\n    \n    .rating-section {\n      margin: 24px 0;\n      text-align: center;\n      padding: 24px;\n      background: #f9fafb;\n      border-radius: 8px;\n    }\n    \n    .stars {\n      font-size: 40px;\n      color: #fbbf24;\n      letter-spacing: 6px;\n      margin-bottom: 12px;\n    }\n    \n    .rating-text {\n      font-size: 18px;\n      color: #374151;\n      font-weight: 600;\n      margin-bottom: 12px;\n    }\n    \n    .sentiment-badge {\n      display: inline-block;\n      padding: 10px 20px;\n      border-radius: 24px;\n      font-weight: 600;\n      font-size: 15px;\n      color: white;\n      text-transform: uppercase;\n      letter-spacing: 0.5px;\n    }\n    \n    .section {\n      margin: 32px 0;\n    }\n    \n    .section-title {\n      font-size: 20px;\n      font-weight: 700;\n      color: #111827;\n      margin-bottom: 16px;\n      display: flex;\n      align-items: center;\n    }\n    \n    .section-title::before {\n      content: '';\n      width: 4px;\n      height: 28px;\n      background: #667eea;\n      margin-right: 12px;\n      border-radius: 2px;\n    }\n    \n    .section-content {\n      color: #374151;\n      line-height: 1.8;\n      font-size: 16px;\n    }\n    \n    .feedback-box {\n      background: #f9fafb;\n      padding: 24px;\n      border-radius: 8px;\n      border-left: 4px solid #667eea;\n      margin: 16px 0;\n    }\n    \n    .feedback-box strong {\n      color: #111827;\n      display: block;\n      margin-bottom: 8px;\n      font-size: 14px;\n      text-transform: uppercase;\n      letter-spacing: 0.5px;\n    }\n    \n    ul {\n      list-style: none;\n      padding-left: 0;\n    }\n    \n    ul li {\n      padding: 12px 0;\n      padding-left: 32px;\n      position: relative;\n      color: #374151;\n      line-height: 1.6;\n    }\n    \n    ul li::before {\n      content: '\u25cf';\n      color: #667eea;\n      font-weight: bold;\n      font-size: 24px;\n      position: absolute;\n      left: 0;\n      top: 8px;\n    }\n    \n    .footer {\n      background: #f9fafb;\n      padding: 32px 40px;\n      text-align: center;\n      color: #6b7280;\n      font-size: 14px;\n      border-top: 1px solid #e5e7eb;\n    }\n    \n    .footer p {\n      margin: 4px 0;\n    }\n    \n    .footer strong {\n      color: #111827;\n    }\n    \n    @media print {\n      body {\n        padding: 0;\n        background: white;\n      }\n      \n      .container {\n        box-shadow: none;\n      }\n    }\n  </style>\n</head>\n<body>\n  <div class=\"container\">\n    <div class=\"header\">\n      <h1>\ud83d\udcca Feedback Report</h1>\n      <p>AI-Powered Customer Feedback Analysis</p>\n    </div>\n    \n    <div class=\"content\">\n      <!-- User Information -->\n      <div class=\"user-info\">\n        <div class=\"user-name\">${data.name}</div>\n        <div class=\"user-email\">${data.email || 'No email provided'}</div>\n        <div class=\"submission-id\">Submission ID: ${data.submissionId}</div>\n      </div>\n      \n      <!-- Rating and Sentiment -->\n      <div class=\"rating-section\">\n        <div class=\"stars\">${starRating}</div>\n        <div class=\"rating-text\">${data.rating} out of 5 stars</div>\n        <div class=\"sentiment-badge\" style=\"background-color: ${data.sentimentColor};\">\n          ${data.sentiment} Feedback\n        </div>\n      </div>\n      \n      <!-- AI Summary -->\n      <div class=\"section\">\n        <div class=\"section-title\">AI Summary</div>\n        <div class=\"section-content\">${data.aiSummary}</div>\n      </div>\n      \n      <!-- Key Highlights -->\n      <div class=\"section\">\n        <div class=\"section-title\">Key Highlights</div>\n        <ul>${highlightsList}</ul>\n      </div>\n      \n      <!-- Original Feedback -->\n      <div class=\"section\">\n        <div class=\"section-title\">Original Feedback</div>\n        <div class=\"feedback-box\">\n          <strong>Comments</strong>\n          ${data.comments}\n        </div>\n        <div class=\"feedback-box\">\n          <strong>Suggestions</strong>\n          ${data.suggestions}\n        </div>\n      </div>\n      \n      <!-- AI Recommendations -->\n      ${data.aiSuggestions.length > 0 ? `\n      <div class=\"section\">\n        <div class=\"section-title\">Recommended Actions</div>\n        <ul>${suggestionsList}</ul>\n      </div>\n      ` : ''}\n    </div>\n    \n    <div class=\"footer\">\n      <p><strong>Report Generated:</strong> ${new Date(data.timestamp).toLocaleString('en-US', { \n        weekday: 'long', \n        year: 'numeric', \n        month: 'long', \n        day: 'numeric',\n        hour: '2-digit',\n        minute: '2-digit'\n      })}</p>\n      <p style=\"margin-top: 16px;\">Thank you for your valuable feedback! \ud83d\ude4f</p>\n    </div>\n  </div>\n</body>\n</html>\n`;\n\n// Return the HTML content along with all data\nreturn [{\n  json: {\n    ...data,\n    htmlContent: html\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "06215d6b-d83c-4ef6-9560-6bad2005f079",
      "name": "Sticky Note - PDF Generation1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4064,
        -784
      ],
      "parameters": {
        "color": 7,
        "width": 376,
        "height": 561,
        "content": "## STEP 6: GENERATE PDF\n\n**API Response:**\n```json\n{\n  \"success\": true,\n  \"pdf_url\": \"https://generated-image.s3.ap-south-1.amazonaws.com/pdfs/[uuid].pdf\",\n  \"file_size_bytes\": 136164,\n  \"file_deletion_date\": \"2025-11-09\",\n  \"source_type\": \"html\"\n}\n```\n\n**Output:**\n- PDF URL (hosted on AWS S3)\n- File size in bytes\n- Expiration date (30 days from generation)\n- Success status"
      },
      "typeVersion": 1
    },
    {
      "id": "d10efd0f-df5c-43f1-b744-4287a0d32a02",
      "name": "Sticky Note - Process PDF1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3664,
        -688
      ],
      "parameters": {
        "color": 7,
        "width": 392,
        "height": 465,
        "content": "## STEP 7: MERGE PDF METADATA\n\n**Key Operations:**\n1. **Retrieves** previous feedback data from \"Build HTML Report\" node\n2. **Extracts** PDF metadata from current node:\n   - pdf_url: Direct link to generated PDF\n   - file_size_bytes: PDF file size\n   - file_deletion_date: When PDF expires (30 days)\n3. **Generates** descriptive filename: `Feedback-Report-[SubmissionID].pdf`\n4. **Adds** `pdfReady: true` flag for downstream nodes\n5. **Merges** all data into single JSON object\n"
      },
      "typeVersion": 1
    },
    {
      "id": "ede437b3-bbcc-4ac4-8848-d92d7a351cf8",
      "name": "Process PDF Response1",
      "type": "n8n-nodes-base.code",
      "position": [
        -3552,
        -368
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst previousData = $node[\"Build HTML Report1\"].json;\nconst pdfInfo = items[0].json; // This is the output from HTML to PDF node\n\nreturn [{\n  json: {\n    ...previousData,\n    pdfUrl: pdfInfo.pdf_url,\n    pdfFileSize: pdfInfo.file_size_bytes,\n    pdfDeletionDate: pdfInfo.file_deletion_date,\n    pdfFileName: `Feedback-Report-${previousData.submissionId}.pdf`,\n    pdfReady: true\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "8f4b800d-8889-46c8-8fb0-234d4728f6b4",
      "name": "Sticky Note - Email Validation1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3232,
        -672
      ],
      "parameters": {
        "color": 7,
        "width": 392,
        "height": 434,
        "content": "## STEP 8: EMAIL VALIDATION CHECK\n\n**Condition:**\n- Checks: `hasValidEmail` boolean flag\n- Operation: Equals `true`\n\n**\ud83d\udfe2 TRUE Branch (Valid Email):**\n- Routes to \"Email User Report\" node\n- Sends personalized email with PDF link\n- User receives thank you message\n- Then proceeds to logging\n"
      },
      "typeVersion": 1
    },
    {
      "id": "171bdb6d-4c98-49d9-9928-0f7c11a0eb57",
      "name": "Check Valid Email1",
      "type": "n8n-nodes-base.if",
      "position": [
        -3136,
        -368
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1668e9a2-47cc-4a7b-8dd2-29de8771f4c5",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.hasValidEmail }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "70c0fcc0-cb4b-4ffc-8a5a-bab1f0e71c3e",
      "name": "Email User Report1",
      "type": "n8n-nodes-base.gmail",
      "position": [
        -2688,
        -384
      ],
      "parameters": {
        "sendTo": "={{ $json.email }}",
        "message": "=<div style=\"font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;\">\n  <div style=\"text-align: center; margin-bottom: 32px;\">\n    <h1 style=\"color: #667eea; margin: 0; font-size: 28px;\">Thank You for Your Feedback! \ud83c\udf89</h1>\n  </div>\n  \n  <p style=\"font-size: 16px; line-height: 1.6;\">Hi <strong>{{ $json.name }}</strong>,</p>\n  \n  <p style=\"font-size: 16px; line-height: 1.6;\">Thank you for taking the time to share your thoughts with us. Your feedback is invaluable in helping us improve our services.</p>\n  \n  <div style=\"background: linear-gradient(135deg, #667eea15 0%, #764ba215 100%); padding: 24px; border-radius: 12px; margin: 32px 0; border-left: 4px solid {{ $json.sentimentColor }};\">\n    <p style=\"margin: 0; font-size: 16px;\"><strong>\ud83d\udcca Your Rating:</strong>{{ $json.rating }}/5</p>\n    <p style=\"margin: 12px 0 0 0; font-size: 16px;\"><strong>\ud83d\udcad Sentiment:</strong> <span style=\"color: {{ $json.sentimentColor }}; font-weight: 700;\">{{ $json.sentiment }}</span></p>\n  </div>\n  \n  <div style=\"background: #f9fafb; padding: 20px; border-radius: 8px; margin: 24px 0;\">\n    <p style=\"margin: 0 0 12px 0; font-weight: 600; color: #111827;\">\ud83d\udcc4 Your Personalized Feedback Report</p>\n    <p style=\"margin: 0 0 16px 0; font-size: 14px; line-height: 1.6; color: #6b7280;\">We've prepared a detailed AI-powered analysis of your feedback including key highlights, sentiment analysis, and recommended actions.</p>\n    <a href=\"{{ $json.pdfUrl }}\" style=\"display: inline-block; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 14px 28px; border-radius: 8px; text-decoration: none; font-weight: 600; font-size: 16px;\">\ud83d\udce5 Download Your Report</a>\n    <p style=\"margin: 12px 0 0 0; font-size: 12px; color: #9ca3af;\">\u23f0 Report available until: {{ $json.pdfDeletionDate }}</p>\n  </div>\n  \n  <div style=\"background: white; border: 1px solid #e5e7eb; padding: 20px; border-radius: 8px; margin: 24px 0;\">\n    <p style=\"margin: 0 0 8px 0; font-weight: 600; color: #111827;\">\u2728 Your Report Includes:</p>\n    <ul style=\"margin: 0; padding-left: 20px; line-height: 1.8; color: #374151;\">\n      <li>AI-generated summary of your feedback</li>\n      <li>Key highlights and main points</li>\n      <li>Sentiment analysis with visual indicators</li>\n      <li>Actionable recommendations based on your input</li>\n      <li>Complete feedback history</li>\n    </ul>\n  </div>\n  \n  <p style=\"font-size: 16px; line-height: 1.6;\">If you have any questions or would like to discuss your feedback further, please don't hesitate to reach out. We're here to help!</p>\n  \n  <div style=\"margin-top: 40px; padding-top: 24px; border-top: 2px solid #e5e7eb;\">\n    <p style=\"margin: 0; font-size: 16px;\">Warm regards,</p>\n    <p style=\"margin: 4px 0 0 0; font-size: 18px; font-weight: 700; color: #667eea;\">The Team</p>\n  </div>\n  \n  <div style=\"margin-top: 32px; padding: 20px; background: #f9fafb; border-radius: 8px; text-align: center;\">\n    <p style=\"margin: 0; font-size: 12px; color: #6b7280;\">Submission ID: <code style=\"background: #e5e7eb; padding: 2px 8px; border-radius: 4px; font-family: monospace;\">{{ $json.submissionId }}</code></p>\n    <p style=\"margin: 8px 0 0 0; font-size: 12px; color: #9ca3af;\">Generated on {{ new Date($json.timestamp).toLocaleString() }}</p>\n  </div>\n</div>",
        "options": {},
        "subject": "Your Feedback Summary Report \u2013 Thank You for Sharing!"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "f9b4ca97-21e0-4a4f-9b42-43e1390a87c8",
      "name": "Sticky Note - Log Data1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2480,
        -768
      ],
      "parameters": {
        "color": 7,
        "width": 424,
        "height": 546,
        "content": "## STEP 10: LOG TO GOOGLE SHEETS\n\n\n**Columns Logged:**\n1. **Submission ID** - Unique identifier\n2. **Timestamp** - ISO 8601 date/time\n3. **Name** - Customer name\n4. **Email** - Customer email\n5. **Rating** - 1-5 star rating\n6. **Sentiment** - Positive/Neutral/Negative\n7. **Comments** - Original feedback text\n8. **Suggestions** - Customer suggestions\n9. **AI Summary** - Generated summary\n10. **PDF URL** - Link to report\n11. **PDF Available Until** - Expiration date\n12. **Email Sent** - Yes/No tracking"
      },
      "typeVersion": 1
    },
    {
      "id": "1d12b1e5-a8cb-436a-8a7f-4e093faaf42b",
      "name": "Log Feedback Data1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -2336,
        -384
      ],
      "parameters": {
        "columns": {
          "value": {
            "Name": "={{ $('Check Valid Email1').item.json.name }}",
            "Email": "={{ $('Check Valid Email1').item.json.email }}",
            "Rating": "={{ $('Check Valid Email1').item.json.rating }}",
            "PDF URL": "={{ $('Check Valid Email1').item.json.pdfUrl }}",
            "Comments": "={{ $('Check Valid Email1').item.json.comments }}",
            "Sentiment": "={{ $('Check Valid Email1').item.json.sentiment }}",
            "Timestamp": "={{ $('Check Valid Email1').item.json.timestamp }}",
            "AI Summary": "={{ $('Check Valid Email1').item.json.aiSummary }}",
            "Email Sent": "={{ $('Check Valid Email1').item.json.hasValidEmail ? 'Yes' : 'No' }}",
            "Suggestions": "={{ $('Check Valid Email1').item.json.suggestions }}",
            "Submission ID": "={{ $('Check Valid Email1').item.json.submissionId }}",
            "PDF Available Until": "={{ $('Check Valid Email1').item.json.pdfDeletionDate }}"
          },
          "schema": [
            {
              "id": "Submission ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Submission ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Email",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Rating",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Rating",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Sentiment",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Sentiment",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Comments",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Comments",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Suggestions",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Suggestions",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "AI Summary",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "AI Summary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "PDF Available Until",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "PDF Available Until",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "PDF URL",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "PDF URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Email Sent",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Email Sent",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Submission ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID",
          "cachedResultUrl": "",
          "cachedResultName": "Feedback Log"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "c0c1f555-140d-448f-ba78-399971e374a6",
      "name": "Sticky Note - Team Alert1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2032,
        -800
      ],
      "parameters": {
        "color": 7,
        "width": 520,
        "height": 578,
        "content": "## STEP 11: SLACK TEAM NOTIFICATION\n\n**Message Contents:**\n1. **Header** - \"\ud83c\udd95 New Feedback Received!\"\n2. **Customer Info**\n   - Name\n   - Email address\n3. **Feedback Metrics**\n   - Star rating (X/5 \u2b50)\n   - Sentiment (Positive/Neutral/Negative)\n4. **AI Summary** - Quick overview\n5. **Key Highlights** - Top 2 points from AI analysis\n6. **Recommended Actions** - AI suggestions\n7. **PDF Link** - Direct link to full report\n8. **Footer** - Submission ID & confirmation of logging\n\n**Alternative:** Can use Teams, Discord, or email notifications instead."
      },
      "typeVersion": 1
    },
    {
      "id": "6deba6a0-a406-4865-8254-694ab11bd1e0",
      "name": "Notify Team1",
      "type": "n8n-nodes-base.slack",
      "position": [
        -1920,
        -384
      ],
      "parameters": {
        "text": "=\ud83c\udd95 *New Feedback Received!*\n\n*Customer:* {{ $json.Name }}\n*Email:* {{ $json.Email }}\n*Rating:* {{ $json.Rating }}/5 \u2b50\n*Sentiment:* {{ $json.Sentiment }}\n\n*AI Summary:*\n{{ $json['AI Summary'] }}\n\n*Key Highlights:*\n- {{ $('Check Valid Email1').item.json.highlights[0] }}\n- {{ $('Check Valid Email1').item.json.highlights[1] }}\n\n*Recommended Actions:*\n- {{ $('Check Valid Email1').item.json.aiSuggestions[0] }}\n- {{ $('Check Valid Email1').item.json.aiSuggestions[1] }}\n\n\ud83d\udcc4 *View Full Report:* {{ $json['PDF URL'] }}\n\n_Submission ID: {{ $json['Submission ID'] }}_\n_\u2705 Logged to tracking sheet_",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_SLACK_CHANNEL_ID",
          "cachedResultName": "feedback-notifications"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "a569c084-e6ac-4ca0-8060-f952fa7e0589",
      "name": "Sticky Note - Final Response1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1472,
        -864
      ],
      "parameters": {
        "color": 7,
        "width": 568,
        "height": 642,
        "content": "## STEP 12: WEBHOOK RESPONSE\n\n**Response Structure:**\n```json\n{\n  \"success\": true,\n  \"message\": \"Thank you for your feedback! We've sent you a detailed report via email.\",\n  \"data\": {\n    \"submissionId\": \"FB-xxx\",\n    \"name\": \"Customer Name\",\n    \"email\": \"customer@email.com\",\n    \"rating\": 4,\n    \"sentiment\": \"Positive\",\n    \"emailSent\": true,\n    \"reportUrl\": \"https://...\",\n    \"reportAvailableUntil\": \"2025-11-09\"\n  }\n}\n```\n\n**Use Case:**\nIf integrating with custom forms, this response can trigger:\n- Success confirmation page\n- Download PDF button\n- Redirect to thank you page\n- Track submission in analytics\n"
      },
      "typeVersion": 1
    },
    {
      "id": "3b59493a-e441-49ac-9627-9dd157254c3f",
      "name": "Send Success Response1",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -1184,
        -384
      ],
      "parameters": {
        "options": {
          "responseCode": 200
        },
        "respondWith": "json",
        "responseBody": "={\n  \"success\": true,\n  \"message\": \"Thank you for your feedback! We've sent you a detailed report via email.\",\n  \"data\": {\n    \"submissionId\": \"={{ $('Log Feedback Data1').item.json['Submission ID'] }}\",\n    \"name\": \"={{ $('Log Feedback Data1').item.json.Name }}\",\n    \"email\": \"={{ $('Log Feedback Data1').item.json.Email }}\",\n    \"rating\": \"={{ $('Log Feedback Data1').item.json.Rating }}\",\n    \"sentiment\": \"={{ $('Log Feedback Data1').item.json.Sentiment }}\",\n    \"emailSent\": \"={{ $('Check Valid Email1').item.json.hasValidEmail }}\",\n    \"reportUrl\": \"={{ $('Check Valid Email1').item.json.pdfUrl }}\",\n    \"reportAvailableUntil\": \"={{ $('Check Valid Email1').item.json.pdfDeletionDate }}\"\n  } \n}\n"
      },
      "typeVersion": 1.1
    },
    {
      "id": "0fc941d8-3ae3-4334-aaad-456b75177c35",
      "name": "Sticky Note - Testing Guide1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -6768,
        -880
      ],
      "parameters": {
        "color": 7,
        "width": 549,
        "height": 600,
        "content": "## \ud83d\ude80 TESTING YOUR WORKFLOW\n\n**Step-by-Step Testing Guide:**\n\n**1. Initial Setup**\n- \u2705 Verify all credentials are configured\n- \u2705 Check Google Sheet has correct columns\n- \u2705 Confirm Slack bot is in channel\n- \u2705 Test webhook URL is active\n\n**2. Test Data**\nUse this JSON in Postman/curl:\n```json\n{\n  \"name\": \"Sarah Johnson\",\n  \"email\": \"your-email@example.com\",\n  \"rating\": 4,\n  \"comments\": \"Great product! Delivery was slow but customer service was helpful.\",\n  \"suggestions\": \"Improve shipping speed and tracking updates.\"\n}\n```\n\n**3. Execution Steps**\n- Execute \"Webhook\" node to get test URL\n- Send POST request with test data\n- Watch nodes execute sequentially\n- Check for green checkmarks on all nodes\n"
      },
      "typeVersion": 1
    },
    {
      "id": "749a54d2-50ac-40ab-b3a0-792e9fdc4387",
      "name": "HTML to PDF1",
      "type": "n8n-nodes-htmlcsstopdf.htmlcsstopdf",
      "position": [
        -3936,
        -368
      ],
      "parameters": {
        "html_content": "={{ $json.htmlContent }}"
      },
      "credentials": {
        "htmlcsstopdfApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "f210d49f-ebe2-4e23-823e-e29412a330e8",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2800,
        -528
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 304,
        "content": "## STEP 9:EMAIL USER REPORT\n\nSends an email user report to the email id of the user"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "",
  "connections": {
    "HTML to PDF1": {
      "main": [
        [
          {
            "node": "Process PDF Response1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notify Team1": {
      "main": [
        [
          {
            "node": "Send Success Response1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build HTML Report1": {
      "main": [
        [
          {
            "node": "HTML to PDF1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Valid Email1": {
      "main": [
        [
          {
            "node": "Email User Report1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email User Report1": {
      "main": [
        [
          {
            "node": "Log Feedback Data1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Feedback Data1": {
      "main": [
        [
          {
            "node": "Notify Team1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse AI Response1": {
      "main": [
        [
          {
            "node": "Build HTML Report1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate AI Summary1": {
      "main": [
        [
          {
            "node": "Parse AI Response1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process PDF Response1": {
      "main": [
        [
          {
            "node": "Check Valid Email1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clean & Normalize Data1": {
      "main": [
        [
          {
            "node": "Generate AI Summary1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook - Receive Feedback1": {
      "main": [
        [
          {
            "node": "Clean & Normalize Data1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Transform customer feedback into actionable insights automatically with AI analysis, professional PDF reports, personalized emails, and real-time team notifications. Overview Features Demo Prerequisites Quick Start Configuration Usage Troubleshooting License

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

More AI & RAG workflows → · Browse all categories →

Related workflows

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

AI & RAG

Imagine your recruitment process transformed into a sleek, efficient, AI-powered assembly line for talent. That's exactly what this system creates. It automates the heavy lifting, allowing your human

Google Sheets, OpenAI, Gmail +2
AI & RAG

Transform your webinar registrations from basic form submissions into a verified, personalized, and premium attendee experience.

N8N Nodes Verifiemail, Slack, Stop And Error +4
AI & RAG

This workflow automates the initial screening process for new job applications, freeing up your recruitment team to focus on qualified candidates. It receives applications from a webhook, uses OpenAI

HTTP Request, OpenAI, Google Sheets +2
AI & RAG

This system meticulously guides each lead through a fully automated journey, from initial contact to a personalized follow-up and CRM integration.

OpenAI, @Elevenlabs/N8N Nodes Elevenlabs, Google Drive +4
AI & RAG

Analyze website SEO issues and generate optimization actions with AI

HTTP Request, OpenAI, Google Sheets +2