This workflow corresponds to n8n.io template #13772 — we link there as the canonical source.
This workflow follows the Chainllm → Gmail 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 →
{
"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
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Product managers, customer success teams, and small business owners who collect feedback via Google Forms and want automated sentiment analysis without manual review. Ideal for teams processing 10-100+ responses weekly.
Source: https://n8n.io/workflows/13772/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Revenue operations teams, SaaS growth managers, and sales directors who need automated weekly insights from their Stripe payment data. Perfect for small to medium businesses tracking subscription reve
This n8n template demonstrates how to use AI to score the all Resumes by matching it with Job profile
Automate your lead generation and outreach process seamlessly using AI, Gmail, and Google Sheets—all within n8n. No complicated setup—just import, activate, and start reaching prospects with personali
Lead Generating Web Scraper & CRM Automation. Uses httpRequest, airtable, googleSheets, gmail. Scheduled trigger; 38 nodes.
N8N Financial Tracker Telegram Invoices to Notion with AI Summaries & Reports. Uses lmChatGoogleGemini, chainLlm, stickyNote, scheduleTrigger. Scheduled trigger; 28 nodes.