This workflow corresponds to n8n.io template #10339 — we link there as the canonical source.
This workflow follows the Agent → Airtable 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": "FVAlstK1A75QWcNY",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Developer Chat Q&A Capture (Slack)",
"tags": [],
"nodes": [
{
"id": "d605b180-fd35-4ab7-9bc3-b7ac4d07382a",
"name": "Configure GPT-4o Model",
"type": "@n8n/n8n-nodes-langchain.lmChatAzureOpenAi",
"position": [
-48,
160
],
"parameters": {
"model": "gpt-4o",
"options": {}
},
"credentials": {
"azureOpenAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "673d6f84-b6df-4ab0-b660-68e9c2f42c58",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1008,
144
],
"parameters": {
"height": 416,
"content": "## Node Name: Slack Channel Trigger \u2013 Developer Q&A\n**Action**: Triggers the workflow when a new message is posted in the specified Slack channel.\n**Description**:\nThis node listens for new messages in the Slack channel. \nEach time a developer posts a question or discussion message, it captures the message content, user ID, timestamp, and channel details. \nThis initiates the automation for classifying and responding to developer Q&A.\n"
},
"typeVersion": 1
},
{
"id": "47fcda03-e6b3-4c57-9759-5ecd78e5111c",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-736,
-528
],
"parameters": {
"height": 448,
"content": "## Node Name: Validate Slack Message Payload\n**Action**: Checks if the incoming Slack message includes valid user and text data.\n**Description**:\nThis node ensures that the incoming event payload contains both a valid user ID and message text before proceeding. \nIf the message payload is incomplete or malformed, the workflow redirects execution to the error logging path. \nThis step prevents unnecessary AI calls for empty or system-generated Slack messages.\n"
},
"typeVersion": 1
},
{
"id": "ca80b0ca-55db-4daa-ae54-b0bebeeaa496",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-48,
-592
],
"parameters": {
"height": 496,
"content": "## Node Name: Classify Developer Question (AI)\n**Action**: Uses GPT-4o to determine whether the Slack message matches a known FAQ.\n**Description**:\nThis AI Agent compares the new Slack question against an internal FAQ knowledge base. \nIf a close match is found, GPT-4o returns structured JSON indicating \u201canswered,\u201d along with a canonical response and quality rating. \nIf no match exists, it flags the query as \u201cunanswered,\u201d prompting human follow-up. \nThis ensures that recurring developer issues are identified and reused efficiently."
},
"typeVersion": 1
},
{
"id": "3de471ba-4941-4bd4-b4c8-c0ef1ddac91d",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-384,
-528
],
"parameters": {
"height": 448,
"content": "## Node Name: Extract Question Metadata (JavaScript)\n**Action**: Cleans and structures Slack message data into a standardized JSON format.\n**Description**:\nThis JavaScript node extracts key fields \u2014 message text, user ID, timestamp, and channel \u2014 and removes unwanted characters or formatting (`>`, newlines, quotes). \nIt creates a clean \u201cquestion object\u201d ready for AI classification, improving accuracy and processing consistency.\n"
},
"typeVersion": 1
},
{
"id": "4918701a-c839-4ccc-8520-68896948d3b7",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-64,
320
],
"parameters": {
"height": 384,
"content": "## Node Name: Configure GPT-4o Model\n**Action**: Sets up Azure OpenAI GPT-4o as the active AI model.\n**Description**:\nThis node configures the GPT-4o model as the underlying large language model for question classification and similarity matching. \nIt enables intelligent comparison of Slack questions with internal FAQ data using semantic understanding.\n"
},
"typeVersion": 1
},
{
"id": "e49f36cf-2e1c-46cb-82b8-933828f06eee",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-448,
528
],
"parameters": {
"height": 416,
"content": "## Node Name: Log Workflow Errors to Google Sheets\n**Action**: Logs missing payloads or node execution errors into a Google Sheet.\n**Description**:\nIf a Slack event, AI response, or integration call fails, this node appends an entry in the \u201cerror log sheet.\u201d \nEach entry contains the error ID and description, enabling quick monitoring and debugging. \nThis maintains workflow transparency and ensures no silent automation failures.\n"
},
"typeVersion": 1
},
{
"id": "2579076f-312f-42a3-a4ec-28a06734d309",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
288,
-512
],
"parameters": {
"height": 432,
"content": "## Node Name: Parse AI JSON Output\n**Action**: Converts the AI\u2019s string output into structured JSON for further logic.\n**Description**:\nThe GPT model returns text formatted as JSON, but downstream nodes require an actual object. \nThis code node safely parses that text into valid JSON, making keys like `status`, `answer_quality`, and `canonical_answer` accessible for conditional checks. \nActs as the bridge between the AI\u2019s output and workflow logic.\n"
},
"typeVersion": 1
},
{
"id": "8acec43e-5345-47e6-b982-0ca2fc8bb8f6",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
544,
112
],
"parameters": {
"height": 448,
"content": "## Node Name: Check If Question Was Answered\n**Action**: Evaluates the AI\u2019s response to decide if the Slack question was resolved.\n**Description**:\nThis IF node checks whether `status == \"answered\"`. \nIf true, the flow routes toward the Notion database for documentation. \nIf false, it diverts to the Airtable node for escalation and manual review. \nImplements automated decision-making between documentation and triage paths.\n"
},
"typeVersion": 1
},
{
"id": "6aca240f-06ae-4efa-81da-de358ff558c2",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
880,
-736
],
"parameters": {
"height": 496,
"content": "## Node Name: Save Answered Question to Notion FAQ\n**Action**: Logs answered questions and AI explanations in the Notion database.\n**Description**:\nThis node creates a Notion entry within the database. \nIt records fields like `Status`, `Answer Quality`, and the AI\u2019s `Canonical Answer`. \nHelps maintain a living internal knowledge base automatically enriched from Slack discussions. \nEnables rapid reuse of resolved questions and promotes team self-help.\n"
},
"typeVersion": 1
},
{
"id": "5e8735d8-e19a-4022-8a88-11a5c089628d",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"position": [
912,
336
],
"parameters": {
"height": 464,
"content": "## Node Name: Log Unanswered Question to Airtable\n**Action**: Records new or unrecognized questions into Airtable for review.\n**Description**:\nThis Airtable node creates a record containing the Slack question text, user ID, message ID, and AI classification results. \nUsed for tracking unresolved or novel issues that should be reviewed by developers or support engineers. \nOver time, these entries feed back into the internal FAQ training loop.\n"
},
"typeVersion": 1
},
{
"id": "0f0577a1-998f-4d2a-bcc8-bdad6ce83712",
"name": "Slack Channel Trigger \u2013 Developer Q&A",
"type": "n8n-nodes-base.slackTrigger",
"position": [
-944,
-32
],
"parameters": {
"options": {},
"trigger": [
"message"
],
"channelId": {
"__rl": true,
"mode": "list",
"value": "C09CVLMSF3R",
"cachedResultName": "issue-smarteremr"
}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "4ae060d6-e45c-4186-8ca4-fc6254b937df",
"name": "Validate Slack Message Payload",
"type": "n8n-nodes-base.if",
"position": [
-672,
-32
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "e2adb005-2b3c-4d1e-8445-442df1fe925a",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json.user }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "e7aea1a1-c5d6-4036-b964-f7261b82f5a4",
"name": "Extract Question Metadata (JavaScript)",
"type": "n8n-nodes-base.code",
"position": [
-320,
-48
],
"parameters": {
"jsCode": "return [{\n question: $json.text.replace(/>|[\\n\"]/g, '').trim(),\n user: $json.user,\n timestamp: $json.ts,\n channel: $json.channel\n}];\n"
},
"typeVersion": 2
},
{
"id": "582017af-c1ee-4670-87a8-b3fd5baf3199",
"name": " Classify Developer Question (AI)",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-16,
-48
],
"parameters": {
"text": "=You are a Slack Q&A classifier for developer discussions. \nYou have the following internal FAQ knowledge:\n\n1. Q: How to connect local API server to staging database? \n A: Update the `.env` file with staging DB credentials and restart the service.\n\n2. Q: How to restart the backend server? \n A: Run `pm2 restart backend` or `npm run serve` depending on environment.\n\n3. Q: How to fix 401 error on /auth/login endpoint? \n A: Ensure the auth token is refreshed using the /refresh-token route before login.\n\n---\n\nYour job:\nWhen a new developer question comes in, check if it matches (or is similar) to one of the questions above. \nIf yes, output JSON like this:\n{\n \"status\": \"answered\",\n \"answer_quality\": \"strong\",\n \"canonical_answer\": \"(matching answer from above)\"\n}\n\nIf no match found, output:\n{\n \"status\": \"unanswered\",\n \"answer_quality\": \"weak\",\n \"canonical_answer\": \"\"\n}\nAlways respond with clean JSON only.\n",
"options": {
"systemMessage": "=Question: {{$json.question}}\n"
},
"promptType": "define"
},
"typeVersion": 2.1
},
{
"id": "fd788ab2-46df-498d-a8df-b4d45161ffd5",
"name": "Parse AI JSON Output",
"type": "n8n-nodes-base.code",
"position": [
368,
-48
],
"parameters": {
"jsCode": "return [JSON.parse($json.output)];\n"
},
"typeVersion": 2
},
{
"id": "53a743a3-1b56-419b-a8f6-1a5c8af21ec1",
"name": "Check If Question Was Answered",
"type": "n8n-nodes-base.if",
"position": [
656,
-48
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "b082c89c-ddea-4183-ad99-04b76ffb9bdb",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.status }}",
"rightValue": "answered"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "050dfc42-d32a-48cc-ab95-f47ba238d719",
"name": "Save Answered Question to Notion FAQ",
"type": "n8n-nodes-base.notion",
"position": [
944,
-208
],
"parameters": {
"title": "={{ $('Slack Channel Trigger \u2013 Developer Q&A').item.json.text }}",
"simple": false,
"options": {},
"resource": "databasePage",
"databaseId": {
"__rl": true,
"mode": "list",
"value": "29a802b9-1fa0-804a-b406-e078961e0659",
"cachedResultUrl": "https://www.notion.so/29a802b91fa0804ab406e078961e0659",
"cachedResultName": "Release Notes"
},
"propertiesUi": {
"propertyValues": [
{
"key": "Status|rich_text",
"textContent": "={{ $json.status }}"
},
{
"key": "Priority|rich_text",
"textContent": "={{ $json.answer_quality }}"
},
{
"key": "FAQ Content|rich_text",
"textContent": "={{ $json.canonical_answer }}"
}
]
}
},
"credentials": {
"notionApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "9c8a1797-2cc1-415c-839c-88dd8b4aa1b5",
"name": " Log Unanswered Question to Airtable",
"type": "n8n-nodes-base.airtable",
"position": [
944,
160
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "appsZ3Uuh5PnD215s",
"cachedResultUrl": "https://airtable.com/appsZ3Uuh5PnD215s",
"cachedResultName": "Github Issue"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblZvyR7J8hndLlUZ",
"cachedResultUrl": "https://airtable.com/appsZ3Uuh5PnD215s/tblZvyR7J8hndLlUZ",
"cachedResultName": "Table 1"
},
"columns": {
"value": {
"Name": "={{ $('Slack Channel Trigger \u2013 Developer Q&A').item.json.text }}",
"Status": "Todo",
"FAQ Match": "={{ $json.answer_quality }}",
"Error Code": "={{ $json.status }}",
"Issue Number": "={{ $('Slack Channel Trigger \u2013 Developer Q&A').item.json.client_msg_id }}"
},
"schema": [
{
"id": "Name",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Status",
"type": "options",
"display": true,
"options": [
{
"name": "Todo",
"value": "Todo"
},
{
"name": "In progress",
"value": "In progress"
},
{
"name": "Done",
"value": "Done"
}
],
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Error Code",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Error Code",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Error Category",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Error Category",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "FAQ Match",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "FAQ Match",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Root Cause",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Root Cause",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Suggested Action",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Suggested Action",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Confidence",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Confidence",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Severity",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Severity",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Affected Endpoint",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Affected Endpoint",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Source URL",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Source URL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Repository",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Repository",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Issue Number",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Issue Number",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Created At",
"type": "string",
"display": true,
"removed": true,
"readOnly": true,
"required": false,
"displayName": "Created At",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "create"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "9a40948e-659c-42ef-8817-985eab6191d6",
"name": "Log Workflow Errors to Google Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
-384,
336
],
"parameters": {
"columns": {
"value": {},
"schema": [
{
"id": "error_id",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "error_id",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "error",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "error",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"error_id"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": 1338537721,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Uldk_4BxWbdZTDZxFUeohIfeBmGHHqVEl9Ogb0l6R8Y/edit#gid=1338537721",
"cachedResultName": "error log sheet"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1Uldk_4BxWbdZTDZxFUeohIfeBmGHHqVEl9Ogb0l6R8Y",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Uldk_4BxWbdZTDZxFUeohIfeBmGHHqVEl9Ogb0l6R8Y/edit?usp=drivesdk",
"cachedResultName": "Interviewer Brief Pack "
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.6
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "5d86bdfd-9f04-44c3-b043-b573e72cfe3b",
"connections": {
"Parse AI JSON Output": {
"main": [
[
{
"node": "Check If Question Was Answered",
"type": "main",
"index": 0
}
]
]
},
"Configure GPT-4o Model": {
"ai_languageModel": [
[
{
"node": " Classify Developer Question (AI)",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Check If Question Was Answered": {
"main": [
[
{
"node": "Save Answered Question to Notion FAQ",
"type": "main",
"index": 0
}
],
[
{
"node": " Log Unanswered Question to Airtable",
"type": "main",
"index": 0
}
]
]
},
"Validate Slack Message Payload": {
"main": [
[
{
"node": "Extract Question Metadata (JavaScript)",
"type": "main",
"index": 0
}
],
[
{
"node": "Log Workflow Errors to Google Sheets",
"type": "main",
"index": 0
}
]
]
},
" Classify Developer Question (AI)": {
"main": [
[
{
"node": "Parse AI JSON Output",
"type": "main",
"index": 0
}
]
]
},
"Extract Question Metadata (JavaScript)": {
"main": [
[
{
"node": " Classify Developer Question (AI)",
"type": "main",
"index": 0
}
]
]
},
"Slack Channel Trigger \u2013 Developer Q&A": {
"main": [
[
{
"node": "Validate Slack Message Payload",
"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.
airtableTokenApiazureOpenAiApigoogleSheetsOAuth2ApinotionApislackApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow automates the Developer Q&A Classification and Documentation process using Slack, Azure OpenAI GPT-4o, Notion, Airtable, and Google Sheets. Whenever a new message is posted in a specific Slack channel, the workflow automatically: Captures and validates the message…
Source: https://n8n.io/workflows/10339/ — 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.
This workflow automates end-to-end AI-driven inventory intelligence, transforming Airtable stock data into optimized reorder recommendations, daily operational summaries, and instant Slack alerts. It
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.
This n8n workflow lets you control access to your internal Telegram bots and automation systems based on user roles and departments. It ensures that only authorized team members — defined in your empl
This n8n workflow helps you restrict access to your internal chats or chatbots so that only authorized team members can interact with them. It's perfect for setups using Telegram, Slack, or other corp
Xmind Sales Email v2. Uses gmailTrigger, notion, googleSheets, googleSheetsTrigger. Event-driven trigger; 37 nodes.