AutomationFlowsGeneral › Daily Manual Update from VOC Data

Daily Manual Update from VOC Data

Original n8n title: 파이프라인 ③ 매뉴얼 자동 업데이트

파이프라인 ③ 매뉴얼 자동 업데이트. Uses scheduleTrigger, httpRequest, noOp. Scheduled trigger; 10 nodes.

Cron / scheduled trigger★★★★☆ complexity10 nodesHTTP Request
General Trigger: Cron / scheduled Nodes: 10 Complexity: ★★★★☆ Added:

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
{
  "name": "\ud30c\uc774\ud504\ub77c\uc778 \u2462 \ub9e4\ub274\uc5bc \uc790\ub3d9 \uc5c5\ub370\uc774\ud2b8",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hour": 9
            }
          ]
        }
      },
      "id": "schedule-trigger",
      "name": "\ub9e4\uc77c \uc624\uc804 9\uc2dc",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.notion.com/v1/databases/{{ $env.NOTION_VOC_DB_ID }}/query",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "notionApi",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Notion-Version",
              "value": "2022-06-28"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"filter\": {\n    \"and\": [\n      {\n        \"property\": \"\uc218\uc9d1\uc77c\",\n        \"date\": {\n          \"past_week\": {}\n        }\n      },\n      {\n        \"property\": \"\ucc98\ub9ac\uc0c1\ud0dc\",\n        \"select\": {\n          \"equals\": \"\uc2e0\uaddc\"\n        }\n      }\n    ]\n  }\n}"
      },
      "id": "get-recent-voc",
      "name": "\ucd5c\uadfc 7\uc77c VOC \uc870\ud68c",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        220,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "// \uc9c8\ubb38 \uc720\ud615\ubcc4 \ud074\ub7ec\uc2a4\ud130\ub9c1 (\ud0a4\uc6cc\ub4dc \uae30\ubc18)\nconst vocData = $input.first().json;\n\nif (!vocData.results || vocData.results.length === 0) {\n  return [{ json: { has_patterns: false, message: '\ubd84\uc11d\ud560 VOC \uc5c6\uc74c' } }];\n}\n\n// \uc0c1\ud488\ubcc4, \uce74\ud14c\uace0\ub9ac\ubcc4 \uc9c8\ubb38 \uadf8\ub8f9\ud551\nconst patterns = {};\n\nfor (const item of vocData.results) {\n  const productName = item.properties['\uc0c1\ud488\uba85']?.rich_text?.[0]?.text?.content || 'Unknown';\n  const category = item.properties['\uce74\ud14c\uace0\ub9ac']?.select?.name || '\uae30\ud0c0';\n  const question = item.properties['\uc9c8\ubb38']?.rich_text?.[0]?.text?.content || '';\n  \n  const key = `${productName}::${category}`;\n  \n  if (!patterns[key]) {\n    patterns[key] = {\n      product_name: productName,\n      category: category,\n      questions: [],\n      count: 0\n    };\n  }\n  \n  patterns[key].questions.push(question);\n  patterns[key].count++;\n}\n\n// 3\ud68c \uc774\uc0c1 \ubc18\ubcf5\ub41c \ud328\ud134\ub9cc \ucd94\ucd9c\nconst repeatedPatterns = Object.values(patterns).filter(p => p.count >= 3);\n\nif (repeatedPatterns.length === 0) {\n  return [{ json: { has_patterns: false, message: '\ubc18\ubcf5 \ud328\ud134 \uc5c6\uc74c (\uc784\uacc4\uac12: 3\ud68c)' } }];\n}\n\nreturn [{\n  json: {\n    has_patterns: true,\n    patterns: repeatedPatterns,\n    analyzed_at: new Date().toISOString()\n  }\n}];"
      },
      "id": "cluster-questions",
      "name": "\uc9c8\ubb38 \uc720\ud615 \ud074\ub7ec\uc2a4\ud130\ub9c1",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        440,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "leftValue": "={{ $json.has_patterns }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "if-has-patterns",
      "name": "\ubc18\ubcf5 \ud328\ud134 \uc788\uc74c?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        660,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.notion.com/v1/databases/{{ $env.NOTION_MANUAL_DB_ID }}/query",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "notionApi",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Notion-Version",
              "value": "2022-06-28"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"filter\": {\n    \"property\": \"\uc0c1\ud488\uba85\",\n    \"title\": {\n      \"contains\": \"{{ $json.patterns[0].product_name }}\"\n    }\n  }\n}"
      },
      "id": "get-existing-manual",
      "name": "\uae30\uc874 \ub9e4\ub274\uc5bc \uc870\ud68c",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        880,
        -100
      ]
    },
    {
      "parameters": {
        "jsCode": "// \uae30\uc874 \ub9e4\ub274\uc5bc\uacfc \uc2e0\uaddc \uc9c8\ubb38 \ub370\uc774\ud130 \ubcd1\ud569\nconst patternsData = $('\ubc18\ubcf5 \ud328\ud134 \uc788\uc74c?').first().json;\nconst manualData = $input.first().json;\n\nlet existingFaq = '';\nif (manualData.results && manualData.results.length > 0) {\n  existingFaq = manualData.results[0].properties['FAQ']?.rich_text?.[0]?.text?.content || '';\n}\n\nreturn [{\n  json: {\n    product_name: patternsData.patterns[0].product_name,\n    category: patternsData.patterns[0].category,\n    existing_faq: existingFaq,\n    new_questions: patternsData.patterns[0].questions,\n    question_count: patternsData.patterns[0].count\n  }\n}];"
      },
      "id": "merge-for-gemini",
      "name": "Gemini \uc785\ub825 \uc900\ube44",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1100,
        -100
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"contents\": [{\n    \"parts\": [{\n      \"text\": \"\ub2f9\uc2e0\uc740 CS \ub9e4\ub274\uc5bc \uad00\ub9ac\uc790\uc785\ub2c8\ub2e4.\\n\\n[\uae30\uc874 \ub9e4\ub274\uc5bc FAQ]\\n{{ $json.existing_faq }}\\n\\n[\uc2e0\uaddc \uace0\uac1d \uc9c8\ubb38\ub4e4 - {{ $json.question_count }}\uac74 \ubc18\ubcf5]\\n{{ $json.new_questions.join('\\n') }}\\n\\n\uc704 \uc2e0\uaddc \uc9c8\ubb38\ub4e4\uc744 \ubd84\uc11d\ud558\uc5ec:\\n1. \uae30\uc874 \ub9e4\ub274\uc5bc\uc5d0\uc11c \ubd80\uc871\ud55c \ubd80\ubd84 \uc2dd\ubcc4\\n2. \ucd94\uac00\ud574\uc57c \ud560 FAQ \ud56d\ubaa9 \uc81c\uc548\\n3. \uc218\uc815\uc774 \ud544\uc694\ud55c \uae30\uc874 \ub2f5\ubcc0 \uac1c\uc120\uc548\\n\\n[\ucd9c\ub825 \ud615\uc2dd - JSON]\\n{\\n  \\\"analysis\\\": \\\"\ubd80\uc871\ud55c \ubd80\ubd84 \ubd84\uc11d\\\",\\n  \\\"new_faq\\\": [\\n    {\\\"q\\\": \\\"\ucd94\uac00 \uc9c8\ubb381\\\", \\\"a\\\": \\\"\uad8c\uc7a5 \ub2f5\ubcc01\\\"}\\n  ],\\n  \\\"improvements\\\": \\\"\uae30\uc874 \ub2f5\ubcc0 \uac1c\uc120 \uc81c\uc548\\\"\\n}\"\n    }]\n  }],\n  \"generationConfig\": {\n    \"temperature\": 0.7,\n    \"maxOutputTokens\": 2048\n  }\n}"
      },
      "id": "gemini-analyze",
      "name": "Gemini \ubcf4\uc644\uc548 \uc0dd\uc131",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1320,
        -100
      ]
    },
    {
      "parameters": {
        "jsCode": "// Gemini \uc751\ub2f5 \ud30c\uc2f1\nconst response = $input.first().json;\nconst inputData = $('Gemini \uc785\ub825 \uc900\ube44').first().json;\n\nlet suggestion = {};\ntry {\n  const text = response.candidates[0].content.parts[0].text;\n  const jsonMatch = text.match(/\\{[\\s\\S]*\\}/);\n  if (jsonMatch) {\n    suggestion = JSON.parse(jsonMatch[0]);\n  }\n} catch (e) {\n  suggestion = {\n    analysis: '\ud30c\uc2f1 \uc2e4\ud328',\n    new_faq: [],\n    improvements: ''\n  };\n}\n\nreturn [{\n  json: {\n    product_name: inputData.product_name,\n    category: inputData.category,\n    ...suggestion,\n    generated_at: new Date().toISOString(),\n    status: '\uac80\ud1a0\ub300\uae30'\n  }\n}];"
      },
      "id": "parse-suggestion",
      "name": "\ubcf4\uc644\uc548 \ud30c\uc2f1",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1540,
        -100
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $env.JANDI_WEBHOOK_URL }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"body\": \"[\ub9e4\ub274\uc5bc \uc5c5\ub370\uc774\ud2b8] \uac80\ud1a0 \uc694\uccad\",\n  \"connectColor\": \"#FAC11B\",\n  \"connectInfo\": [\n    {\n      \"title\": \"\uc0c1\ud488\",\n      \"description\": \"{{ $json.product_name }}\"\n    },\n    {\n      \"title\": \"\ubc18\ubcf5 \uc9c8\ubb38 \uc720\ud615\",\n      \"description\": \"{{ $json.category }}\"\n    },\n    {\n      \"title\": \"\ubd84\uc11d \uacb0\uacfc\",\n      \"description\": \"{{ $json.analysis }}\"\n    },\n    {\n      \"title\": \"\ucd94\uac00 FAQ \uc218\",\n      \"description\": \"{{ $json.new_faq.length }}\uac74\"\n    }\n  ]\n}"
      },
      "id": "jandi-review-request",
      "name": "\uc794\ub514 \uac80\ud1a0 \uc694\uccad",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1760,
        -100
      ]
    },
    {
      "parameters": {},
      "id": "no-pattern",
      "name": "\ud328\ud134 \uc5c6\uc74c - \uc885\ub8cc",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        880,
        100
      ]
    }
  ],
  "connections": {
    "\ub9e4\uc77c \uc624\uc804 9\uc2dc": {
      "main": [
        [
          {
            "node": "\ucd5c\uadfc 7\uc77c VOC \uc870\ud68c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ucd5c\uadfc 7\uc77c VOC \uc870\ud68c": {
      "main": [
        [
          {
            "node": "\uc9c8\ubb38 \uc720\ud615 \ud074\ub7ec\uc2a4\ud130\ub9c1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\uc9c8\ubb38 \uc720\ud615 \ud074\ub7ec\uc2a4\ud130\ub9c1": {
      "main": [
        [
          {
            "node": "\ubc18\ubcf5 \ud328\ud134 \uc788\uc74c?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ubc18\ubcf5 \ud328\ud134 \uc788\uc74c?": {
      "main": [
        [
          {
            "node": "\uae30\uc874 \ub9e4\ub274\uc5bc \uc870\ud68c",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\ud328\ud134 \uc5c6\uc74c - \uc885\ub8cc",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\uae30\uc874 \ub9e4\ub274\uc5bc \uc870\ud68c": {
      "main": [
        [
          {
            "node": "Gemini \uc785\ub825 \uc900\ube44",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini \uc785\ub825 \uc900\ube44": {
      "main": [
        [
          {
            "node": "Gemini \ubcf4\uc644\uc548 \uc0dd\uc131",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini \ubcf4\uc644\uc548 \uc0dd\uc131": {
      "main": [
        [
          {
            "node": "\ubcf4\uc644\uc548 \ud30c\uc2f1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ubcf4\uc644\uc548 \ud30c\uc2f1": {
      "main": [
        [
          {
            "node": "\uc794\ub514 \uac80\ud1a0 \uc694\uccad",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}
Pro

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

How this works

This workflow automates the daily update of your customer service manual by analysing recent customer feedback to identify gaps and generate targeted improvements, saving hours of manual review and ensuring your support team always has the latest guidance. It is ideal for customer support managers or product teams handling high volumes of voice-of-customer (VOC) data, particularly in multilingual environments like Korean operations. The key step involves querying the past seven days' VOC via HTTP requests, clustering question patterns with custom code, and using Google Gemini to create precise supplement recommendations based on existing manual content.

Use this workflow when you need proactive, scheduled enhancements to service documentation driven by real customer interactions, such as in e-commerce or tech support pipelines running every morning. Avoid it for one-off audits or if your VOC data lacks structure, as it relies on consistent API access. Common variations include adjusting the cron schedule for weekly runs or integrating additional APIs like Zendesk for broader feedback sources.

About this workflow

파이프라인 ③ 매뉴얼 자동 업데이트. Uses scheduleTrigger, httpRequest, noOp. Scheduled trigger; 10 nodes.

Source: https://github.com/USEONGEE/n8n-example/blob/8d817d796e277931d4ecd47aaad930bf3afcca9c/.n8n/pipeline-3-manual-update.json — original creator credit. Request a take-down →

More General workflows → · Browse all categories →

Related workflows

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

General

WF-Main - XHS 主控制器. Uses scheduleTrigger, httpRequest, executeWorkflow, noOp. Scheduled trigger; 21 nodes.

HTTP Request
General

Dm-Profile-Visitors. Uses httpRequest, googleSheets. Scheduled trigger; 21 nodes.

HTTP Request, Google Sheets
General

RSS to Multi-Channel Social (X / LinkedIn / Discord). Uses stickyNote, scheduleTrigger, httpRequest. Scheduled trigger; 19 nodes.

HTTP Request
General

YouTube Channel to Notion. Uses stickyNote, scheduleTrigger, httpRequest, noOp. Scheduled trigger; 18 nodes.

HTTP Request
General

Automate Droplet Snapshots On Digitalocean. Uses httpRequest, stickyNote. Scheduled trigger; 17 nodes.

HTTP Request