toolgrader.com

N8N WORKFLOWS — SALES & LEAD MANAGEMENT

AI Lead Qualification & CRM Routing

Our Guidance

Captures inquiries from 4 channels (Website, Telegram, WhatsApp, Email), uses AI to qualify leads, instantly alerts sales for hot leads on Slack/Telegram, and auto-replies to standard inquiries on their original channel.

Create a Google Sheet with two tabs — “Leads” and “ErrorLog” — then fill in the Config node with your Sheet ID, reply-from email, Slack channel, and your sales team’s Telegram/WhatsApp IDs.

Connect only the channels you actually use — Website form, Telegram, WhatsApp, or Gmail — you don’t need all 4 active at once.

Hot leads (clear buying intent, budget, or urgency) alert your sales team on Slack AND Telegram instantly, so a high-value lead is never missed.

Standard inquiries get an automatic, on-brand reply through whichever channel they arrived on, plus a logged record in your Sheet.

Full node-by-node setup notes are built directly into the workflow file itself — no separate guide needed.

Get the Workflow File

{"name": "AI Lead Qualification & CRM Routing", "nodes": [{"parameters": {"httpMethod": "POST", "path": "new-lead", "options": {"noResponseBody": false, "responseData": "{\"status\":\"received\"}"}}, "id": "3dc0436b-89a8-4fcd-a952-ea8bcd32ca17", "name": "Webhook - Website Inquiry", "type": "n8n-nodes-base.webhook", "typeVersion": 2.1, "position": [-688, 48], "webhookId": "75b1b060-ee3d-42f9-9ff7-a397cf98c145"}, {"parameters": {"assignments": {"assignments": [{"id": "sheetId", "name": "sheetId", "value": "<__PLACEHOLDER_VALUE__paste your Google Sheet ID or full URL__>", "type": "string"}, {"id": "sheetName", "name": "sheetName", "value": "Leads", "type": "string"}, {"id": "fromEmail", "name": "fromEmail", "value": "<__PLACEHOLDER_VALUE__e.g. support@yourbusiness.com__>", "type": "string"}, {"id": "fromName", "name": "fromName", "value": "<__PLACEHOLDER_VALUE__e.g. Your Business Name__>", "type": "string"}, {"id": "salesSlackChannel", "name": "salesSlackChannel", "value": "<__PLACEHOLDER_VALUE__e.g. hot-leads__>", "type": "string"}, {"id": "salesTelegramChatId", "name": "salesTelegramChatId", "value": "<__PLACEHOLDER_VALUE__sales team chat ID (different from customer chats)__>", "type": "string"}, {"id": "whatsappPhoneNumberId", "name": "whatsappPhoneNumberId", "value": "<__PLACEHOLDER_VALUE__your WhatsApp Business phone number ID__>", "type": "string"}]}, "includeOtherFields": true, "options": {}}, "id": "afc74381-1977-4821-a539-e37540745b6f", "name": "Config (edit me)", "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [-416, 208]}, {"parameters": {"jsCode": "\nconst items = $input.all();\n\nreturn items.map(item => {\n const raw = item.json;\n let channel = 'email';\n let customerName = '';\n let customerEmail = '';\n let customerPhone = '';\n let telegramChatId = '';\n let messageText = '';\n\n if (raw.message && raw.message.chat && raw.message.chat.id !== undefined) {\n channel = 'telegram';\n telegramChatId = String(raw.message.chat.id);\n customerName = [raw.message.from?.first_name, raw.message.from?.last_name].filter(Boolean).join(' ') || 'there';\n messageText = raw.message.text || '';\n } else if (raw.entry && Array.isArray(raw.entry)) {\n channel = 'whatsapp';\n const change = raw.entry[0]?.changes?.[0]?.value;\n const msg = change?.messages?.[0];\n const contact = change?.contacts?.[0];\n customerPhone = msg?.from || '';\n customerName = contact?.profile?.name || 'there';\n messageText = msg?.text?.body || '';\n } else if (raw.from || raw.headers) {\n channel = 'email';\n customerEmail = (raw.from || '').match(/<(.+)>/)?.[1] || raw.from || '';\n customerName = (raw.from || '').split('<')[0].trim() || 'there';\n messageText = raw.text || raw.snippet || '';\n } else {\n channel = 'email';\n const body = raw.body || raw;\n customerName = body.name || body.customerName || 'there';\n customerEmail = body.email || body.customerEmail || '';\n messageText = body.message || body.inquiry || body.text || '';\n }\n\n return {\n json: {\n channel,\n customerName,\n customerEmail,\n customerPhone,\n telegramChatId,\n messageText\n }\n };\n});\n"}, "id": "90829650-d668-4d47-a748-a1e1f350f5b1", "name": "Normalize Inquiry Data", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [-160, 208]}, {"parameters": {"modelId": {"__rl": true, "mode": "id", "value": "claude-sonnet-5"}, "messages": {"values": [{"content": "={{ \"Customer name: \" + nodeJson(normalizeInquiry, 'customerName') + \"\\nChannel: \" + nodeJson(normalizeInquiry, 'channel') + \"\\nMessage: \" + nodeJson(normalizeInquiry, 'messageText') + \"\\n\\nAnalyze this inquiry and respond with ONLY a JSON object, no markdown fences, no commentary.\" }}"}]}, "options": {"system": "You are an expert sales and support triage analyst. Given an incoming customer message, you: (1) qualify it as a lead, (2) detect urgency and sentiment, (3) draft a short personalized professional reply usable for standard inquiries. You ALWAYS respond with ONLY a single valid JSON object matching exactly this shape: {\"qualification\":\"hot\"|\"standard\",\"urgency\":\"high\"|\"medium\"|\"low\",\"sentiment\":string,\"reason\":string,\"suggestedResponse\":string}. Qualify as \"hot\" only for clear buying intent, budget/timeline mentioned, enterprise scale, or explicit urgency (e.g. \"ready to purchase\", \"need this today\", \"what is your pricing for 50 seats\"). Everything else (general questions, support requests, vague interest) is \"standard\". suggestedResponse must be a complete, ready-to-send, professional reply in plain text addressing their message - do not mention that you are an AI.", "maxTokens": 1200, "temperature": 0.5}}, "id": "54efe3af-9902-4a5a-a967-409d00c1b7bb", "name": "Analyze Lead (Claude)", "type": "@n8n/n8n-nodes-langchain.anthropic", "typeVersion": 1, "position": [112, 208], "retryOnFail": true, "maxTries": 2, "waitBetweenTries": 3000, "credentials": {"anthropicApi": {"id": "LNlkJdvx9A1wJIV6", "name": "Anthropic account"}}}, {"parameters": {"jsCode": "\nconst items = $input.all();\n\nreturn items.map(item => {\n const json = item.json;\n let raw = json.content || json.text || (json.message && json.message.content) || '';\n if (Array.isArray(raw)) {\n raw = raw.map(block => (block && block.text) ? block.text : '').join('');\n }\n if (typeof raw !== 'string') {\n raw = String(raw || '');\n }\n\n const cleaned = raw.replace(/```json/gi, '').replace(/```/g, '').trim();\n\n let parsed = null;\n try {\n parsed = JSON.parse(cleaned);\n } catch (e) {\n parsed = null;\n }\n\n const parseError = !parsed || typeof parsed !== 'object';\n\n return {\n json: {\n qualification: parseError ? 'standard' : (parsed.qualification || 'standard'),\n urgency: parseError ? 'unknown' : (parsed.urgency || 'unknown'),\n sentiment: parseError ? 'unknown' : (parsed.sentiment || 'unknown'),\n reason: parseError ? 'Could not parse AI response - defaulted to standard handling' : (parsed.reason || ''),\n suggestedResponse: parseError ? '' : (parsed.suggestedResponse || ''),\n parseError\n }\n };\n});\n"}, "id": "3e17a121-cccb-4560-8986-0f11b3faddd7", "name": "Parse AI Output", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [368, 208]}, {"parameters": {"conditions": {"options": {"caseSensitive": false, "leftValue": "", "typeValidation": "loose", "version": 1}, "combinator": "and", "conditions": [{"leftValue": "={{ $json.qualification }}", "operator": {"type": "string", "operation": "equals"}, "rightValue": "hot"}]}, "options": {}}, "id": "df9c8040-3e9f-4fa0-acda-cbb17098e97a", "name": "Is Hot Lead?", "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [624, 208]}, {"parameters": {"operation": "append", "documentId": {"__rl": true, "mode": "id", "value": "={{ nodeJson(config, 'sheetId') }}", "cachedResultName": "From Config"}, "sheetName": {"__rl": true, "mode": "name", "value": "={{ nodeJson(config, 'sheetName') }}", "cachedResultName": "From Config"}, "columns": {"mappingMode": "defineBelow", "value": {"Timestamp": "={{ $now.toISO() }}", "CustomerName": "={{ nodeJson(normalizeInquiry, 'customerName') }}", "Channel": "={{ nodeJson(normalizeInquiry, 'channel') }}", "ContactInfo": "={{ nodeJson(normalizeInquiry, 'customerEmail') + nodeJson(normalizeInquiry, 'customerPhone') + nodeJson(normalizeInquiry, 'telegramChatId') }}", "Message": "={{ nodeJson(normalizeInquiry, 'messageText') }}", "Qualification": "Hot Lead", "Urgency": "={{ $json.urgency }}", "Sentiment": "={{ $json.sentiment }}", "Reason": "={{ $json.reason }}"}, "schema": [{"id": "Timestamp", "displayName": "Timestamp", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "CustomerName", "displayName": "CustomerName", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "Channel", "displayName": "Channel", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "ContactInfo", "displayName": "ContactInfo", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "Message", "displayName": "Message", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "Qualification", "displayName": "Qualification", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "Urgency", "displayName": "Urgency", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "Sentiment", "displayName": "Sentiment", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "Reason", "displayName": "Reason", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}]}, "options": {}}, "id": "65521deb-bf8b-40ab-ba6f-a7820ac760e1", "name": "Log Hot Lead to CRM", "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [880, 48], "retryOnFail": true, "maxTries": 3, "waitBetweenTries": 2000}, {"parameters": {"select": "channel", "channelId": {"__rl": true, "mode": "name", "value": "={{ nodeJson(config, 'salesSlackChannel') }}", "cachedResultName": "From Config"}, "text": "={{ \"šŸ”„ HOT LEAD (\" + $json.urgency + \" urgency) via \" + nodeJson(normalizeInquiry, 'channel') + \"\\nFrom: \" + nodeJson(normalizeInquiry, 'customerName') + \"\\nMessage: \" + nodeJson(normalizeInquiry, 'messageText') + \"\\nWhy: \" + $json.reason }}", "otherOptions": {}}, "id": "b2d29476-53f4-4e35-a4cf-cf80d728aac6", "name": "Alert Sales (Slack)", "type": "n8n-nodes-base.slack", "typeVersion": 2.5, "position": [1152, 48], "retryOnFail": true, "maxTries": 2, "waitBetweenTries": 2000, "webhookId": "cd0c9477-4656-4f08-83bb-95269c52818b", "onError": "continueRegularOutput"}, {"parameters": {"chatId": "={{ nodeJson(config, 'salesTelegramChatId') }}", "text": "={{ \"šŸ”„ HOT LEAD (\" + $json.urgency + \" urgency) via \" + nodeJson(normalizeInquiry, 'channel') + \"\\nFrom: \" + nodeJson(normalizeInquiry, 'customerName') + \"\\nMessage: \" + nodeJson(normalizeInquiry, 'messageText') }}", "additionalFields": {"appendAttribution": false}}, "id": "c0d4eaaf-98f4-4989-8826-282c92100406", "name": "Alert Sales (Telegram)", "type": "n8n-nodes-base.telegram", "typeVersion": 1.2, "position": [1408, 48], "retryOnFail": true, "maxTries": 2, "waitBetweenTries": 2000, "webhookId": "c25426cf-43ff-42fe-a54f-1618ba4b62fc", "onError": "continueRegularOutput"}, {"parameters": {"operation": "append", "documentId": {"__rl": true, "mode": "id", "value": "={{ nodeJson(config, 'sheetId') }}", "cachedResultName": "From Config"}, "sheetName": {"__rl": true, "mode": "name", "value": "={{ nodeJson(config, 'sheetName') }}", "cachedResultName": "From Config"}, "columns": {"mappingMode": "defineBelow", "value": {"Timestamp": "={{ $now.toISO() }}", "CustomerName": "={{ nodeJson(normalizeInquiry, 'customerName') }}", "Channel": "={{ nodeJson(normalizeInquiry, 'channel') }}", "ContactInfo": "={{ nodeJson(normalizeInquiry, 'customerEmail') + nodeJson(normalizeInquiry, 'customerPhone') + nodeJson(normalizeInquiry, 'telegramChatId') }}", "Message": "={{ nodeJson(normalizeInquiry, 'messageText') }}", "Qualification": "Standard Inquiry", "Urgency": "={{ $json.urgency }}", "Sentiment": "={{ $json.sentiment }}", "Reason": "={{ $json.reason }}"}, "schema": [{"id": "Timestamp", "displayName": "Timestamp", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "CustomerName", "displayName": "CustomerName", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "Channel", "displayName": "Channel", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "ContactInfo", "displayName": "ContactInfo", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "Message", "displayName": "Message", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "Qualification", "displayName": "Qualification", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "Urgency", "displayName": "Urgency", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "Sentiment", "displayName": "Sentiment", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "Reason", "displayName": "Reason", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}]}, "options": {}}, "id": "b6106dad-a50f-4bbd-b560-c690dffbdbcf", "name": "Log Standard Inquiry", "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [880, 400], "retryOnFail": true, "maxTries": 3, "waitBetweenTries": 2000}, {"parameters": {"rules": {"values": [{"conditions": {"options": {"caseSensitive": false, "leftValue": "", "typeValidation": "strict", "version": 1}, "conditions": [{"leftValue": "={{ nodeJson(normalizeInquiry, 'channel') }}", "operator": {"type": "string", "operation": "equals"}, "rightValue": "telegram"}], "combinator": "and"}}, {"conditions": {"options": {"caseSensitive": false, "leftValue": "", "typeValidation": "strict", "version": 1}, "conditions": [{"leftValue": "={{ nodeJson(normalizeInquiry, 'channel') }}", "operator": {"type": "string", "operation": "equals"}, "rightValue": "whatsapp"}], "combinator": "and"}}, {"conditions": {"options": {"caseSensitive": false, "leftValue": "", "typeValidation": "strict", "version": 1}, "conditions": [{"leftValue": "={{ nodeJson(normalizeInquiry, 'channel') }}", "operator": {"type": "string", "operation": "equals"}, "rightValue": "email"}], "combinator": "and"}}]}, "options": {"fallbackOutput": "extra", "renameFallbackOutput": "Unknown Channel"}}, "id": "ee9573a5-c8d5-4d2d-92b7-433e546d2d48", "name": "Route Reply by Channel", "type": "n8n-nodes-base.switch", "typeVersion": 3.4, "position": [1152, 400]}, {"parameters": {"chatId": "={{ nodeJson(normalizeInquiry, 'telegramChatId') }}", "text": "={{ $json.suggestedResponse }}", "additionalFields": {"appendAttribution": false}}, "id": "c13a4070-d8f7-4a39-bcdc-adc313d20082", "name": "Reply via Telegram", "type": "n8n-nodes-base.telegram", "typeVersion": 1.2, "position": [1408, 288], "retryOnFail": true, "maxTries": 2, "waitBetweenTries": 2000, "webhookId": "8278b07b-3eca-4c5d-9703-e4da3ef41abd", "onError": "continueRegularOutput"}, {"parameters": {"operation": "send", "phoneNumberId": "={{ nodeJson(config, 'whatsappPhoneNumberId') }}", "recipientPhoneNumber": "={{ nodeJson(normalizeInquiry, 'customerPhone') }}", "textBody": "={{ $json.suggestedResponse }}", "additionalFields": {}}, "id": "cd6e29b2-0085-487c-b5f8-af915e7c48f7", "name": "Reply via WhatsApp", "type": "n8n-nodes-base.whatsApp", "typeVersion": 1.1, "position": [1408, 400], "retryOnFail": true, "maxTries": 2, "waitBetweenTries": 2000, "webhookId": "165aa525-f597-4f2f-b016-eaccfb0254f5", "onError": "continueRegularOutput"}, {"parameters": {"resource": "mail", "fromEmail": "={{ nodeJson(config, 'fromEmail') }}", "fromName": "={{ nodeJson(config, 'fromName') }}", "toEmail": "={{ nodeJson(normalizeInquiry, 'customerEmail') }}", "subject": "Re: Your inquiry", "contentValue": "={{ $json.suggestedResponse }}", "additionalFields": {}}, "id": "b3f22f79-c548-4643-9841-3779ec4db99c", "name": "Reply via Email", "type": "n8n-nodes-base.sendGrid", "typeVersion": 1, "position": [1408, 528], "retryOnFail": true, "maxTries": 2, "waitBetweenTries": 2000, "onError": "continueRegularOutput"}, {"parameters": {"operation": "append", "documentId": {"__rl": true, "mode": "id", "value": "={{ nodeJson(config, 'sheetId') }}", "cachedResultName": "From Config"}, "sheetName": {"__rl": true, "mode": "name", "value": "ErrorLog", "cachedResultName": "ErrorLog"}, "columns": {"mappingMode": "defineBelow", "value": {"Timestamp": "={{ $now.toISO() }}", "FailedNode": "Route Reply by Channel", "ErrorMessage": "={{ \"Unrecognized channel value: \" + nodeJson(normalizeInquiry, 'channel') + \" - no automatic reply sent\" }}"}, "schema": [{"id": "Timestamp", "displayName": "Timestamp", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "FailedNode", "displayName": "FailedNode", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "ErrorMessage", "displayName": "ErrorMessage", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}]}, "options": {}}, "id": "5d42c83d-8f4c-475d-b3a2-6cd0b7c7b435", "name": "Log Unknown Channel (No Reply Sent)", "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [1408, 640], "retryOnFail": true, "maxTries": 3, "waitBetweenTries": 2000}, {"parameters": {"updates": ["message"], "additionalFields": {}}, "id": "878a4f66-1c7f-472d-8b9b-458fb9771344", "name": "Telegram - Customer Message", "type": "n8n-nodes-base.telegramTrigger", "typeVersion": 1.3, "position": [-688, 160], "webhookId": "d2f2a082-31a4-4b72-ba03-03b34d13bb01"}, {"parameters": {"updates": ["messages"], "options": {}}, "id": "a6184184-2231-438f-b21c-ef6e0dd8e3f9", "name": "WhatsApp - Customer Message", "type": "n8n-nodes-base.whatsAppTrigger", "typeVersion": 1, "position": [-688, 288], "webhookId": "8680d678-e57c-49fa-8bd3-2617ec2102c7"}, {"parameters": {"pollTimes": {"item": [{"mode": "everyMinute"}]}, "simple": false, "filters": {"readStatus": "unread"}, "options": {}}, "id": "d02e0e7a-6888-483e-9518-42eab11dc52c", "name": "Gmail - Customer Email", "type": "n8n-nodes-base.gmailTrigger", "typeVersion": 1.4, "position": [-688, 400]}, {"parameters": {}, "id": "f7d9a660-f878-4290-9c61-76673164790f", "name": "On Any Error", "type": "n8n-nodes-base.errorTrigger", "typeVersion": 1, "position": [-688, -288]}, {"parameters": {"select": "channel", "channelId": {"__rl": true, "mode": "name", "value": "<__PLACEHOLDER_VALUE__e.g. ops-alerts__>", "cachedResultName": "From placeholder"}, "text": "={{ \"🚨 Lead Qualification workflow failed: \" + $json.execution.error.message + \" (node: \" + $json.execution.error.node.name + \")\" }}", "otherOptions": {}}, "id": "d462d919-4ce7-44de-833c-26982970d56d", "name": "Notify Ops (Slack)", "type": "n8n-nodes-base.slack", "typeVersion": 2.5, "position": [-416, -288], "webhookId": "37631df3-1736-45fd-a5f5-f8a451b68c6f"}, {"parameters": {"operation": "append", "documentId": {"__rl": true, "mode": "id", "value": "<__PLACEHOLDER_VALUE__same Google Sheet ID as Config below__>", "cachedResultName": "From Config"}, "sheetName": {"__rl": true, "mode": "name", "value": "ErrorLog", "cachedResultName": "ErrorLog"}, "columns": {"mappingMode": "defineBelow", "value": {"Timestamp": "={{ $now.toISO() }}", "FailedNode": "={{ $json.execution.error.node.name }}", "ErrorMessage": "={{ $json.execution.error.message }}"}, "schema": [{"id": "Timestamp", "displayName": "Timestamp", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "FailedNode", "displayName": "FailedNode", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}, {"id": "ErrorMessage", "displayName": "ErrorMessage", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": false}]}, "options": {}}, "id": "b7dd3167-2ff7-4c65-a86b-ca20ac2d4309", "name": "Log Error to Sheet", "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [-160, -288], "retryOnFail": true, "maxTries": 3, "waitBetweenTries": 2000}, {"parameters": {"content": "## šŸ“Š AI Lead Qualification & CRM Routing\n\nCatches inquiries from 4 channels (Website form, Telegram, WhatsApp, Email) → Claude reads the message once and both qualifies it AND drafts a reply in the same call → Hot leads alert your sales team instantly; everything else gets an automatic, channel-aware, personalized reply.\n\n**Design choice:** one AI call does double duty (qualification + draft reply) rather than two separate calls — the draft is simply unused on the Hot Lead path, saving an API call on every request.", "height": 340, "width": 560, "color": 7}, "id": "bd4487d2-58f4-4412-bf67-cde7ec3babd8", "name": "Overview", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [-1328, -336]}, {"parameters": {"content": "## 🧭 How to Use This\n\n**Setup (once):**\n1. Create a Google Sheet with 2 tabs: \"Leads\" (columns: Timestamp, CustomerName, Channel, ContactInfo, Message, Qualification, Urgency, Sentiment, Reason) and \"ErrorLog\" (Timestamp, FailedNode, ErrorMessage)\n2. Fill in Config: Sheet ID, tab name, your reply-from email, Slack channel for hot leads, Telegram chat ID for your SALES TEAM (not customers), WhatsApp phone number ID\n3. Connect accounts on whichever trigger(s) you actually use — you don't need all 4; disable triggers for channels you don't use\n4. Anthropic (Claude) is already connected\n5. Activate\n\n**Daily use:** nothing — it runs itself. Hot leads ping Slack + Telegram immediately; standard inquiries get an instant reply and a logged record.", "height": 420, "width": 560, "color": 2}, "id": "38b5450c-a41d-4c7e-ba10-d7346a16a203", "name": "How to Use This", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [-1376, 272]}, {"parameters": {"content": "## 0ļøāƒ£ Error Handling & Logging\n\nTwo layers, as requested:\n1. **Hard failures** (API down, bad credential) → On Any Error → Slack alert + a row in the ErrorLog tab, so nothing is lost silently.\n2. **Soft failures** (Claude responds but not valid JSON) → caught inline by Parse AI Output, defaults safely to \"standard\" handling rather than crashing, and still gets logged.\n\nāš ļø Settings → Error Workflow → select this workflow itself.", "height": 320, "width": 560, "color": 3}, "id": "09b87bb5-60e0-4156-aa18-76009197deb7", "name": "Error Handling", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [-688, -688]}, {"parameters": {"content": "## ⚔ Why 4 Triggers Share One Path\n\nEach channel has a completely different payload shape (Telegram vs WhatsApp's Meta webhook format vs Gmail vs a generic form). \"Normalize Inquiry Data\" is the ONLY place that knows about these differences — it converts all 4 into one common shape (channel, customerName, contact info, messageText). Every node after it only ever deals with that one clean shape.\n\nWebsite form submissions are treated as \"email\" channel for replies, since a contact form almost always collects an email address.", "height": 340, "width": 640, "color": 4}, "id": "1278e0ef-547f-4d28-9f49-4b546769afe8", "name": "Why 4 Triggers Share One Path", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [48, -224]}, {"parameters": {"content": "## šŸ”„ Hot Lead Path\n\nQualified as \"hot\" only for clear buying intent, budget/timeline mentioned, or explicit urgency — not just enthusiasm. Logs to CRM, then alerts BOTH Slack and Telegram (redundant on purpose — a hot lead is too valuable to risk missing because one channel had an issue).", "height": 220, "width": 500, "color": 6}, "id": "15d79677-e90f-4f41-9d8c-39e72db907a3", "name": "Hot Lead Path", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [880, -368]}, {"parameters": {"content": "## šŸ’¬ Standard Reply Path\n\nLogs the inquiry, then routes the reply back through the SAME channel it arrived on — Telegram stays in Telegram, WhatsApp stays in WhatsApp, email/website form gets an email. If a channel value is ever unrecognized, it's logged instead of silently dropped or sent to the wrong place.", "height": 220, "width": 640, "color": 5}, "id": "b07bfc43-027b-4657-8a07-6cdcf1909731", "name": "Standard Reply Path", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [656, 688]}], "connections": {"Webhook - Website Inquiry": {"main": [[{"node": "Config (edit me)", "type": "main", "index": 0}]]}, "Config (edit me)": {"main": [[{"node": "Normalize Inquiry Data", "type": "main", "index": 0}]]}, "Normalize Inquiry Data": {"main": [[{"node": "Analyze Lead (Claude)", "type": "main", "index": 0}]]}, "Analyze Lead (Claude)": {"main": [[{"node": "Parse AI Output", "type": "main", "index": 0}]]}, "Parse AI Output": {"main": [[{"node": "Is Hot Lead?", "type": "main", "index": 0}]]}, "Is Hot Lead?": {"main": [[{"node": "Log Hot Lead to CRM", "type": "main", "index": 0}], [{"node": "Log Standard Inquiry", "type": "main", "index": 0}]]}, "Log Hot Lead to CRM": {"main": [[{"node": "Alert Sales (Slack)", "type": "main", "index": 0}]]}, "Alert Sales (Slack)": {"main": [[{"node": "Alert Sales (Telegram)", "type": "main", "index": 0}]]}, "Log Standard Inquiry": {"main": [[{"node": "Route Reply by Channel", "type": "main", "index": 0}]]}, "Route Reply by Channel": {"main": [[{"node": "Reply via Telegram", "type": "main", "index": 0}], [{"node": "Reply via WhatsApp", "type": "main", "index": 0}], [{"node": "Reply via Email", "type": "main", "index": 0}], [{"node": "Log Unknown Channel (No Reply Sent)", "type": "main", "index": 0}]]}, "Telegram - Customer Message": {"main": [[{"node": "Config (edit me)", "type": "main", "index": 0}]]}, "WhatsApp - Customer Message": {"main": [[{"node": "Config (edit me)", "type": "main", "index": 0}]]}, "Gmail - Customer Email": {"main": [[{"node": "Config (edit me)", "type": "main", "index": 0}]]}, "On Any Error": {"main": [[{"node": "Notify Ops (Slack)", "type": "main", "index": 0}]]}, "Notify Ops (Slack)": {"main": [[{"node": "Log Error to Sheet", "type": "main", "index": 0}]]}}, "pinData": {}, "meta": {"aiBuilderAssisted": true, "builderVariant": "mcp", "instanceId": "a8610f00e1fe2a5bce768598a7e63848df427bd4d8770071107d6f6e694414de"}}

Paste into n8n via Import from Clipboard.

Scroll to Top