This workflow follows the OpenAI → Slack 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 →
{
"name": "03 - AI Support Ticket Triage",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "support-ticket",
"responseMode": "responseNode",
"options": {}
},
"id": "node-webhook",
"name": "Ticket Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
260,
300
]
},
{
"parameters": {
"jsCode": "// Verify HMAC signature to ensure ticket comes from a trusted source\nconst crypto = require('crypto');\nconst SECRET = $env['WEBHOOK_SECRET'] || 'change-me';\n\nconst body = $input.first().json;\nconst rawBody = JSON.stringify(body.body || body);\nconst signature = $input.first().json.headers?.['x-webhook-signature'] || '';\n\nconst expected = crypto\n .createHmac('sha256', SECRET)\n .update(rawBody)\n .digest('hex');\n\nconst verified = signature === `sha256=${expected}`;\n\nif (!verified && $env['ENFORCE_SIGNATURE'] === 'true') {\n throw new Error('Invalid webhook signature \u2014 request rejected');\n}\n\nconst ticket = body.body || body;\n\nreturn [{\n json: {\n signatureVerified: verified,\n ticketId: ticket.id || `TKT-${Date.now()}`,\n subject: ticket.subject || ticket.title || 'No Subject',\n body: ticket.body || ticket.description || ticket.message || '',\n email: ticket.email || ticket.from || '',\n name: ticket.name || ticket.customer_name || 'Customer',\n priority: ticket.priority || 'normal',\n channel: ticket.channel || 'email',\n timestamp: new Date().toISOString()\n }\n}];"
},
"id": "node-verify-sig",
"name": "Code - Verify Signature",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
480,
300
]
},
{
"parameters": {
"resource": "text",
"operation": "message",
"modelId": {
"__rl": true,
"value": "gpt-4o",
"mode": "list"
},
"messages": {
"values": [
{
"role": "system",
"content": "You are a support ticket classifier. Analyze the ticket and respond with a JSON object containing:\n- category: one of [billing, technical, account, feature_request, bug, complaint, general]\n- urgency: one of [low, medium, high, critical]\n- sentiment: one of [positive, neutral, negative, angry]\n- department: one of [billing_team, tech_support, account_management, product_team, general_support]\n- summary: 1-sentence summary of the issue\n- suggested_response_tone: one of [empathetic, technical, informative, apologetic]\n\nRespond ONLY with valid JSON, no markdown."
},
{
"role": "user",
"content": "Subject: {{ $json.subject }}\n\nMessage: {{ $json.body }}\n\nCustomer: {{ $json.name }} ({{ $json.email }})"
}
]
},
"options": {
"temperature": 0.2,
"maxTokens": 300
}
},
"id": "node-openai-classify",
"name": "OpenAI - Categorize Ticket",
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 1.7,
"position": [
700,
300
],
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const ticket = $('Code - Verify Signature').first().json;\nconst aiRaw = $input.first().json.message?.content || $input.first().json.choices?.[0]?.message?.content || '{}';\n\nlet aiResult;\ntry {\n aiResult = JSON.parse(aiRaw);\n} catch (e) {\n aiResult = { category: 'general', urgency: 'medium', sentiment: 'neutral', department: 'general_support', summary: 'Unable to parse AI classification' };\n}\n\nreturn [{\n json: {\n ...ticket,\n ...aiResult,\n aiClassified: true\n }\n}];"
},
"id": "node-parse-ai",
"name": "Code - Parse AI Result",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
920,
300
]
},
{
"parameters": {
"dataType": "string",
"value1": "={{ $json.department }}",
"rules": {
"rules": [
{
"value2": "billing_team",
"outputKey": "billing"
},
{
"value2": "tech_support",
"outputKey": "tech"
},
{
"value2": "account_management",
"outputKey": "account"
},
{
"value2": "product_team",
"outputKey": "product"
}
]
},
"fallbackOutput": "last"
},
"id": "node-switch",
"name": "Switch - Route by Department",
"type": "n8n-nodes-base.switch",
"typeVersion": 3.2,
"position": [
1140,
300
]
},
{
"parameters": {
"select": "channel",
"channelId": {
"__rl": true,
"value": "C_BILLING_CHANNEL",
"mode": "id"
},
"messageType": "block",
"blocksUi": {
"blocksValues": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "\ud83d\udcb3 Billing Ticket \u2014 {{ $json.urgency.toUpperCase() }} Priority"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*ID:* {{ $json.ticketId }}"
},
{
"type": "mrkdwn",
"text": "*Customer:* {{ $json.name }}"
},
{
"type": "mrkdwn",
"text": "*Email:* {{ $json.email }}"
},
{
"type": "mrkdwn",
"text": "*Sentiment:* {{ $json.sentiment }}"
}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Summary:* {{ $json.summary }}"
}
}
]
}
},
"id": "node-slack-billing",
"name": "Slack - Billing Team",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [
1360,
140
],
"credentials": {
"slackApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"select": "channel",
"channelId": {
"__rl": true,
"value": "C_TECH_CHANNEL",
"mode": "id"
},
"messageType": "block",
"blocksUi": {
"blocksValues": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "\ud83d\udd27 Tech Support Ticket \u2014 {{ $json.urgency.toUpperCase() }} Priority"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*ID:* {{ $json.ticketId }}"
},
{
"type": "mrkdwn",
"text": "*Customer:* {{ $json.name }}"
},
{
"type": "mrkdwn",
"text": "*Category:* {{ $json.category }}"
},
{
"type": "mrkdwn",
"text": "*Sentiment:* {{ $json.sentiment }}"
}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Summary:* {{ $json.summary }}"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Message:* {{ $json.body.substring(0, 500) }}"
}
}
]
}
},
"id": "node-slack-tech",
"name": "Slack - Tech Support",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [
1360,
260
],
"credentials": {
"slackApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"select": "channel",
"channelId": {
"__rl": true,
"value": "C_ACCOUNT_CHANNEL",
"mode": "id"
},
"messageType": "text",
"text": "\ud83d\udc64 *Account Ticket* [{{ $json.urgency }}] - {{ $json.name }} ({{ $json.email }})\n*Summary:* {{ $json.summary }}"
},
"id": "node-slack-account",
"name": "Slack - Account Mgmt",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [
1360,
380
],
"credentials": {
"slackApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"select": "channel",
"channelId": {
"__rl": true,
"value": "C_PRODUCT_CHANNEL",
"mode": "id"
},
"messageType": "text",
"text": "\ud83d\udca1 *Product Feedback / Feature Request* from {{ $json.name }}\n*Summary:* {{ $json.summary }}\n*Full message:* {{ $json.body.substring(0, 300) }}..."
},
"id": "node-slack-product",
"name": "Slack - Product Team",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [
1360,
500
],
"credentials": {
"slackApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"select": "channel",
"channelId": {
"__rl": true,
"value": "C_GENERAL_SUPPORT",
"mode": "id"
},
"messageType": "text",
"text": "\ud83d\udce9 *General Support Ticket* [{{ $json.urgency }}]\n*From:* {{ $json.name }} ({{ $json.email }})\n*Category:* {{ $json.category }}\n*Summary:* {{ $json.summary }}"
},
"id": "node-slack-general",
"name": "Slack - General Support",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [
1360,
620
],
"credentials": {
"slackApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={ \"success\": true, \"ticketId\": \"{{ $('Code - Parse AI Result').item.json.ticketId }}\", \"department\": \"{{ $('Code - Parse AI Result').item.json.department }}\" }"
},
"id": "node-respond",
"name": "Respond to Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
1140,
520
]
}
],
"connections": {
"Ticket Webhook": {
"main": [
[
{
"node": "Code - Verify Signature",
"type": "main",
"index": 0
}
]
]
},
"Code - Verify Signature": {
"main": [
[
{
"node": "OpenAI - Categorize Ticket",
"type": "main",
"index": 0
}
]
]
},
"OpenAI - Categorize Ticket": {
"main": [
[
{
"node": "Code - Parse AI Result",
"type": "main",
"index": 0
}
]
]
},
"Code - Parse AI Result": {
"main": [
[
{
"node": "Switch - Route by Department",
"type": "main",
"index": 0
},
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
},
"Switch - Route by Department": {
"main": [
[
{
"node": "Slack - Billing Team",
"type": "main",
"index": 0
}
],
[
{
"node": "Slack - Tech Support",
"type": "main",
"index": 0
}
],
[
{
"node": "Slack - Account Mgmt",
"type": "main",
"index": 0
}
],
[
{
"node": "Slack - Product Team",
"type": "main",
"index": 0
}
],
[
{
"node": "Slack - General Support",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"tags": [
{
"name": "support"
},
{
"name": "ai"
},
{
"name": "routing"
}
],
"meta": {
"description": "Receives support tickets via webhook, verifies HMAC signature, uses GPT-4o to classify category/urgency/sentiment, then routes via Switch node to the correct Slack department channel.",
"prerequisites": [
"OpenAI API key with gpt-4o access",
"Slack Bot Token with chat:write scope",
"Set WEBHOOK_SECRET env variable in n8n",
"Set ENFORCE_SIGNATURE=true to reject unsigned requests",
"Create Slack channels: billing, tech-support, account-management, product, general-support"
],
"testingScenario": {
"happy_path": "POST billing complaint \u2192 routes to billing channel with correct urgency",
"test_payloads": [
{
"subject": "Charged twice this month",
"body": "I was billed twice for my subscription. This needs to be fixed ASAP!",
"email": "customer@test.com",
"name": "Test User"
},
{
"subject": "App crashes on login",
"body": "The mobile app crashes every time I try to log in on iOS 17.",
"email": "dev@test.com",
"name": "Dev User"
},
{
"subject": "Feature request: dark mode",
"body": "Would love a dark mode option in the dashboard.",
"email": "user@test.com",
"name": "Power User"
}
]
}
}
}
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.
openAiApislackApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
03 - AI Support Ticket Triage. Uses openAi, slack. Webhook trigger; 11 nodes.
Source: https://github.com/satmakuru222/TheAIStackk/blob/main/n8n-workflows/03-ai-support-ticket-triage.json — 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 captures new leads via webhook, enriches and scores them with Apollo and Google Gemini, logs everything in Google Sheets, and automates outreach, reply handling, follow-ups, meeting prep
Venafi Presentation - Watch Video
Pyragogy AI Village - Orchestrazione Master (Architettura Profonda V2). Uses start, postgres, openAi, emailSend. Webhook trigger; 37 nodes.
Automatically detects missed Zoom demos booked via Calendly and triggers AI-powered follow-up sequences.
Pyragogy AI Village - Orchestrazione Master (Architettura Profonda V2). Uses start, postgres, openAi, emailSend. Webhook trigger; 36 nodes.