{
  "id": "jPodSrGZLNPzZ2cy",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "AI WhatsApp Sales Closer Bot",
  "tags": [
    {
      "id": "DiwTzDkTaFSX5bEX",
      "name": "recruitment",
      "createdAt": "2026-05-11T13:01:00.870Z",
      "updatedAt": "2026-05-11T13:01:00.870Z"
    },
    {
      "id": "z6mxJFPmp5Jq0Lnu",
      "name": "whatsapp",
      "createdAt": "2026-05-11T13:01:00.955Z",
      "updatedAt": "2026-05-11T13:01:00.955Z"
    },
    {
      "id": "s4xffeURdnx7oinT",
      "name": "hr-automation",
      "createdAt": "2026-05-11T13:01:00.986Z",
      "updatedAt": "2026-05-11T13:01:00.986Z"
    },
    {
      "id": "fmYbvcTTTlbTp6aj",
      "name": "ai-powered",
      "createdAt": "2026-05-11T13:01:00.986Z",
      "updatedAt": "2026-05-11T13:01:00.986Z"
    },
    {
      "id": "aVLSLG3iHePWjrQE",
      "name": "sales",
      "createdAt": "2026-05-11T13:05:19.233Z",
      "updatedAt": "2026-05-11T13:05:19.233Z"
    },
    {
      "id": "F89H8wlybgmonk5J",
      "name": "ai-bot",
      "createdAt": "2026-05-11T13:05:19.248Z",
      "updatedAt": "2026-05-11T13:05:19.248Z"
    },
    {
      "id": "9lOfmvXjZotSSkKk",
      "name": "lead-qualification",
      "createdAt": "2026-05-11T13:05:19.238Z",
      "updatedAt": "2026-05-11T13:05:19.238Z"
    }
  ],
  "nodes": [
    {
      "id": "d3fbe3c4-2541-4591-af90-bf54a3b6b13a",
      "name": "Sticky Note - Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        0
      ],
      "parameters": {
        "width": 980,
        "height": 960,
        "content": "## AI WhatsApp Sales Closer Bot\n\nThis workflow automatically qualifies leads, answers FAQs, and books sales calls via WhatsApp using AI-powered conversation logic.\n\n### Who's it for\n\u2022 Sales teams handling high inbound message volume\n\u2022 SaaS / service businesses using WhatsApp for outreach\n\u2022 Agencies automating lead qualification pipelines\n\n### How it works\n1. WhatsApp message arrives via webhook (Twilio/Meta Cloud API)\n2. Conversation state is loaded from memory/sheet\n3. AI qualifies lead (Budget, Authority, Need, Timeline)\n4. Bot answers FAQs automatically\n5. Qualified leads get calendar booking link / slot picker\n6. All activity is logged to Google Sheets CRM\n7. Hot leads trigger Slack/email alert to sales rep\n\n### How to set up\n1. Import this workflow into n8n\n2. Set up credentials: Twilio/Meta WhatsApp, Google Sheets, Google Calendar, OpenAI\n3. Update YOUR_SHEET_ID and YOUR_CALENDAR_ID placeholders\n4. Update your product FAQ knowledge base in the AI node\n5. Activate workflow and configure your WhatsApp webhook URL\n\n### Requirements\n\u2022 Twilio WhatsApp API or Meta Cloud API\n\u2022 Google Sheets (CRM tracker)\n\u2022 Google Calendar or Calendly\n\u2022 OpenAI API key\n\u2022 n8n instance (cloud or self-hosted)\n\n### Customization\n\u2022 Edit BANT scoring thresholds in JS node\n\u2022 Update FAQ knowledge base in AI system prompt\n\u2022 Modify booking time slots in calendar node\n\u2022 Adjust Slack alert conditions"
      },
      "typeVersion": 1
    },
    {
      "id": "3f408032-4ab3-4d60-8a8a-cfc8ea7d193c",
      "name": "Sticky Note - Intake",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1104,
        240
      ],
      "parameters": {
        "color": 4,
        "width": 780,
        "height": 520,
        "content": "## 1. Intake & State Load"
      },
      "typeVersion": 1
    },
    {
      "id": "e7c28836-9c41-41f8-a458-2431470eb6bb",
      "name": "Sticky Note - Intent",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1920,
        160
      ],
      "parameters": {
        "color": 3,
        "width": 852,
        "height": 620,
        "content": "## 2. Intent Detection & Lead Scoring"
      },
      "typeVersion": 1
    },
    {
      "id": "29201094-f5ff-4721-a097-e0d0c94306ab",
      "name": "Sticky Note - AI Response",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2832,
        160
      ],
      "parameters": {
        "color": 4,
        "width": 820,
        "height": 640,
        "content": "## 3. AI Response & Booking"
      },
      "typeVersion": 1
    },
    {
      "id": "10573369-9b91-4bbb-bf6c-3c7b34cad857",
      "name": "Sticky Note - Send Track",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3712,
        208
      ],
      "parameters": {
        "color": 3,
        "width": 1012,
        "height": 580,
        "content": "## 4. Send & Track"
      },
      "typeVersion": 1
    },
    {
      "id": "777ea74f-90b7-4629-82c0-800a60d6041c",
      "name": "Webhook - WhatsApp Inbound",
      "type": "n8n-nodes-base.webhook",
      "position": [
        1264,
        400
      ],
      "parameters": {
        "path": "whatsapp-inbound",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 1.1
    },
    {
      "id": "47ce6d03-f271-4a6a-89bd-6460930841df",
      "name": "Normalize Message Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        1504,
        400
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "name": "phoneNumber",
              "type": "string",
              "value": "={{ $json.From || $json.body?.From || $json.WaId || $json.contacts?.[0]?.wa_id }}"
            },
            {
              "name": "incomingMessage",
              "type": "string",
              "value": "={{ $json.Body || $json.body?.Body || $json.messages?.[0]?.text?.body || $json.body }}"
            },
            {
              "name": "messageId",
              "type": "string",
              "value": "={{ $json.MessageSid || $json.messages?.[0]?.id || $now.toMillis().toString() }}"
            },
            {
              "name": "senderName",
              "type": "string",
              "value": "={{ $json.ProfileName || $json.contacts?.[0]?.profile?.name || 'Prospect' }}"
            },
            {
              "name": "timestamp",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            },
            {
              "name": "channel",
              "type": "string",
              "value": "WhatsApp"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "f94351b4-644a-44d7-8863-4b5f2391ee8a",
      "name": "Load Lead State from Sheet",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1728,
        400
      ],
      "parameters": {
        "url": "=https://sheets.googleapis.com/v4/spreadsheets/YOUR_SHEET_ID/values/Leads!A:J?majorDimension=ROWS",
        "options": {},
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleSheetsOAuth2Api"
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "4c66e12a-940a-4c14-95d0-9acda08cb7ef",
      "name": "JS - Resolve Lead State",
      "type": "n8n-nodes-base.code",
      "position": [
        1952,
        400
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Find existing lead record by phone number\nconst phone = $('Normalize Message Fields').item.json.phoneNumber;\nconst sheetData = $input.item.json;\n\nlet existingLead = null;\nlet conversationStage = 'new';\nlet bantScore = { budget: false, authority: false, need: false, timeline: false };\nlet conversationHistory = [];\nlet leadScore = 0;\n\nif (sheetData.values && sheetData.values.length > 1) {\n  const rows = sheetData.values.slice(1); // skip header\n  const found = rows.find(row => row[1] === phone);\n  if (found) {\n    existingLead = {\n      id: found[0],\n      phone: found[1],\n      name: found[2],\n      stage: found[3] || 'new',\n      leadScore: parseInt(found[4]) || 0,\n      bantBudget: found[5] === 'TRUE',\n      bantAuthority: found[6] === 'TRUE',\n      bantNeed: found[7] === 'TRUE',\n      bantTimeline: found[8] === 'TRUE',\n      conversationHistory: found[9] ? JSON.parse(found[9]) : []\n    };\n    conversationStage = existingLead.stage;\n    bantScore = {\n      budget: existingLead.bantBudget,\n      authority: existingLead.bantAuthority,\n      need: existingLead.bantNeed,\n      timeline: existingLead.bantTimeline\n    };\n    conversationHistory = existingLead.conversationHistory;\n    leadScore = existingLead.leadScore;\n  }\n}\n\nconst message = $('Normalize Message Fields').item.json.incomingMessage;\n\n// Add incoming message to history\nconversationHistory.push({\n  role: 'user',\n  content: message,\n  ts: new Date().toISOString()\n});\n\n// Cap history at last 20 messages to stay within token limits\nif (conversationHistory.length > 20) {\n  conversationHistory = conversationHistory.slice(-20);\n}\n\nreturn {\n  json: {\n    ...$('Normalize Message Fields').item.json,\n    isNewLead: !existingLead,\n    conversationStage,\n    bantScore,\n    leadScore,\n    conversationHistory,\n    existingLead\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "e03347d4-08b6-4693-a1eb-6f36ddeb0787",
      "name": "Python - Detect Intent",
      "type": "n8n-nodes-base.code",
      "position": [
        2192,
        400
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "language": "python"
      },
      "typeVersion": 2
    },
    {
      "id": "ce92a186-75c4-4c80-86ed-5779b7534fd5",
      "name": "Route - Book Call?",
      "type": "n8n-nodes-base.filter",
      "position": [
        2432,
        304
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.intent }}",
              "rightValue": "book_call"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "7b92ceb9-885a-4da0-a3ab-69904b165450",
      "name": "Route - FAQ?",
      "type": "n8n-nodes-base.filter",
      "position": [
        2432,
        464
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.intent }}",
              "rightValue": "faq"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "5cab22e2-d764-4480-829f-7bc0eb14a69e",
      "name": "Route - Hot Lead?",
      "type": "n8n-nodes-base.filter",
      "position": [
        2432,
        624
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "conditions": [
            {
              "operator": {
                "type": "number",
                "operation": "gte"
              },
              "leftValue": "={{ $json.leadScore }}",
              "rightValue": 75
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "53406cc1-40b3-4c52-b9f6-0d0b631cd4a2",
      "name": "Wait - Rate Limit",
      "type": "n8n-nodes-base.wait",
      "position": [
        2672,
        464
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "4e49f5d6-ad28-40b1-9fcb-ed4a5a060d41",
      "name": "AI - Sales Closer Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        3088,
        400
      ],
      "parameters": {
        "text": "={{ JSON.stringify($json.conversationHistory.map(m => ({role: m.role, content: m.content}))) }}",
        "options": {
          "systemMessage": "=You are Alex, a friendly and professional sales assistant for [YOUR COMPANY NAME]. Your goal is to qualify leads using the BANT framework, answer product questions, and book sales calls.\n\n## Your Personality\n- Warm, conversational, and consultative \u2014 never pushy\n- Use the prospect's first name when known\n- Keep messages short (under 80 words) \u2014 this is WhatsApp, not email\n- Use line breaks and emojis sparingly for readability\n- Never use more than 3 emojis per message\n\n## BANT Qualification (collect one at a time, naturally)\n- **Budget**: Do they have the budget? ($500\u2013$5000/mo range)\n- **Authority**: Are they the decision maker?\n- **Need**: What problem are they solving?\n- **Timeline**: When do they want to start?\n\nCurrent BANT status: {{ JSON.stringify($json.bantScore) }}\nCurrent lead score: {{ $json.leadScore }}/100\nConversation stage: {{ $json.conversationStage }}\nProspect name: {{ $json.senderName }}\n\n## FAQ Knowledge Base\n**What does your product do?**\nWe help [TARGET PERSONA] achieve [OUTCOME] through [METHOD]. Typical clients see [RESULT] within [TIMEFRAME].\n\n**How much does it cost?**\nOur plans range from $X to $Y/month depending on team size and features. We also offer custom enterprise pricing.\n\n**Do you offer a free trial?**\nYes! We offer a 14-day free trial, no credit card required.\n\n**How long does onboarding take?**\nMost clients are fully onboarded within 1\u20132 weeks. We provide dedicated support.\n\n**What integrations do you support?**\nWe integrate with Salesforce, HubSpot, Slack, Google Workspace, and 50+ tools via Zapier/n8n.\n\n**Is my data secure?**\nAbsolutely. We are SOC 2 Type II certified and GDPR compliant.\n\n## Booking a Call\nWhen a lead is qualified (score \u2265 75) or asks to book, say:\n\"Great! I'd love to connect you with our team. Here's a link to pick a time that works for you: [CALENDLY_LINK]\"\n\n## Guardrails\n- Never make up pricing or features not listed above\n- If you don't know the answer, say: \"Great question! Let me connect you with a specialist who can answer that.\"\n- Never discuss competitors\n- If the user seems frustrated, offer to escalate to a human immediately"
        },
        "promptType": "define"
      },
      "typeVersion": 1.6
    },
    {
      "id": "a171ada3-410b-44e1-b019-6026dbe0c554",
      "name": "OpenAI GPT-4.1-mini",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        3008,
        608
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {
          "temperature": 0.7
        },
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "d0f78a88-a412-4eb8-aa09-926d3207606b",
      "name": "JS - Score & Format Reply",
      "type": "n8n-nodes-base.code",
      "position": [
        3424,
        400
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const item = $input.item.json;\nconst aiResponse = item.output || item.response || item.text || 'Thank you for reaching out! How can I help you today?';\n\n// Update BANT score based on AI output hints\nconst bantScore = item.bantScore || { budget: false, authority: false, need: false, timeline: false };\nconst responseLower = aiResponse.toLowerCase();\n\n// Simple keyword-based BANT detection (AI handles the nuanced conversation)\nif (responseLower.includes('[budget_confirmed]')) bantScore.budget = true;\nif (responseLower.includes('[authority_confirmed]')) bantScore.authority = true;\nif (responseLower.includes('[need_confirmed]')) bantScore.need = true;\nif (responseLower.includes('[timeline_confirmed]')) bantScore.timeline = true;\n\n// Calculate lead score (25 points per BANT element)\nconst newLeadScore = Object.values(bantScore).filter(Boolean).length * 25;\n\n// Determine conversation stage\nlet stage = item.conversationStage || 'new';\nif (newLeadScore === 0) stage = 'awareness';\nelse if (newLeadScore <= 25) stage = 'qualifying';\nelse if (newLeadScore <= 75) stage = 'engaged';\nelse stage = 'hot_lead';\n\n// Add AI reply to history\nconst updatedHistory = [...(item.conversationHistory || []), {\n  role: 'assistant',\n  content: aiResponse,\n  ts: new Date().toISOString()\n}];\n\n// Clean reply text (remove internal tags)\nconst cleanReply = aiResponse\n  .replace(/\\[budget_confirmed\\]/gi, '')\n  .replace(/\\[authority_confirmed\\]/gi, '')\n  .replace(/\\[need_confirmed\\]/gi, '')\n  .replace(/\\[timeline_confirmed\\]/gi, '')\n  .trim();\n\nreturn {\n  json: {\n    ...item,\n    replyText: cleanReply,\n    bantScore,\n    leadScore: newLeadScore,\n    conversationStage: stage,\n    conversationHistory: updatedHistory,\n    updatedAt: new Date().toISOString().split('T')[0]\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "54fa865e-c43c-4e58-a802-738748ac320d",
      "name": "Send WhatsApp Reply (Twilio)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3760,
        320
      ],
      "parameters": {
        "url": "https://api.twilio.com/2010-04-01/Accounts/YOUR_TWILIO_ACCOUNT_SID/Messages.json",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "specifyBody": "formUrlEncoded",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "twilioApi"
      },
      "credentials": {
        "twilioApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "07b30862-a4da-4689-8b2c-c9143b59e612",
      "name": "Upsert Lead in Google Sheets",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3776,
        576
      ],
      "parameters": {
        "url": "https://sheets.googleapis.com/v4/spreadsheets/YOUR_SHEET_ID/values/Leads!A1:append?valueInputOption=USER_ENTERED&insertDataOption=INSERT_ROWS",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"values\": [[\n    \"{{ $json.isNewLead ? $now.toMillis().toString() : ($json.existingLead?.id || $now.toMillis().toString()) }}\",\n    \"{{ $json.phoneNumber }}\",\n    \"{{ $json.senderName }}\",\n    \"{{ $json.conversationStage }}\",\n    \"{{ $json.leadScore }}\",\n    \"{{ $json.bantScore?.budget }}\",\n    \"{{ $json.bantScore?.authority }}\",\n    \"{{ $json.bantScore?.need }}\",\n    \"{{ $json.bantScore?.timeline }}\",\n    {{ JSON.stringify(JSON.stringify($json.conversationHistory)) }},\n    \"{{ $json.updatedAt }}\",\n    \"{{ $json.channel }}\"\n  ]]\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleSheetsOAuth2Api"
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "c0fcac11-cafa-416b-9272-f582bbd618cc",
      "name": "Check - Hot Lead Alert?",
      "type": "n8n-nodes-base.filter",
      "position": [
        4000,
        400
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "conditions": [
            {
              "operator": {
                "type": "number",
                "operation": "gte"
              },
              "leftValue": "={{ $json.leadScore }}",
              "rightValue": 75
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "191427d9-bb14-42a9-8ed2-b9be6bbd6c99",
      "name": "Slack - Hot Lead Alert",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4240,
        320
      ],
      "parameters": {
        "url": "https://slack.com/api/chat.postMessage",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"channel\": \"#sales-alerts\",\n  \"text\": \"\ud83d\udd25 *Hot Lead Alert!*\",\n  \"blocks\": [\n    {\n      \"type\": \"section\",\n      \"text\": {\n        \"type\": \"mrkdwn\",\n        \"text\": \"\ud83d\udd25 *Hot Lead Qualified!*\\n*Name:* {{ $json.senderName }}\\n*Phone:* {{ $json.phoneNumber }}\\n*Lead Score:* {{ $json.leadScore }}/100\\n*Stage:* {{ $json.conversationStage }}\\n*BANT:* Budget={{ $json.bantScore?.budget }} | Authority={{ $json.bantScore?.authority }} | Need={{ $json.bantScore?.need }} | Timeline={{ $json.bantScore?.timeline }}\\n\\n_Last message:_ {{ $json.incomingMessage?.substring(0, 200) }}\"\n      }\n    },\n    {\n      \"type\": \"actions\",\n      \"elements\": [\n        {\n          \"type\": \"button\",\n          \"text\": { \"type\": \"plain_text\", \"text\": \"View in CRM\" },\n          \"url\": \"https://docs.google.com/spreadsheets/d/YOUR_SHEET_ID\",\n          \"style\": \"primary\"\n        }\n      ]\n    }\n  ]\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "slackApi"
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "7fcdb351-5a31-43aa-b3d7-0eea271e5dec",
      "name": "Book Call in Google Calendar",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4240,
        480
      ],
      "parameters": {
        "url": "https://www.googleapis.com/calendar/v3/calendars/YOUR_CALENDAR_ID/events",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"summary\": \"Sales Call - {{ $json.senderName }}\",\n  \"description\": \"WhatsApp Lead\\nPhone: {{ $json.phoneNumber }}\\nLead Score: {{ $json.leadScore }}/100\\nBANT: Budget={{ $json.bantScore?.budget }}, Authority={{ $json.bantScore?.authority }}, Need={{ $json.bantScore?.need }}, Timeline={{ $json.bantScore?.timeline }}\\n\\nConversation Summary: Auto-booked via WhatsApp Sales Bot\",\n  \"start\": {\n    \"dateTime\": \"{{ $now.plus({days: 2}).startOf('day').plus({hours: 10}).toISO() }}\",\n    \"timeZone\": \"America/New_York\"\n  },\n  \"end\": {\n    \"dateTime\": \"{{ $now.plus({days: 2}).startOf('day').plus({hours: 10, minutes: 30}).toISO() }}\",\n    \"timeZone\": \"America/New_York\"\n  },\n  \"attendees\": [],\n  \"reminders\": {\n    \"useDefault\": false,\n    \"overrides\": [\n      { \"method\": \"email\", \"minutes\": 1440 },\n      { \"method\": \"popup\", \"minutes\": 30 }\n    ]\n  }\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleCalendarOAuth2Api"
      },
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "932736bb-ae5c-4b72-a08d-e207840c7483",
      "name": "Webhook Response - 200 OK",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        4480,
        400
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ status: 'ok', messageId: $json.messageId }) }}"
      },
      "typeVersion": 1.1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "a3e24126-aa3b-4aaa-a5ad-ca1d0f404091",
  "connections": {
    "Route - FAQ?": {
      "main": [
        [
          {
            "node": "Wait - Rate Limit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route - Hot Lead?": {
      "main": [
        [
          {
            "node": "Wait - Rate Limit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait - Rate Limit": {
      "main": [
        [
          {
            "node": "AI - Sales Closer Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route - Book Call?": {
      "main": [
        [
          {
            "node": "Wait - Rate Limit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI GPT-4.1-mini": {
      "ai_languageModel": [
        [
          {
            "node": "AI - Sales Closer Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Python - Detect Intent": {
      "main": [
        [
          {
            "node": "Route - Book Call?",
            "type": "main",
            "index": 0
          },
          {
            "node": "Route - FAQ?",
            "type": "main",
            "index": 0
          },
          {
            "node": "Route - Hot Lead?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack - Hot Lead Alert": {
      "main": [
        [
          {
            "node": "Webhook Response - 200 OK",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI - Sales Closer Agent": {
      "main": [
        [
          {
            "node": "JS - Score & Format Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check - Hot Lead Alert?": {
      "main": [
        [
          {
            "node": "Slack - Hot Lead Alert",
            "type": "main",
            "index": 0
          },
          {
            "node": "Book Call in Google Calendar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "JS - Resolve Lead State": {
      "main": [
        [
          {
            "node": "Python - Detect Intent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Message Fields": {
      "main": [
        [
          {
            "node": "Load Lead State from Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "JS - Score & Format Reply": {
      "main": [
        [
          {
            "node": "Send WhatsApp Reply (Twilio)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Upsert Lead in Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load Lead State from Sheet": {
      "main": [
        [
          {
            "node": "JS - Resolve Lead State",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook - WhatsApp Inbound": {
      "main": [
        [
          {
            "node": "Normalize Message Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Book Call in Google Calendar": {
      "main": [
        [
          {
            "node": "Webhook Response - 200 OK",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send WhatsApp Reply (Twilio)": {
      "main": [
        [
          {
            "node": "Check - Hot Lead Alert?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upsert Lead in Google Sheets": {
      "main": [
        [
          {
            "node": "Check - Hot Lead Alert?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}