This workflow corresponds to n8n.io template #10896 — we link there as the canonical source.
This workflow follows the Gmail → Gmail Trigger 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 →
{
"id": "CPsX6vfZu43t79t4",
"name": "Automate Feedback-to-Backlog",
"tags": [
{
"id": "5iQR5Cw05Bzz7Aqt",
"name": "Google",
"createdAt": "2025-08-02T17:36:32.751Z",
"updatedAt": "2025-08-02T17:36:32.751Z"
},
{
"id": "DW3QCDhQZ9ZpSunz",
"name": "Bot",
"createdAt": "2025-08-02T17:37:06.011Z",
"updatedAt": "2025-08-02T17:37:06.011Z"
},
{
"id": "IUTR4oS3KHYPxOqH",
"name": "Jira",
"createdAt": "2025-11-14T09:45:17.977Z",
"updatedAt": "2025-11-14T09:45:17.977Z"
},
{
"id": "PlBq4GrfRsEhR6mN",
"name": "google form",
"createdAt": "2025-08-14T09:57:38.641Z",
"updatedAt": "2025-08-14T09:57:38.641Z"
},
{
"id": "S94PaeZJiPKn5Ei9",
"name": "gmail",
"createdAt": "2025-08-14T09:57:18.678Z",
"updatedAt": "2025-08-14T09:57:18.678Z"
},
{
"id": "erKAjTLp0jWx08QX",
"name": "notions",
"createdAt": "2025-11-16T23:07:16.962Z",
"updatedAt": "2025-11-16T23:07:16.962Z"
},
{
"id": "h2TBJOs7feXDIfbL",
"name": "google sheet",
"createdAt": "2025-08-14T09:57:22.041Z",
"updatedAt": "2025-08-14T09:57:22.041Z"
},
{
"id": "rYuINsb3Y1XjrgNv",
"name": "Productivity",
"createdAt": "2025-08-02T17:36:49.812Z",
"updatedAt": "2025-08-02T17:36:49.812Z"
},
{
"id": "yfpCFhbNZGRtYKc0",
"name": "Telegram",
"createdAt": "2025-08-02T17:36:35.592Z",
"updatedAt": "2025-08-02T17:36:35.592Z"
}
],
"nodes": [
{
"id": "dc69068a-911d-4fbc-b613-d550a4ba637a",
"name": "google form trigger",
"type": "n8n-nodes-base.googleSheetsTrigger",
"position": [
736,
304
],
"parameters": {
"event": "rowAdded",
"options": {},
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": 1438845877,
"cachedResultUrl": "https://docs.google.com/spreadsheets",
"cachedResultName": "R\u00e9ponses au formulaire 1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "yourdocumentid",
"cachedResultUrl": "https://docs.google.com/spreadsheets",
"cachedResultName": "feedback (r\u00e9ponses)"
}
},
"typeVersion": 1
},
{
"id": "27892223-b214-4537-99cd-cbd21395ff15",
"name": "Gmail Trigger",
"type": "n8n-nodes-base.gmailTrigger",
"position": [
736,
496
],
"parameters": {
"filters": {
"sender": "user@example.com",
"labelIds": [
"Label_3151595203057118485"
]
},
"pollTimes": {
"item": [
{
"mode": "everyHour"
}
]
}
},
"typeVersion": 1.3
},
{
"id": "412a97e2-e756-4bbe-bd89-9382fea15acb",
"name": "data normalizer",
"type": "n8n-nodes-base.code",
"position": [
1008,
304
],
"parameters": {
"jsCode": "let source = 'unknown';\n\nif ($json.message && $json.message.chat && $json.message.from) {\n source = 'telegram';\n} else if ($json.threadId && $json.id && $json.from && $json.subject) {\n source = 'gmail';\n} else if ($json['Your Email'] && $json[\"What's your feedback?\"]) {\n source = 'google_forms';\n}\n\nlet normalized = {};\n\nif (source === 'telegram') {\n const msg = $json.message;\n normalized = {\n id: `tg_${msg.from.id}_${Date.now()}`,\n source: 'telegram',\n timestamp: new Date().toISOString(),\n user: {\n telegram_id: msg.from.id,\n name: msg.from.first_name + (msg.from.last_name ? ' ' + msg.from.last_name : ''),\n username: msg.from.username || null,\n email: null, \n tier: 'free', \n },\n content: {\n raw_text: msg.text,\n subject: null,\n },\n metadata: {\n chat_id: msg.chat.id,\n message_id: msg.message_id,\n language: msg.from.language_code || 'en',\n },\n };\n}\nelse if (source === 'gmail') {\n normalized = {\n id: `email_${$json.id}`,\n source: 'gmail',\n timestamp: $json.receivedAt,\n user: {\n email: $json.from,\n name: $json.from.split('@')[0],\n tier: 'free', \n },\n content: {\n raw_text: $json.textPlain || $json.textHtml,\n subject: $json.subject,\n },\n metadata: {\n message_id: $json.id,\n thread_id: $json.threadId,\n },\n };\n}\nelse if (source === 'google_forms') {\n normalized = {\n id: `form_${Date.now()}`,\n source: 'google_forms',\n timestamp: $json.Timestamp,\n user: {\n email: $json['Your Email'],\n name: $json['Your Email'].split('@')[0],\n tier: $json['User Tier'] || 'free',\n },\n content: {\n raw_text: $json[\"What's your feedback?\"],\n subject: $json['Category'],\n category_hint: $json['Category'], \n urgency_hint: $json['How urgent?'], \n },\n metadata: {\n form_response: true,\n },\n };\n} else {\n \n normalized = {\n id: `unknown_${Date.now()}`,\n source,\n raw: $json,\n };\n}\n\n\nreturn [\n {\n json: normalized,\n },\n];\n"
},
"typeVersion": 2
},
{
"id": "ba9e2e00-38c4-402d-9189-042ec8e425f1",
"name": "instant reply",
"type": "n8n-nodes-base.telegram",
"position": [
1216,
304
],
"parameters": {
"text": "=\u2705 Feedback received \ud83d\udd04 Analysis in progress \u23f1\ufe0f Estimated time 2 minutes",
"chatId": "000000000",
"additionalFields": {
"parse_mode": "MarkdownV2",
"disable_notification": false,
"disable_web_page_preview": false
}
},
"typeVersion": 1.2
},
{
"id": "bcde33d7-c039-4855-84e8-8edff8836e8a",
"name": "user enrichment",
"type": "n8n-nodes-base.code",
"position": [
1424,
304
],
"parameters": {
"jsCode": "const tierMapping = {\n 'user@example.com': 'enterprise',\n 'user@example.com': 'enterprise',\n 'user@example.com': 'pro',\n};\n\nconst user = $json.user || {};\n\nconst email = user.email || null;\nlet tier = user.tier || 'free'; \n\nif (email && tierMapping[email]) {\n tier = tierMapping[email];\n}\n\nconst tierWeights = {\n enterprise: 2.0,\n pro: 1.5,\n free: 1.0,\n};\n\nconst safeTierKey = (tier || 'free').toLowerCase();\n\nreturn {\n ...$json,\n user: {\n ...user,\n tier,\n tier_weight: tierWeights[safeTierKey] || 1.0,\n },\n};\n"
},
"typeVersion": 2
},
{
"id": "63cb7b9a-2338-4e13-ba87-e2abfa70d22c",
"name": "priority calculator",
"type": "n8n-nodes-base.code",
"position": [
2288,
304
],
"parameters": {
"jsCode": "const ai = $json.ai_analysis;\nconst tier_weight = $json.user?.tier_weight || 1.0;\n\nconst pain = ai.pain_level;\nconst impact = ai.business_impact;\nconst effort = ai.implementation_effort;\n\nconst frequency = 10;\n\nconst weighted_impact = (pain * 0.4 + impact * 0.6) * tier_weight;\n\nconst rice_score = (weighted_impact * frequency) / effort;\n\nlet priority;\n\nif (rice_score >= 15 || (ai.category === 'bug' && pain >= 8)) {\n priority = 'P0';\n} else if (rice_score >= 10) {\n priority = 'P1';\n} else if (rice_score >= 5) {\n priority = 'P2';\n} else {\n priority = 'P3';\n}\n\n\nconst jiraPriorityNameMap = {\n P0: 'Highest',\n P1: 'High',\n P2: 'Medium',\n P3: 'Low', \n};\n\nconst jira_priority = jiraPriorityNameMap[priority] || 'Low';\n\n\nconst jiraPriorityIdMap = {\n Highest: '1',\n High: '2',\n Medium: '3',\n Low: '4',\n Lowest: '5',\n};\n\nconst jira_priority_id = jiraPriorityIdMap[jira_priority] || jiraPriorityIdMap['Low'];\n\nreturn {\n ...$json,\n scoring: {\n rice_score: Math.round(rice_score * 10) / 10,\n weighted_impact: Math.round(weighted_impact * 10) / 10,\n frequency,\n priority, \n jira_priority, \n jira_priority_id, \n components: {\n pain,\n business_impact: impact,\n implementation_effort: effort,\n tier_weight,\n },\n },\n};\n"
},
"typeVersion": 2
},
{
"id": "9b46d82b-efbf-45a8-bc4b-92fe546800a1",
"name": "Create an issue",
"type": "n8n-nodes-base.jira",
"position": [
2512,
304
],
"parameters": {
"project": {
"__rl": true,
"mode": "list",
"value": "10002",
"cachedResultName": "test2"
},
"summary": "=[{{ $json.scoring.priority }}] {{ $json.ai_analysis.summary }}",
"issueType": {
"__rl": true,
"mode": "list",
"value": "10014",
"cachedResultName": "Story"
},
"additionalFields": {
"priority": {
"__rl": true,
"mode": "id",
"value": "={{ $json.scoring.jira_priority_id }}"
},
"description": "={{ $('parse json').item.json.ai_analysis.description }}"
}
},
"typeVersion": 1
},
{
"id": "caff0f41-fdd9-4895-8939-c9489d4a468d",
"name": "send result to user",
"type": "n8n-nodes-base.telegram",
"position": [
3136,
176
],
"parameters": {
"text": "=\ud83c\udf89 *Feedback treated !*\n\n\ud83c\udfab *Ticket created :* [{{ $json.jira.key }}]({{ $json.jira.url }})\n\n\ud83d\udcca *Analysis :*\n- Category : {{ $('priority calculator').item.json.ai_analysis.category }}\n- Priority : {{ $('priority calculator').item.json.scoring.priority }}\n- Score RICE :{{ $('priority calculator').item.json.scoring.rice_score }}\n\n\ud83c\udff7\ufe0f *Tags :* {{ $('priority calculator').item.json.ai_analysis.tags }}\n\n\n\nThanks ! \ud83d\ude4f\n",
"chatId": "0000000",
"additionalFields": {}
},
"typeVersion": 1.2
},
{
"id": "14fdc3e7-f458-4c3f-aecb-27dfb1867cc8",
"name": "log to analytics",
"type": "n8n-nodes-base.googleSheets",
"position": [
3328,
320
],
"parameters": {
"columns": {
"value": {
"Tags": "={{ $('priority calculator').item.json.ai_analysis.tags }}",
"Source": "={{ $('data normalizer').item.json.source }}",
"Status": "in_jira",
"Category": "={{ $('priority calculator').item.json.ai_analysis.category }}",
"Jira URL": "={{ $json.jira.url }}",
"Priority": "={{ $('priority calculator').item.json.scoring.priority }}",
"Sentiment": "={{ $('priority calculator').item.json.ai_analysis.sentiment }}",
"Timestamp": "={{ $now.toFormat('yyyy-LL-dd HH:mm:ss') }}",
"User Name": "={{ $('google form trigger').item.json[\"What are your feedback about ?\"] }}",
"AI Summary": "={{ $('priority calculator').item.json.ai_analysis.summary }}",
"Pain Level": "={{ $('priority calculator').item.json.scoring.components.pain }}",
"RICE Score": "={{ $('priority calculator').item.json.scoring.rice_score }}",
"User Email": "={{ $('data normalizer').item.json.raw.From }}",
"Feedback ID": "={{ $json.id }}",
"Jira Ticket": "={{ $json.jira.key }}",
"Business Impact": "={{ $('priority calculator').item.json.scoring.components.business_impact }}",
"Implementation Effort": "={{ $('priority calculator').item.json.scoring.components.implementation_effort }}"
},
"schema": [
{
"id": "Timestamp",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Timestamp",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Feedback ID",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Feedback ID",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Source",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Source",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "User Email",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "User Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "User Name",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "User Name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Category",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Category",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Tags",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Tags",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Sentiment",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Sentiment",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Pain Level",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Pain Level",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Business Impact",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Business Impact",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Implementation Effort",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Implementation Effort",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "RICE Score",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "RICE Score",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Priority",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Priority",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "AI Summary",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "AI Summary",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Jira Ticket",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Jira Ticket",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Jira URL",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Jira URL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Status",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Status",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets",
"cachedResultName": "Feuille 1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "yourdocumentid",
"cachedResultUrl": "https://docs.google.com/spreadsheets",
"cachedResultName": "users feedback"
}
},
"typeVersion": 4.7
},
{
"id": "70cd5a1c-b68d-4810-8ce0-6a8c8494e20d",
"name": "end notification",
"type": "n8n-nodes-base.gmail",
"position": [
3552,
320
],
"parameters": {
"sendTo": "user@example.com",
"message": "=Hello, \n\nwe would like to thank you for your feedback.\nYour feedback has been treated.\n\nThanks for using our products\nThe team",
"options": {},
"subject": "Your feedback"
},
"typeVersion": 2.1
},
{
"id": "7fa7aeb3-d306-4233-9174-3938ab5a8f2b",
"name": "parse json",
"type": "n8n-nodes-base.code",
"position": [
2096,
304
],
"parameters": {
"jsCode": "const items = $input.all().map(item => {\n let aiResponseText = item.json.message?.content || '';\n\n aiResponseText = aiResponseText\n .replace(/```json\\s*/gi, '')\n .replace(/```\\s*/g, '')\n .trim();\n\n let aiAnalysis;\n\n try {\n aiAnalysis = JSON.parse(aiResponseText);\n } catch (error) {\n console.error('JSON Parse Error:', error);\n console.error('Raw response:', aiResponseText);\n\n aiAnalysis = {\n category: 'question',\n tags: ['needs-review'],\n sentiment: 'neutral',\n pain_level: 5,\n business_impact: 5,\n implementation_effort: 5,\n summary: 'Parse error - manual review needed',\n description: aiResponseText.substring(0, 200),\n acceptance_criteria: ['Manual review required'],\n };\n }\n\n return {\n json: {\n ...item.json, \n ai_analysis: aiAnalysis,\n },\n };\n});\n\nreturn items;\n"
},
"typeVersion": 2
},
{
"id": "1a84a28c-db1c-448d-8dd2-005b923496f1",
"name": "Create a page",
"type": "n8n-nodes-base.notion",
"position": [
3760,
320
],
"parameters": {
"title": "=Feedback {{ $now.toFormat('dd/MM/yyyy HH:mm:ss') }}\n",
"pageId": {
"__rl": true,
"mode": "url",
"value": "https://www.notion.yoururl"
},
"simple": false,
"blockUi": {
"blockValues": [
{
"textContent": "={{ $('Message a model').item.json.message.content }}"
},
{
"textContent": "={{ $('Create an issue').item.json.self }}{{ $('Create an issue').item.json.key }}"
},
{
"textContent": "={{ $('parse json').item.json.ai_analysis.category }}{{ $('parse json').item.json.ai_analysis.tags }}"
},
{
"textContent": "={{ $('parse json').item.json.ai_analysis.summary }}\n\n{{ $('parse json').item.json.ai_analysis.description }}\n\n{{ $('parse json').item.json.ai_analysis.acceptance_criteria }}"
}
]
},
"options": {}
},
"typeVersion": 2.2
},
{
"id": "e98e99da-4257-48bc-95b9-3bb16002d98a",
"name": "Message a model1",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
1616,
960
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-5",
"cachedResultName": "GPT-5"
},
"options": {},
"messages": {
"values": [
{
"content": "You are a Senior Product Manager analyzing monthly user feedback data.\nGenerate strategic insights, identify trends, and provide actionable recommendations.\n\nReturn ONLY valid JSON (no markdown):\n{\n \"executive_summary\": \"2-3 sentence critical takeaway\",\n \"key_insights\": [\n \"Insight 1 (data-driven, actionable)\",\n \"Insight 2\",\n \"Insight 3\",\n \"Insight 4\",\n \"Insight 5\"\n ],\n \"trends\": {\n \"positive\": [\"Positive trend 1\", \"Positive trend 2\"],\n \"concerning\": [\"Concern 1\", \"Concern 2\"]\n },\n \"strategic_recommendations\": [\n {\n \"title\": \"Recommendation title\",\n \"rationale\": \"Why this matters (with data)\",\n \"priority\": \"High|Medium|Low\",\n \"estimated_impact\": \"Expected business impact\"\n }\n ],\n \"risk_areas\": [\n {\n \"area\": \"Risk description\",\n \"severity\": \"High|Medium|Low\",\n \"mitigation\": \"Suggested action\"\n }\n ]\n}\n\n"
},
{
"content": "=Analyze this monthly feedback data for {{ $now.toFormat('LLLL yyyy') }}:\n\nOVERVIEW:\n- Total feedbacks: {{ $json.stats.total }}\n- Avg RICE Score: {{ $json.stats.avg_rice_score }}\n- P0/P1 rate: {{ $json.stats.p0_p1_rate }}\n- Sentiment score: {{ $json.stats.sentiment_score }}/10\n\nBY CATEGORY:\n{{ Object.entries($json.stats.by_category).map(([k,v]) => `- ${k}: ${v}`).join('\\n') }}\n\nBY USER TIER:\n{{ Object.entries($json.stats.by_tier).map(([k,v]) => `- ${k}: ${v}`).join('\\n') }}\n\nTOP PAIN POINTS (by RICE):\n{{ $json.stats.top_pain_points.slice(0, 5).map((p, i) => `${i+1}. ${p.summary} (RICE: ${p.rice}, Pain: ${p.pain})`).join('\\n') }}\n\nTOP FEATURE REQUESTS:\n{{ $json.stats.top_feature_requests.slice(0, 5).map((f, i) => `${i+1}. ${f.summary} (RICE: ${f.rice})`).join('\\n') }}\n\nGenerate strategic insights and recommendations."
}
]
},
"simplify": false,
"jsonOutput": true
},
"typeVersion": 1.8
},
{
"id": "c3ea0b0b-93d4-4bfb-b1d0-e4056ae55cde",
"name": "monthly report",
"type": "n8n-nodes-base.notion",
"position": [
2640,
960
],
"parameters": {
"title": "=Montly report",
"pageId": {
"__rl": true,
"mode": "url",
"value": "https://www.notion.so/yoururl"
},
"blockUi": {
"blockValues": [
{
"textContent": "={{ $json.choices[0].message.content.executive_summary }}\n"
}
]
},
"options": {}
},
"typeVersion": 2.2
},
{
"id": "18ce849f-45c1-4554-a467-4c0bd561edd6",
"name": "Telegram Trigger",
"type": "n8n-nodes-base.telegramTrigger",
"position": [
736,
144
],
"parameters": {
"updates": [
"message"
],
"additionalFields": {}
},
"typeVersion": 1.2,
"alwaysOutputData": false
},
{
"id": "7c294a16-6b04-4548-a6a2-867c36514903",
"name": "post processing",
"type": "n8n-nodes-base.code",
"position": [
2720,
304
],
"parameters": {
"jsCode": "const jiraResponse = $json;\nconst originalData = $input.first().json;\n\nreturn {\n ...originalData,\n jira: {\n key: jiraResponse.key,\n id: jiraResponse.id,\n url: `https://yourspace.atlassian.net/browse/${jiraResponse.key}`,\n self: jiraResponse.self\n }\n};"
},
"typeVersion": 2
},
{
"id": "cbc5c39c-0751-47c1-ad97-595f610a4522",
"name": "If",
"type": "n8n-nodes-base.if",
"position": [
2928,
304
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "e52332bb-ea6a-4fc3-9fd2-0cee6ddcd969",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $('data normalizer').item.json.source }}",
"rightValue": "telegram"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "e7aa9fab-61bd-4fe5-a577-1f80a4e6efde",
"name": "query google sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
1024,
960
],
"parameters": {
"options": {
"dataLocationOnSheet": {
"values": {
"range": "A1:S1000",
"rangeDefinition": "specifyRangeA1"
}
}
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets",
"cachedResultName": "Feuille 1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "yourdocumentid",
"cachedResultUrl": "https://docs.google.com/spreadsheets",
"cachedResultName": "users feedback"
}
},
"typeVersion": 4.7
},
{
"id": "e853f0f4-2ea0-47fd-9e7e-d03b7d9c5575",
"name": "1 month Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
720,
960
],
"parameters": {
"rule": {
"interval": [
{
"daysInterval": 30,
"triggerAtHour": 17
}
]
}
},
"typeVersion": 1.2
},
{
"id": "03417a8a-514d-4c2c-8de0-1d5bab4418da",
"name": "aggregate monthly stats",
"type": "n8n-nodes-base.code",
"position": [
1360,
960
],
"parameters": {
"jsCode": "const allFeedbacks = $input.all().map(item => item.json);\n\nconst now = new Date();\nconst monthStart = new Date(now.getFullYear(), now.getMonth(), 1);\nconst monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0);\n\nconst monthName = monthStart.toLocaleString('default', { \n month: 'long', \n year: 'numeric' \n});\n\nconst monthlyFeedbacks = allFeedbacks.filter(fb => {\n if (!fb || !fb.Timestamp) {\n return false;\n }\n\n \n const safeTimestamp = String(fb.Timestamp).replace(\" \", \"T\");\n const fbDate = new Date(safeTimestamp);\n\n if (isNaN(fbDate)) {\n return false;\n }\n\n return fbDate >= monthStart && fbDate <= monthEnd;\n});\n\nif (monthlyFeedbacks.length === 0) {\n const summary = `Monthly feedback report \u2013 ${monthName}\\n\\nNo feedbacks found for this period.`;\n\n return {\n month: monthName,\n stats: {\n month: monthName,\n date_range: {\n start: monthStart.toISOString().split('T')[0],\n end: monthEnd.toISOString().split('T')[0],\n },\n total: 0,\n by_category: {},\n by_sentiment: {},\n by_priority: {},\n by_source: {},\n by_tier: {},\n avg_rice_score: 0,\n avg_pain_level: 0,\n avg_business_impact: 0,\n p0_p1_count: 0,\n p0_p1_rate: '0%',\n top_pain_points: [],\n top_feature_requests: [],\n bug_patterns: {},\n sentiment_score: 0,\n top_category: 'none',\n note: 'No feedbacks found for this period',\n },\n raw_feedbacks: [],\n summary_for_notion: summary.slice(0, 1900),\n };\n}\n\nconst stats = {\n month: monthName,\n date_range: {\n start: monthStart.toISOString().split('T')[0],\n end: monthEnd.toISOString().split('T')[0]\n },\n total: monthlyFeedbacks.length,\n by_category: {},\n by_sentiment: {},\n by_priority: {},\n by_source: {},\n by_tier: {},\n avg_rice_score: 0,\n avg_pain_level: 0,\n avg_business_impact: 0,\n p0_p1_count: 0,\n top_pain_points: [],\n top_feature_requests: [],\n bug_patterns: {}\n};\n\nlet totalRice = 0, totalPain = 0, totalImpact = 0;\n\nmonthlyFeedbacks.forEach(fb => {\n const cat = fb.Category || 'unknown';\n stats.by_category[cat] = (stats.by_category[cat] || 0) + 1;\n \n const sent = fb.Sentiment || 'neutral';\n stats.by_sentiment[sent] = (stats.by_sentiment[sent] || 0) + 1;\n \n const prio = fb.Priority || 'P3';\n stats.by_priority[prio] = (stats.by_priority[prio] || 0) + 1;\n \n const src = fb.Source || 'unknown';\n stats.by_source[src] = (stats.by_source[src] || 0) + 1;\n \n const tier = fb['User Tier'] || 'free';\n stats.by_tier[tier] = (stats.by_tier[tier] || 0) + 1;\n \n const rice = parseFloat(fb['RICE Score']) || 0;\n const pain = parseFloat(fb['Pain Level']) || 0;\n const impact = parseFloat(fb['Business Impact']) || 0;\n \n totalRice += rice;\n totalPain += pain;\n totalImpact += impact;\n \n if (prio === 'P0' || prio === 'P1') {\n stats.p0_p1_count++;\n }\n \n if (pain >= 8) {\n stats.top_pain_points.push({\n summary: fb['AI Summary'],\n pain,\n rice,\n tier,\n jira: fb['Jira Ticket']\n });\n }\n \n if (cat === 'feature_request') {\n stats.top_feature_requests.push({\n summary: fb['AI Summary'],\n rice,\n tier,\n jira: fb['Jira Ticket']\n });\n }\n \n if (cat === 'bug') {\n const tags = (fb.Tags || '').split(',').map(t => t.trim());\n tags.forEach(tag => {\n if (tag) {\n stats.bug_patterns[tag] = (stats.bug_patterns[tag] || 0) + 1;\n }\n });\n }\n});\n\nstats.avg_rice_score = (totalRice / monthlyFeedbacks.length).toFixed(2);\nstats.avg_pain_level = (totalPain / monthlyFeedbacks.length).toFixed(1);\nstats.avg_business_impact = (totalImpact / monthlyFeedbacks.length).toFixed(1);\nstats.p0_p1_rate = ((stats.p0_p1_count / monthlyFeedbacks.length) * 100).toFixed(1) + '%';\n\nstats.top_pain_points.sort((a, b) => b.rice - a.rice);\nstats.top_pain_points = stats.top_pain_points.slice(0, 10);\n\nstats.top_feature_requests.sort((a, b) => b.rice - a.rice);\nstats.top_feature_requests = stats.top_feature_requests.slice(0, 10);\n\nconst sentimentWeights = { \n positive: 10, \n neutral: 5, \n negative: 0 \n};\n\nlet sentimentSum = 0;\nObject.keys(stats.by_sentiment).forEach(s => {\n const weight = sentimentWeights[s] || 5;\n sentimentSum += stats.by_sentiment[s] * weight;\n});\nstats.sentiment_score = (sentimentSum / monthlyFeedbacks.length).toFixed(1);\n\nconst topCategory = Object.entries(stats.by_category)\n .sort((a, b) => b[1] - a[1])[0];\n\nstats.top_category = topCategory ? \n `${topCategory[0]} (${topCategory[1]})` : 'none';\n\nconst summaryLines = [\n `Monthly feedback report \u2013 ${monthName}`,\n ``,\n `\u2022 Total feedbacks: ${stats.total}`,\n `\u2022 Top category: ${stats.top_category}`,\n `\u2022 Avg RICE score: ${stats.avg_rice_score}`,\n `\u2022 Avg pain level: ${stats.avg_pain_level}`,\n `\u2022 Avg business impact: ${stats.avg_business_impact}`,\n `\u2022 P0/P1 ratio: ${stats.p0_p1_rate}`,\n];\n\nconst summaryText = summaryLines.join('\\n');\nconst notionSummary = summaryText.slice(0, 1900);\n\nreturn {\n month: monthName,\n stats,\n raw_feedbacks: monthlyFeedbacks.slice(0, 50),\n summary_for_notion: notionSummary,\n};\n"
},
"typeVersion": 2
},
{
"id": "b8bd6bed-4b92-40c6-835c-1d199d24e660",
"name": "parse response",
"type": "n8n-nodes-base.code",
"position": [
2096,
960
],
"parameters": {
"jsCode": "let aiText = $json.choices?.[0]?.message?.content;\n\nif (!aiText) {\n throw new Error(\"No AI content found in OpenAI response.\");\n}\n\nif (typeof aiText === 'object') {\n return {\n ...$input.first().json,\n ai_insights: aiText\n };\n}\n\naiText = String(aiText);\n\naiText = aiText\n .replace(/```json/gi, '')\n .replace(/```/g, '')\n .trim();\n\nlet insights;\ntry {\n insights = JSON.parse(aiText);\n} catch (e) {\n throw new Error(\"Failed to parse AI JSON: \" + aiText);\n}\n\nreturn {\n ...$input.first().json,\n ai_insights: insights\n};\n"
},
"typeVersion": 2
},
{
"id": "b318e791-fac0-4b18-8254-3f8d16b58b36",
"name": "montly report log",
"type": "n8n-nodes-base.googleSheets",
"position": [
3136,
960
],
"parameters": {
"columns": {
"value": {
"Month": "={{ $('aggregate monthly stats').item.json.stats.month }}",
"P0/P1 Count": "={{ $('aggregate monthly stats').item.json.stats.p0_p1_count }}",
"Generated At": "={{ $now.toISO() }}",
"Top Category": "={{ $('aggregate monthly stats').item.json.stats.top_category }}",
"Avg RICE Score": "={{ $('aggregate monthly stats').item.json.stats.avg_rice_score }}",
"Sentiment Score": "={{ $('aggregate monthly stats').item.json.stats.sentiment_score }}",
"Total Feedbacks": "={{ $('aggregate monthly stats').item.json.stats.total }}",
"Executive Summary": "={{ $('parse response').item.json.choices[0].message.content.executive_summary }}",
"Notion Report URL": "={{ $json.url }}"
},
"schema": [
{
"id": "Month",
"type": "string",
"display": true,
"required": false,
"displayName": "Month",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Total Feedbacks",
"type": "string",
"display": true,
"required": false,
"displayName": "Total Feedbacks",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Avg RICE Score",
"type": "string",
"display": true,
"required": false,
"displayName": "Avg RICE Score",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "P0/P1 Count",
"type": "string",
"display": true,
"required": false,
"displayName": "P0/P1 Count",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Top Category",
"type": "string",
"display": true,
"required": false,
"displayName": "Top Category",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Sentiment Score",
"type": "string",
"display": true,
"required": false,
"displayName": "Sentiment Score",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Executive Summary",
"type": "string",
"display": true,
"required": false,
"displayName": "Executive Summary",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Notion Report URL",
"type": "string",
"display": true,
"required": false,
"displayName": "Notion Report URL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Generated At",
"type": "string",
"display": true,
"required": false,
"displayName": "Generated At",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets",
"cachedResultName": "Feuille 1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1xqb63TDzt6I5XKxMan84M1XTHrI2PkT3J-knt_BSMGs",
"cachedResultUrl": "https://docs.google.com/spreadsheets",
"cachedResultName": "Monthly report"
}
},
"typeVersion": 4.7
},
{
"id": "a0997a78-e13f-4bcc-a7fd-13223166132e",
"name": "Message a model",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
1808,
304
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-4o-mini",
"cachedResultName": "GPT-4O-MINI"
},
"options": {},
"messages": {
"values": [
{
"content": "You are a Product Manager analyzing user feedback.\nYour task is to categorize, tag, score and summarize feedback.\n\nReturn ONLY valid JSON with this exact structure (no markdown, no backticks):\n{\n \"category\": \"bug|feature_request|improvement|question\",\n \"tags\": [\"tag1\", \"tag2\", \"tag3\"],\n \"sentiment\": \"positive|neutral|negative\",\n \"pain_level\": 1-10,\n \"business_impact\": 1-10,\n \"implementation_effort\": 1-10,\n \"summary\": \"One sentence, max 80 chars\",\n \"description\": \"2-3 sentences for Jira ticket\",\n \"acceptance_criteria\": [\"criterion1\", \"criterion2\", \"criterion3\"]\n}\n\nRules:\n- Tags: max 3, specific and lowercase\n- Summary: actionable, clear, <80 chars\n- Scores: realistic based on content\n- Description: detailed enough for dev team\n- ONLY return the JSON, nothing else"
},
{
"content": "=Analyze this user feedback:\n\nFEEDBACK: \"{{$json.content.raw_text}}\"\nUSER TIER: {{$json.user.tier}}\nSOURCE: {{$json.source}}\n\nUSER SELECTED CATEGORY: {{ $json.content.category_hint || 'none' }}\nUSER URGENCY ASSESSMENT: {{ $json.content.urgency_hint || 'none' }}\n\nReturn the JSON analysis.\n"
}
]
}
},
"typeVersion": 1.8
},
{
"id": "05030e93-1a04-490e-9029-4d66d888d55e",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
432,
0
],
"parameters": {
"color": 7,
"width": 528,
"height": 656,
"content": "## 1) Trigger\n3 possible triggers when a message is received:\n- Telegram\n- Google Sheet\n- Gmail"
},
"typeVersion": 1
},
{
"id": "aa170b87-f7a7-4858-9055-bdc647ad250a",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
976,
0
],
"parameters": {
"color": 7,
"width": 1072,
"height": 656,
"content": "## 2) Requests treated and enriched\n\nThe request is analysed and enriched with the AI model. A Telegram message is sent to the requester to confirm."
},
"typeVersion": 1
},
{
"id": "fc87d894-1efa-46ff-9d64-ee2b16345d2f",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
2064,
0
],
"parameters": {
"color": 7,
"width": 1904,
"height": 656,
"content": "## 3) Priority calculation and Jira ticket creation\n\nThe priority is calculated and a ticket is created in Jira. If the request comes from Telegram, another notification will be sent.\nA log is done into a sheet for the reporting and a confirmation email is sent and a page is created on Notions"
},
"typeVersion": 1
},
{
"id": "82a664f3-ef6f-448d-81ef-ed1fb19cc7bd",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
432,
768
],
"parameters": {
"color": 7,
"width": 3536,
"height": 352,
"content": "## 4) Monthly reporting\n\nMonthly trigger.\nThe data are gathered then a report will be built by AI model. \nA page is created on Notions, the Google sheet updated.\nThe report is sent to the stakeholders by email"
},
"typeVersion": 1
},
{
"id": "c62ccc12-755c-454d-b727-dda5dce5e79a",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
0,
0
],
"parameters": {
"width": 400,
"height": 1120,
"content": "## How it works\n\nThis workflow consolidates product feedback coming from three different entry points: a Telegram bot, a Google Form/Sheet, and incoming emails. Each new piece of feedback is captured in real time, cleaned, and unified into a single format.\n\nOnce normalized, the workflow sends the text to an AI model that helps categorize and interpret the message: it assigns a feedback type, evaluates sentiment and urgency, highlights the user pain point, and generates a short summary. It also provides estimated impact and effort so the feedback can be prioritized consistently.\n\nAfter the enrichment step, the workflow calculates a custom priority score and automatically creates both a Jira ticket and a Notion page to keep the product documentation up to date.\n\n At the end of each month, a summary report is generated and emailed to stakeholders so they can follow trends and see what has been addressed.\n\n## Setup steps\n\n1) Configure the Webhook for your Google Form or connect your Google Sheet.\n\n2) Add your Google credentials (Sheets + Gmail).\n\n3) Add your Notion integration token and database ID.\n\n4) Connect your Telegram bot token and chat ID.\n\n5) Add your AI provider API key.\n\n6) Configure your Jira credentials and project key.\nAfter connecting all services, run a test submission to confirm each integration works as expected.\n\n@[youtube](q0Is11oU18Y)"
},
"typeVersion": 1
},
{
"id": "3b83d0c6-0a42-4dc0-befb-8fa9ca7f81f0",
"name": "Email reporting",
"type": "n8n-nodes-base.gmail",
"position": [
3744,
960
],
"parameters": {
"sendTo": "user@example.com",
"message": "=<html>\n<head>\n <style>\n body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }\n .header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; border-radius: 8px; text-align: center; }\n .summary { background: #f7fafc; padding: 20px; margin: 20px 0; border-left: 4px solid #667eea; border-radius: 4px; }\n .metrics { display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px; margin: 20px 0; }\n .metric-card { background: #fff; padding: 15px; border: 1px solid #e2e8f0; border-radius: 6px; }\n .metric-value { font-size: 32px; font-weight: bold; color: #667eea; }\n .metric-label { color: #718096; font-size: 14px; }\n .cta-button { display: inline-block; background: #667eea; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px; margin: 20px 0; }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <h1>\ud83d\udcca Product Intelligence Report</h1>\n <h2>{{ $json[\"Month\"] }}</h2>\n </div>\n\n <div class=\"summary\">\n <h3>\ud83c\udfaf Executive Summary</h3>\n <p><strong>{{ $json[\"Executive Summary\"] }}</strong></p>\n </div>\n\n <h2>\ud83d\udcc8 Key Metrics</h2>\n <div class=\"metrics\">\n <div class=\"metric-card\">\n <div class=\"metric-value\">{{ $json[\"Total Feedbacks\"] }}</div>\n <div class=\"metric-label\">Total Feedbacks</div>\n </div>\n <div class=\"metric-card\">\n <div class=\"metric-value\">{{ $json[\"Avg RICE Score\"] }}</div>\n <div class=\"metric-label\">Avg RICE Score</div>\n </div>\n <div class=\"metric-card\">\n <div class=\"metric-value\">{{ $json[\"P0/P1 Count\"] }}</div>\n <div class=\"metric-label\">P0/P1 Count</div>\n </div>\n <div class=\"metric-card\">\n <div class=\"metric-value\">{{ $json[\"Sentiment Score\"] }}/10</div>\n <div class=\"metric-label\">Sentiment Score</div>\n </div>\n </div>\n\n <center>\n <a href=\"{{ $json[\"Notion Report URL\"] }}\" class=\"cta-button\">\n \ud83d\udcca View Full Report in Notion\n </a>\n </center>\n\n <hr style=\"margin: 40px 0; border: none; border-top: 1px solid #e2e8f0;\">\n \n <p style=\"color: #718096; font-size: 14px; text-align: center;\">\n Generated automatically by Feedback-to-Backlog Intelligence System<br>\n {{ $json[\"Generated At\"] }}\n </p>\n</body>\n</html>\n",
"options": {},
"subject": "=\ud83d\udcca Monthly Product Intelligence Report - {{ $json.Month }}"
},
"typeVersion": 2.1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "add15e21-4274-40c6-97ed-7c3417e52e80",
"connections": {
"If": {
"main": [
[
{
"node": "send result to user",
"type": "main",
"index": 0
}
],
[
{
"node": "log to analytics",
"type": "main",
"index": 0
}
]
]
},
"parse json": {
"main": [
[
{
"node": "priority calculator",
"type": "main",
"index": 0
}
]
]
},
"Gmail Trigger": {
"main": [
[
{
"node": "data normalizer",
"type": "main",
"index": 0
}
]
]
},
"instant reply": {
"main": [
[
{
"node": "user enrichment",
"type": "main",
"index": 0
}
]
]
},
"monthly report": {
"main": [
[
{
"node": "montly report log",
"type": "main",
"index": 0
}
]
]
},
"parse response": {
"main": [
[
{
"node": "monthly report",
"type": "main",
"index": 0
}
]
]
},
"1 month Trigger": {
"main": [
[
{
"node": "query google sheet",
"type": "main",
"index": 0
}
]
]
},
"Create an issue": {
"main": [
[
{
"node": "post processing",
"type": "main",
"index": 0
}
]
]
},
"Message a model": {
"main": [
[
{
"node": "parse json",
"type": "main",
"index": 0
}
]
]
},
"data normalizer": {
"main": [
[
{
"node": "instant reply",
"type": "main",
"index": 0
}
]
]
},
"post processing": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
},
"user enrichment": {
"main": [
[
{
"node": "Message a model",
"type": "main",
"index": 0
}
]
]
},
"Message a model1": {
"main": [
[
{
"node": "parse response",
"type": "main",
"index": 0
}
]
]
},
"Telegram Trigger": {
"main": [
[
{
"node": "data normalizer",
"type": "main",
"index": 0
}
]
]
},
"end notification": {
"main": [
[
{
"node": "Create a page",
"type": "main",
"index": 0
}
]
]
},
"log to analytics": {
"main": [
[
{
"node": "end notification",
"type": "main",
"index": 0
}
]
]
},
"montly report log": {
"main": [
[
{
"node": "Email reporting",
"type": "main",
"index": 0
}
]
]
},
"query google sheet": {
"main": [
[
{
"node": "aggregate monthly stats",
"type": "main",
"index": 0
}
]
]
},
"google form trigger": {
"main": [
[
{
"node": "data normalizer",
"type": "main",
"index": 0
}
]
]
},
"priority calculator": {
"main": [
[
{
"node": "Create an issue",
"type": "main",
"index": 0
}
]
]
},
"send result to user": {
"main": [
[
{
"node": "log to analytics",
"type": "main",
"index": 0
}
]
]
},
"aggregate monthly stats": {
"main": [
[
{
"node": "Message a model1",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow turns scattered user feedback into a structured product backlog pipeline. It collects feedback from three channels (Telegram bot, Google Form/Sheets, and Gmail), normalizes it, and sends it to an AI model that: Classifies the feedback (bug, feature request,…
Source: https://n8n.io/workflows/10896/ — 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.
Xmind Sales Email v2. Uses gmailTrigger, notion, googleSheets, googleSheetsTrigger. Event-driven trigger; 37 nodes.
This template is perfect for Gumroad creators, solopreneurs, digital product sellers, and freelancers who want to track and thank customers automatically — without spending time on manual work.
Customer Relationship Management Final. Uses lmChatOpenAi, gmailTrigger, openAi, gmail. Event-driven trigger; 40 nodes.
Automatically triage Product UAT feedback using AI, route it to the right tools and teams, and close the feedback loop with testers, all in one workflow.
This n8n template demonstrates how to use AI to score the all Resumes by matching it with Job profile