{
  "nodes": [
    {
      "id": "27c5a163-c0e8-4a6e-8a92-46ef3c8858c5",
      "name": "Workflow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -464,
        -176
      ],
      "parameters": {
        "width": 540,
        "height": 820,
        "content": "## Customer feedback analyzer\n\nAutomatically analyze new Google Forms responses using Gemini AI for sentiment analysis, categorization, and actionable insights. Results are logged in Google Sheets, saved to a Notion database, and emailed as a weekly summary.\n\n### How it works\n1. A schedule trigger checks Google Sheets for new unprocessed feedback rows (linked to a Google Form).\n2. A code node filters responses that have not been analyzed yet.\n3. Gemini AI performs sentiment analysis (positive, neutral, negative), categorizes each response by topic, and extracts key themes.\n4. A code node parses the AI output and calculates aggregate statistics like average sentiment score.\n5. Results are written back to the Google Sheets feedback log with sentiment and category columns.\n6. A Notion database entry is created with weekly insight summaries for long-term tracking.\n7. A Gmail message delivers an HTML report with charts-ready data to your team.\n\n### Setup steps\n1. Create a Google Form and link it to a Google Sheet. Add columns: Processed, Sentiment, Category, Score.\n2. Add a **Google Sheets OAuth2** credential.\n3. Add a **Google Gemini API** credential.\n4. Add a **Notion API** credential and create a database with columns: Date, Total Responses, Positive %, Negative %, Top Themes.\n5. Add a **Gmail OAuth2** credential and set the report recipient.\n6. Activate the workflow and test with sample form responses.\n\n### Customization\n- Change the schedule interval in the trigger node (default: daily at 9 AM).\n- Edit the AI prompt to add your own categories or scoring criteria.\n- Adjust the email template in the report formatting code node."
      },
      "typeVersion": 1
    },
    {
      "id": "3d727898-8861-4866-8e57-f2ebd97ddd94",
      "name": "Collection section note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        96,
        -176
      ],
      "parameters": {
        "color": 7,
        "width": 500,
        "height": 380,
        "content": "## Data collection\nReads new form responses from Google Sheets and filters unprocessed rows."
      },
      "typeVersion": 1
    },
    {
      "id": "2f5950c9-a722-4929-ad86-9b4b7187f351",
      "name": "AI section note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        608,
        -176
      ],
      "parameters": {
        "color": 7,
        "width": 344,
        "height": 552,
        "content": "## AI analysis\nGemini analyzes each response for sentiment, category, and key themes."
      },
      "typeVersion": 1
    },
    {
      "id": "3e83b5be-0f6e-4861-a4d9-d5f3fa496214",
      "name": "Output section note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        976,
        -176
      ],
      "parameters": {
        "color": 7,
        "width": 788,
        "height": 556,
        "content": "## Reporting\nWrites results back to Sheets, logs insights in Notion, and emails the summary."
      },
      "typeVersion": 1
    },
    {
      "id": "7d6011fd-40c0-491b-ba28-4a51147ac23f",
      "name": "Daily feedback check",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        128,
        0
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "dcc02c60-9600-4bcd-a772-655ce3d937b5",
      "name": "Read feedback responses",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        304,
        0
      ],
      "parameters": {
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "fa7eb92a-48f6-4b06-b270-aeb2510e12cd",
      "name": "Filter unprocessed responses",
      "type": "n8n-nodes-base.code",
      "position": [
        464,
        0
      ],
      "parameters": {
        "jsCode": "// Filter unprocessed rows and batch them for AI analysis\nvar items = $input.all();\nvar unprocessed = [];\nfor (var i = 0; i < items.length; i++) {\n  var row = items[i].json;\n  if (!row.Processed || row.Processed === '' || row.Processed === 'no') {\n    unprocessed.push({\n      rowIndex: i + 2,\n      timestamp: row.Timestamp || '',\n      response: row.Feedback || row.Response || row['Your feedback'] || '',\n      rating: row.Rating || row.Score || ''\n    });\n  }\n}\nif (unprocessed.length === 0) {\n  return { json: { skip: true, message: 'No new responses to analyze' } };\n}\nvar feedbackText = unprocessed.map(function(r, idx) {\n  return 'Response ' + (idx + 1) + ' (row ' + r.rowIndex + '): ' + r.response + (r.rating ? ' [Rating: ' + r.rating + ']' : '');\n}).join('\\n');\nreturn { json: { skip: false, count: unprocessed.length, feedbackText: feedbackText, rows: unprocessed } };"
      },
      "typeVersion": 2
    },
    {
      "id": "91bd04ed-60f2-448e-8031-4a33616f78a0",
      "name": "Analyze feedback with Gemini",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        672,
        0
      ],
      "parameters": {
        "text": "=Analyze the following customer feedback responses. For each response, determine:\n1. Sentiment: positive, neutral, or negative\n2. Sentiment score: 1-10 (1=very negative, 10=very positive)\n3. Category: product, service, pricing, usability, support, or other\n4. Key theme: a short phrase summarizing the main point\n\nAlso provide an overall summary with:\n- Total responses analyzed\n- Sentiment distribution (count of positive/neutral/negative)\n- Top 3 recurring themes\n- One actionable recommendation\n\nReturn valid JSON with this structure:\n{\n  \"responses\": [{\"rowIndex\": number, \"sentiment\": string, \"score\": number, \"category\": string, \"theme\": string}],\n  \"summary\": {\"total\": number, \"positive\": number, \"neutral\": number, \"negative\": number, \"topThemes\": [string], \"recommendation\": string}\n}\n\nFeedback data:\n{{ $json.feedbackText }}",
        "promptType": "define"
      },
      "typeVersion": 1.4
    },
    {
      "id": "bd24942e-1d59-4a95-9f7f-ac9e64835737",
      "name": "Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        672,
        144
      ],
      "parameters": {
        "options": {
          "temperature": 0.3
        },
        "modelName": "models/gemini-2.0-flash"
      },
      "typeVersion": 1
    },
    {
      "id": "ea186a1a-6d32-458b-b715-911a6fd551c0",
      "name": "Parse results and build report",
      "type": "n8n-nodes-base.code",
      "position": [
        1120,
        0
      ],
      "parameters": {
        "jsCode": "// Parse AI response and prepare data for Sheets and email\nvar aiResponse = $input.first().json;\nvar text = aiResponse.text || aiResponse.response || JSON.stringify(aiResponse);\nvar parsed;\ntry {\n  var jsonMatch = text.match(/\\{[\\s\\S]*\\}/);\n  parsed = JSON.parse(jsonMatch ? jsonMatch[0] : text);\n} catch(e) {\n  parsed = { responses: [], summary: { total: 0, positive: 0, neutral: 0, negative: 0, topThemes: [], recommendation: 'Could not parse AI response' } };\n}\nvar responses = parsed.responses || [];\nvar summary = parsed.summary || {};\nvar rows = [];\nfor (var i = 0; i < responses.length; i++) {\n  var r = responses[i];\n  rows.push({\n    rowIndex: r.rowIndex || 0,\n    sentiment: r.sentiment || 'unknown',\n    score: r.score || 5,\n    category: r.category || 'other',\n    theme: r.theme || ''\n  });\n}\nvar positiveRate = summary.total > 0 ? Math.round((summary.positive / summary.total) * 100) : 0;\nvar negativeRate = summary.total > 0 ? Math.round((summary.negative / summary.total) * 100) : 0;\nvar emailBody = '<h2>Customer Feedback Analysis Report</h2>';\nemailBody += '<p><strong>Date:</strong> ' + new Date().toISOString().split('T')[0] + '</p>';\nemailBody += '<p><strong>Responses analyzed:</strong> ' + (summary.total || 0) + '</p>';\nemailBody += '<h3>Sentiment Distribution</h3>';\nemailBody += '<ul>';\nemailBody += '<li>Positive: ' + (summary.positive || 0) + ' (' + positiveRate + '%)</li>';\nemailBody += '<li>Neutral: ' + (summary.neutral || 0) + '</li>';\nemailBody += '<li>Negative: ' + (summary.negative || 0) + ' (' + negativeRate + '%)</li>';\nemailBody += '</ul>';\nemailBody += '<h3>Top Themes</h3><ul>';\nvar themes = summary.topThemes || [];\nfor (var t = 0; t < themes.length; t++) {\n  emailBody += '<li>' + themes[t] + '</li>';\n}\nemailBody += '</ul>';\nemailBody += '<h3>Recommendation</h3><p>' + (summary.recommendation || 'N/A') + '</p>';\nreturn { json: { rows: rows, summary: summary, positiveRate: positiveRate, negativeRate: negativeRate, emailBody: emailBody, topThemes: (themes).join(', '), recommendation: summary.recommendation || 'N/A', total: summary.total || 0 } };"
      },
      "typeVersion": 2
    },
    {
      "id": "305d9aa9-e043-412a-a8ef-ec5c807960ba",
      "name": "Update feedback sheet with results",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1344,
        0
      ],
      "parameters": {
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "46137cfe-cb9a-4851-911b-d2baae8e840b",
      "name": "Log insights to Notion",
      "type": "n8n-nodes-base.notion",
      "position": [
        1568,
        0
      ],
      "parameters": {
        "options": {},
        "resource": "databasePage",
        "databaseId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "propertiesUi": {
          "propertyValues": [
            {
              "key": "Date"
            },
            {
              "key": "Total Responses",
              "numberValue": "={{ $json.total }}"
            },
            {
              "key": "Positive Rate",
              "numberValue": "={{ $json.positiveRate }}"
            },
            {
              "key": "Top Themes"
            },
            {
              "key": "Recommendation"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "f3aa5cef-9c71-483a-9ed0-0d5f5bd0c00f",
      "name": "Email report to team",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1568,
        224
      ],
      "parameters": {
        "message": "={{ $json.emailBody }}",
        "options": {},
        "subject": "=Customer Feedback Report - {{ new Date().toISOString().split('T')[0] }}"
      },
      "typeVersion": 2.1
    }
  ],
  "connections": {
    "Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Analyze feedback with Gemini",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Daily feedback check": {
      "main": [
        [
          {
            "node": "Read feedback responses",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read feedback responses": {
      "main": [
        [
          {
            "node": "Filter unprocessed responses",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze feedback with Gemini": {
      "main": [
        [
          {
            "node": "Parse results and build report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter unprocessed responses": {
      "main": [
        [
          {
            "node": "Analyze feedback with Gemini",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse results and build report": {
      "main": [
        [
          {
            "node": "Update feedback sheet with results",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log insights to Notion",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update feedback sheet with results": {
      "main": [
        [
          {
            "node": "Email report to team",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}