{
  "nodes": [
    {
      "id": "db7f15db-71af-4ad5-9148-a1c855a58d09",
      "name": "Main",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -496,
        2032
      ],
      "parameters": {
        "width": 944,
        "height": 1000,
        "content": "# \ud83e\udd16 WhatsApp AI Chatbot with Evolution API + Notion CRM\n\n**Who's it for**\nService businesses, agencies, and SMBs that get inbound WhatsApp messages and want a 24/7 AI assistant \u2014 without paying for third-party chatbot platforms.\n\n## What it does\nTurns your WhatsApp Business number into an AI-powered customer assistant that handles **text + voice messages**, filters out automatic replies from other bots, looks up leads in your **Notion CRM**, generates contextual responses with **Claude**, escalates to **Telegram** when human attention is needed, and keeps your CRM updated automatically.\n\n## How it works\n1. **Receive** \u2014 Evolution API webhook receives the WhatsApp message\n2. **Filter** \u2014 Voice messages are transcribed (Whisper); auto-replies are silently discarded\n3. **Lookup** \u2014 The lead is found in Notion CRM (or created if new). If their bot status is \"Paused\", the workflow stops\n4. **Generate** \u2014 Claude builds a contextual response using your knowledge base + lead context\n5. **Send & Log** \u2014 Reply is sent via Evolution API, the conversation is logged to Google Sheets\n6. **Escalate** \u2014 When a human is needed, the bot pauses itself for that contact and alerts you on Telegram\n\n## Setup checklist\n- \u2705 Evolution API instance (URL + API key)\n- \u2705 Anthropic API key (Claude Haiku)\n- \u2705 OpenAI API key (Whisper, optional for voice)\n- \u2705 Google Sheets OAuth (knowledge base + conversation log)\n- \u2705 Notion API (CRM lookup + updates)\n- \u2705 Telegram Bot token + Chat ID\n\n## Templates included with this workflow\n\ud83d\udce5 [Google Sheets knowledge base template](https://docs.google.com/spreadsheets/d/1O1hSVLTL-yfugolm_y22-67lV0i59R0sRM0UPPq2Dlk/edit?usp=sharing) \u2014 free\n\ud83d\uddc3\ufe0f [Notion CRM template](https://cifral.gumroad.com/l/notion-crm-template) \u2014 pre-configured for this workflow"
      },
      "typeVersion": 1
    },
    {
      "id": "cc7eb408-026c-42f5-8094-ede3ffef3b6f",
      "name": "Section 1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        528,
        2020
      ],
      "parameters": {
        "color": 5,
        "width": 1856,
        "height": 280,
        "content": "## \ud83d\udce5 STEP 1 \u2014 Receive & Filter Messages\n\nReceives WhatsApp messages from Evolution API, normalizes the payload, transcribes voice notes, and filters out automatic replies (out-of-office bots, delivery confirmations).\n\n**Evolution webhook setup:**\n- URL: `https://YOUR-N8N-DOMAIN/webhook/evo-inbound`\n- Events: MESSAGES_UPSERT, CONNECTION_UPDATE\n- webhookByEvents: OFF\n- Webhook Base64: ON (required for voice support)\n\nIf you don't need voice support, disable the audio branch."
      },
      "typeVersion": 1
    },
    {
      "id": "fd31f5c5-25c3-400e-a828-189f11af8e2c",
      "name": "Section 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2448,
        2016
      ],
      "parameters": {
        "color": 4,
        "width": 592,
        "height": 424,
        "content": "## \ud83d\uddc3\ufe0f STEP 2 \u2014 Lookup Lead in CRM\n\nChecks your Notion CRM for the incoming phone number:\n- **Known lead** \u2192 reads bot status + lead context (personalization note, pain point)\n- **Unknown lead** \u2192 flagged for creation later\n- If `WA ChatBot Status` = \"Paused\", the workflow stops silently \u2014 letting you take over manually\n\n---\n\n### \ud83d\uddc3\ufe0f Need a pre-built CRM?\n\nGet the **Notion CRM template** already configured to work with this workflow:\n\u2192 [cifral.gumroad.com/l/notion-crm-template](https://cifral.gumroad.com/l/notion-crm-template)\n\nIncludes all required fields, pre-built views for WhatsApp + email pipelines, and the lead schema this workflow expects."
      },
      "typeVersion": 1
    },
    {
      "id": "8c5ad581-d373-41e5-b9cf-a8e706ea7fbc",
      "name": "Section 3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3168,
        2032
      ],
      "parameters": {
        "color": 7,
        "width": 1696,
        "height": 328,
        "content": "## \ud83e\udde0 STEP 3 \u2014 AI Processing\n\nThe AI pipeline:\n1. **Information Extractor** detects intent, sentiment, urgency, and whether a human is needed\n2. **Knowledge Base** (Google Sheets) provides business info, products, and FAQs\n3. **Build Context** combines all this with the lead context from Notion\n4. **Wait 10s** to avoid replying mid-typing\n5. **Generate Response** uses Claude Haiku with conversation memory (last 10 messages)\n6. **Format Cleaner** strips markdown for clean WhatsApp rendering\n\n\ud83d\udcda [Google Sheets template](https://docs.google.com/spreadsheets/d/1O1hSVLTL-yfugolm_y22-67lV0i59R0sRM0UPPq2Dlk/edit?usp=sharing) \u2014 3 tabs required: `Business_Info`, `Products`, `FAQs`\n\nCustomize the **system prompt** in `Generate Response` to match your business voice."
      },
      "typeVersion": 1
    },
    {
      "id": "6e5922a6-a07b-4e6b-88f1-595a5fdcb329",
      "name": "Section 4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4912,
        2032
      ],
      "parameters": {
        "color": 6,
        "width": 560,
        "height": 312,
        "content": "## \ud83d\udce4 STEP 4 \u2014 Send Reply & Log\n\nSends the AI response via Evolution API and logs the full conversation to your `Conversation_Log` Google Sheets tab.\n\n**Send WhatsApp Reply node:**\n- URL: `https://YOUR-EVOLUTION-DOMAIN/message/sendText/YOUR-INSTANCE-NAME`\n- Header `apikey`: your Evolution API instance key\n\nThe instance name is read dynamically from the incoming webhook payload."
      },
      "typeVersion": 1
    },
    {
      "id": "9003d60d-c452-4a8d-9e51-3e226d51e384",
      "name": "Section 5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5520,
        2048
      ],
      "parameters": {
        "color": 2,
        "width": 1584,
        "height": 312,
        "content": "## \ud83d\udea8 STEP 5 \u2014 Handoff & CRM Update\n\nWhen the AI detects buying intent, frustration, or a question it can't answer:\n1. Sends a **Telegram alert** with full conversation context\n2. Sets `WA ChatBot Status = Paused` in Notion (bot stops auto-replying)\n3. Updates the lead: `Last Contact`, `WA Status` (Positive/Negative/Neutral)\n\nIf the contact is new, a lead is **created automatically** with phone number and `Lead Source = WhatsApp`.\n\n**To resume the bot for a paused contact:** manually set `WA ChatBot Status` back to `Active` in Notion."
      },
      "typeVersion": 1
    },
    {
      "id": "c82063b5-3fb1-4e9f-ba30-74e53b7f177a",
      "name": "Evolution Webhook1",
      "type": "n8n-nodes-base.webhook",
      "notes": "SETUP: In Evolution Manager \u2192 your instance \u2192 Webhook\nURL: https://YOUR-N8N-DOMAIN/webhook/evo-inbound\n\nEnable events: MESSAGES_UPSERT, CONNECTION_UPDATE, QRCODE_UPDATED\nwebhookByEvents: OFF \u2705 (keeps URL path fixed \u2014 CRITICAL)\nWebhook Base64: ON \u2705 (required for voice message support)",
      "position": [
        576,
        2480
      ],
      "parameters": {
        "path": "evo-inbound",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "0a39f3d7-fb83-4112-9f3a-590fe0c664d9",
      "name": "Detect Message Type1",
      "type": "n8n-nodes-base.code",
      "position": [
        800,
        2480
      ],
      "parameters": {
        "jsCode": "// ============================================\n// EVOLUTION API PAYLOAD NORMALIZER\n// Evolution sends: { event, instance, data: { key, pushName, message, messageType, ... } }\n// With Base64 enabled, media comes as data.base64 or data.media\n// ============================================\nconst payload = items[0].json.body;\n\n// Solo procesamos mensajes entrantes (no los que enviamos nosotros)\nconst event = payload.event || '';\nif (event !== 'messages.upsert') {\n  return [{ json: { skip: true, reason: 'not a message event', event } }];\n}\n\nconst data = payload.data || {};\nconst key = data.key || {};\n\n// Ignorar mensajes enviados por nosotros\nif (key.fromMe === true) {\n  return [{ json: { skip: true, reason: 'fromMe message' } }];\n}\n\n// Extraer n\u00famero limpio (viene como 'user@example.com' o 'user@example.com')\nconst remoteJid = key.remoteJid || '';\nconst phone = remoteJid.replace(/@s\\.whatsapp\\.net|@c\\.us|@g\\.us/g, '');\n\n// Ignorar mensajes de grupos (tienen @g.us)\nif (remoteJid.includes('@g.us')) {\n  return [{ json: { skip: true, reason: 'group message' } }];\n}\n\nconst senderName = data.pushName || 'Customer';\nconst messageType = data.messageType || 'conversation';\nconst instanceName = payload.instance || 'cifral-test';\n\n// Detectar tipo de mensaje\nconst isAudio = messageType === 'audioMessage' || messageType === 'audio';\nconst isText = messageType === 'conversation' || messageType === 'extendedTextMessage';\n\n// Extraer texto\nlet text = '';\nif (isText) {\n  text = data.message?.conversation \n    || data.message?.extendedTextMessage?.text \n    || '';\n}\n\n// Audio viene como base64 en el payload (porque activamos Webhook Base64)\n// Evolution lo pone en data.base64 o data.message.base64\nconst audioBase64 = isAudio \n  ? (data.base64 || data.media || data.message?.base64 || '')\n  : '';\n\nconst messageId = key.id || String(Date.now());\nconst timestamp = data.messageTimestamp \n  ? new Date(data.messageTimestamp * 1000).toISOString()\n  : new Date().toISOString();\n\nreturn [{\n  json: {\n    skip: false,\n    phone,\n    sender_name: senderName,\n    message_type: isAudio ? 'voice' : 'text',\n    text,\n    audio_base64: audioBase64,\n    message_id: messageId,\n    timestamp,\n    instance_name: instanceName,\n    raw_type: messageType\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "0889f728-0297-4d12-90ae-f4d25e8f363f",
      "name": "Respond OK (skip)3",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1232,
        2320
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ { status: 'ok', reason: $('Detect Message Type1').item.json.reason } }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "3b92667e-feb2-4cc7-b7b3-a2d9a27de66c",
      "name": "Is Voice?1",
      "type": "n8n-nodes-base.if",
      "position": [
        1232,
        2620
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "voice-check",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $('Detect Message Type1').item.json.message_type }}",
              "rightValue": "voice"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "725d2b17-51cb-4e2c-a247-1e44995275ac",
      "name": "Prepare Audio Binary1",
      "type": "n8n-nodes-base.code",
      "notes": "Convierte el base64 del audio a binario para que OpenAI Whisper pueda transcribirlo.\nNo necesita llamada HTTP extra \u2014 el audio ya vino en el webhook payload.",
      "position": [
        1456,
        2496
      ],
      "parameters": {
        "jsCode": "// ============================================\n// PREPARE AUDIO FOR OPENAI TRANSCRIPTION\n// Evolution sends audio as base64 when 'Webhook Base64' is enabled\n// We need to convert it to binary for the OpenAI node\n// ============================================\nconst base64Audio = items[0].json.audio_base64 || '';\n\nif (!base64Audio) {\n  // Si no hay base64, devuelve texto vac\u00edo para no romper el flujo\n  return [{ json: { ...items[0].json, text: '[audio no disponible]' } }];\n}\n\n// Limpiar el base64 por si viene con el prefijo data:audio/ogg;base64,\nconst cleanBase64 = base64Audio.replace(/^data:audio\\/[^;]+;base64,/, '');\n\n// Convertir a buffer binario\nconst buffer = Buffer.from(cleanBase64, 'base64');\n\nreturn [{\n  json: { ...items[0].json },\n  binary: {\n    data: {\n      data: buffer.toString('base64'),\n      mimeType: 'audio/ogg',\n      fileName: 'audio.ogg'\n    }\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "e4216116-2c05-4cce-88d2-ab214e2934a2",
      "name": "Transcribe Audio1",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        1680,
        2496
      ],
      "parameters": {
        "options": {},
        "resource": "audio",
        "operation": "transcribe"
      },
      "typeVersion": 2.1
    },
    {
      "id": "fd255bb7-e56a-4079-9137-8a8ac5a8afa9",
      "name": "Information Extractor1",
      "type": "@n8n/n8n-nodes-langchain.informationExtractor",
      "position": [
        3264,
        2592
      ],
      "parameters": {
        "text": "={{ $('Normalize and Look Match1').item.json.text }}",
        "options": {},
        "schemaType": "manual",
        "inputSchema": "={\n  \"type\": \"object\",\n  \"properties\": {\n    \"intent\": {\n      \"type\": \"string\",\n      \"description\": \"Main intent of the message. Use: interested, not_interested, asking_question, wrong_person, inquiry_price, inquiry_product, inquiry_schedule, inquiry_availability, request_quote, request_contact, greeting, other\"\n    },\n    \"reply_sentiment\": {\n      \"type\": \"string\",\n      \"description\": \"Sentiment toward a B2B outreach offer. 'positive': interested, curious, open to a call, asking for more info. 'negative': not interested, asks to stop, wrong person, dismissive. 'neutral': unclear or cannot be determined. Use 'neutral' for new inbound contacts who were not previously contacted.\"\n    },\n    \"product_mentioned\": {\n      \"type\": \"string\",\n      \"description\": \"Product or service mentioned by the person, if any. Empty if none.\"\n    },\n    \"urgency\": {\n      \"type\": \"string\",\n      \"description\": \"Urgency level: high, medium, low, none\"\n    },\n    \"needs_human\": {\n      \"type\": \"boolean\",\n      \"description\": \"True if the person asks about pricing, wants a quote, wants to speak with someone, shows buying interest, or expresses frustration. Always true for price-related questions.\"\n    }\n  },\n  \"required\": [\"intent\", \"reply_sentiment\", \"needs_human\"]\n}"
      },
      "typeVersion": 1.2
    },
    {
      "id": "57ad78b5-161d-4d00-9585-e2f337333803",
      "name": "Anthropic Model (Extractor)1",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "position": [
        3264,
        2752
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "claude-haiku-4-5-20251001",
          "cachedResultName": "Claude Haiku 4.5"
        },
        "options": {
          "temperature": 0.1,
          "maxTokensToSample": 150
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "79da27f5-8ad9-4027-ba27-075c09cfb3f0",
      "name": "Read Business Info1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        3552,
        2592
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "",
          "cachedResultName": "Business_Info"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID",
          "cachedResultUrl": "",
          "cachedResultName": "YOUR_SHEET"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.7
    },
    {
      "id": "ce06c8e2-30ec-4bb4-9328-f8f6d3ecf164",
      "name": "Read Products1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        3712,
        2592
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 985832111,
          "cachedResultUrl": "",
          "cachedResultName": "Products"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID",
          "cachedResultUrl": "",
          "cachedResultName": "YOUR_SHEET"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.7
    },
    {
      "id": "3007a6cd-50c9-4329-8419-7f45436907e5",
      "name": "Read FAQs1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        3872,
        2592
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 374181838,
          "cachedResultUrl": "",
          "cachedResultName": "FAQs"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID",
          "cachedResultUrl": "",
          "cachedResultName": "YOUR_SHEET"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.7
    },
    {
      "id": "d224a401-581c-4254-a620-108c0149f1e7",
      "name": "Build Context for AI Agent1",
      "type": "n8n-nodes-base.code",
      "position": [
        4048,
        2592
      ],
      "parameters": {
        "jsCode": "// ============================================\n// BUILD CONTEXT FOR AI AGENT\n// Pulls: message data, business KB, lead context from Notion\n// ============================================\n\nconst userMessage = $('Transcribe Audio1').isExecuted \n  ? $('Transcribe Audio1').first().json.text \n  : $('Detect Message Type1').first().json.text || '';\n\nconst userName     = $('Detect Message Type1').first().json.sender_name || 'Customer';\nconst userPhone    = $('Detect Message Type1').first().json.phone || '';\nconst messageType  = $('Detect Message Type1').first().json.message_type || 'text';\nconst instanceName = $('Detect Message Type1').first().json.instance_name || 'cifral-v1';\n\n// --- Knowledge base ---\nconst businessInfoItems = $('Read Business Info1').all();\nconst productsItems     = $('Read Products1').all();\nconst faqsItems         = $('Read FAQs1').all();\n\nconst business = {};\nbusinessInfoItems.forEach(item => {\n  const d = item.json;\n  if (d.business_info && d.value) business[d.business_info] = d.value;\n});\n\nconst productList = productsItems\n  .filter(i => i.json.product_id)\n  .map(i => {\n    const p = i.json;\n    const available = p.available === 'SI' ? 'AVAILABLE' : 'NOT AVAILABLE';\n    const highlight = p.highlight ? ` [${p.highlight}]` : '';\n    return `- ${p.name} (${p.product_id}): ${p.description}\\n  Status: ${available}\\n  Category: ${p.category}${highlight}`;\n  })\n  .join('\\n\\n');\n\nconst faqList = faqsItems\n  .filter(i => i.json.question)\n  .map(i => `Q: ${i.json.question}\\nA: ${i.json.answer}\\nCategory: ${i.json.category}`)\n  .join('\\n\\n');\n\n// --- Extractor output ---\nconst extractor = $('Information Extractor1').first().json.output || {};\n\n// --- Notion lead context (from Check Leads node) ---\nconst notionLead = $('Check Leads1').first().json;\n\nconst outreach_context = notionLead.is_known_lead\n  ? {\n      is_outreach_lead:    true,\n      personalization_note: notionLead.personalization_note || '',\n      pain_point:          notionLead.pain_point || '',\n      lead_status:         notionLead.lead_status || '',\n      notion_page_id:      notionLead.page_id || ''\n    }\n  : {\n      is_outreach_lead: false,\n      notion_page_id:   notionLead.page_id || ''\n    };\n\nreturn [{\n  json: {\n    context: {\n      business_name:        business.business_name || 'Your Business Name',\n      business_description: business.business_description || '',\n      phone:                business.phone || '',\n      email:                business.email || '',\n      hours:                business.hours || '',\n      coverage_area:        business.coverage_area || '',\n      response_tone:        business.response_tone || 'professional and friendly',\n      language:             business.language || 'same language as customer',\n      products:             productList || 'No products configured',\n      faqs:                 faqList || 'No FAQs configured'\n    },\n    user: {\n      name:               userName,\n      phone:              userPhone,\n      message:            userMessage,\n      message_type:       messageType,\n      instance_name:      instanceName,\n      intent:             extractor.intent || 'unknown',\n      product_mentioned:  extractor.product_mentioned || 'none',\n      reply_sentiment:    extractor.reply_sentiment || 'neutral',\n      urgency:            extractor.urgency || 'none',\n      needs_human:        extractor.needs_human || false,\n      outreach_context\n    }\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "434acad4-6485-4aec-b479-f1702acd62b0",
      "name": "Generate Response1",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        4400,
        2568
      ],
      "parameters": {
        "text": "={{ $json.user.message }}",
        "options": {
          "systemMessage": "You are the virtual assistant for {{ $json.context.business_name }}. {{ $json.context.business_description }}\n\nABOUT THE BUSINESS:\n- Website: {{ $json.context.website || 'See our website' }}\n- Email: {{ $json.context.email }}\n- Hours: {{ $json.context.hours }}\n- Coverage area: {{ $json.context.coverage_area }}\n- Booking: {{ $json.context.booking_url || 'Contact us to schedule' }}\n\nSERVICES (for your reference \u2014 never list all of them at once):\n{{ $json.context.products }}\n\nFREQUENTLY ASKED QUESTIONS:\n{{ $json.context.faqs }}\n\n---\n\nFORMATTING RULES \u2014 non-negotiable:\n- No markdown: no **bold**, no _italic_, no # headers\n- No em dashes (\u2014). Use commas or periods instead\n- No bullet lists. Use plain sentences or line breaks\n- Never list all services at once with numbered descriptions\n- Short paragraphs. WhatsApp style, conversational\n- Maximum 2 emojis per message, only when natural\n\n---\n\nCONVERSATION RULES \u2014 non-negotiable:\n- One idea per message. One question per message. Never stack two questions\n- Never present all services at once. If someone asks what you do, describe the most relevant one for their context\n- If someone picks a service, describe only that service in 1-2 lines, then ask one question about their situation\n- Your goal is to understand their problem, not to present a catalog\n- Escalate to human if: they ask for a price, want to get started, ask something you cannot answer with certainty, or show clear buying intent\n\n---\n\nLANGUAGE:\nMatch the customer's language exactly. If they write in Spanish, respond in Spanish. If English, respond in English.\n\n---\n\nOUTREACH CONTEXT:\n{{ $json.user.outreach_context.is_outreach_lead ? 'This contact was previously reached via cold outreach. Use the context below to personalize your response. Do NOT introduce the business generically \u2014 they already know who we are.\\n\\nLead context: ' + $json.user.outreach_context.personalization_note + '\\nProblem identified: ' + $json.user.outreach_context.pain_point : 'This appears to be a new inbound contact with no prior outreach history. Introduce the business naturally if needed.' }}\n\nIf the person seems surprised to receive a message or asks how you got their number:\n- Acknowledge the outreach transparently (e.g. \"We found your business online and thought we could help\")\n- Never pretend the message was not cold outreach if asked directly\n\nIf reply_sentiment is \"negative\": acknowledge politely, wish them well, end the conversation. Never insist.\n\n---\n\nPRICING:\nNever quote specific prices. If asked, send them to the pricing page or invite them to book a free call.\nExample (ES): \"Los precios de cada paquete los tienes en nuestra web. Si quieres que hablemos de cu\u00e1l encaja mejor, agenda una llamada gratuita.\"\nExample (EN): \"You can check all package pricing on our website. To talk through which one fits, book a free call.\"\n\n---\n\nBOOKING:\nWhen someone wants to schedule, speak with someone, or get a recommendation:\nDirect them to the booking link configured in your Business Info sheet.\n\n---\n\nHANDOFF TO TEAM:\nRedirect warmly when:\n- They ask something complex not covered here\n- They express frustration or explicitly ask for a human\n- They want a custom quote or proposal\n\nWhen redirecting: tell them the team will follow up shortly and invite them to share their situation briefly.\n\n---\n\nVOICE NOTES:\nAcknowledge naturally at the start: \"Escuch\u00e9 tu mensaje...\" / \"Got your voice note...\""
        },
        "promptType": "define"
      },
      "typeVersion": 1.8
    },
    {
      "id": "c92f8395-f502-4d9f-82ea-efedf3c4f450",
      "name": "Anthropic Model (Response)1",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "position": [
        4408,
        2792
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "claude-haiku-4-5-20251001",
          "cachedResultName": "Claude Haiku 4.5"
        },
        "options": {
          "temperature": 0.5,
          "maxTokensToSample": 250
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "79b02113-15b2-414d-8e1e-201d383d6d02",
      "name": "Conversation Memory1",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        4536,
        2792
      ],
      "parameters": {
        "sessionKey": "={{ $('Detect Message Type1').first().json.phone }}",
        "sessionIdType": "customKey",
        "contextWindowLength": 10
      },
      "typeVersion": 1.3
    },
    {
      "id": "af26f93c-40d6-44b4-b1dd-8d5988306d9b",
      "name": "Send WhatsApp Reply (Evolution)1",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Evolution API endpoint:\nPOST /message/sendText/{instanceName}\nHeaders: apikey (no Bearer prefix \u2014 Evolution usa apikey directa)\nBody: { number, text }\n\nEl n\u00famero puede ir con o sin @s.whatsapp.net \u2014 Evolution lo resuelve.",
      "position": [
        4976,
        2568
      ],
      "parameters": {
        "url": "=https://YOUR-EVOLUTION-DOMAIN.com/message/sendText/{{ $('Detect Message Type1').first().json.instance_name }}",
        "method": "POST",
        "options": {
          "timeout": 10000
        },
        "jsonBody": "={\n  \"number\": \"{{ $('Detect Message Type1').first().json.phone }}\",\n  \"text\": {{ JSON.stringify(\n    ($('Detect Message Type1').first().json.message_type === 'voice' ? '\ud83c\udfa4 ' : '') + $json.output\n  ) }}\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "apikey",
              "value": "=Add your instance API KEY Here"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "154d02a4-608b-49c0-ae29-ad4b74dad694",
      "name": "Format for Log1",
      "type": "n8n-nodes-base.set",
      "position": [
        5200,
        2568
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "f001",
              "name": "timestamp",
              "type": "string",
              "value": "={{ $now }}"
            },
            {
              "id": "f002",
              "name": "channel",
              "type": "string",
              "value": "whatsapp"
            },
            {
              "id": "f003",
              "name": "user_id",
              "type": "string",
              "value": "={{ $('Detect Message Type1').first().json.phone }}"
            },
            {
              "id": "f004",
              "name": "username",
              "type": "string",
              "value": "={{ $('Detect Message Type1').first().json.sender_name }}"
            },
            {
              "id": "f005",
              "name": "user_name",
              "type": "string",
              "value": "={{ $('Detect Message Type1').first().json.phone }}"
            },
            {
              "id": "f006",
              "name": "message_type",
              "type": "string",
              "value": "={{ $('Detect Message Type1').first().json.message_type }}"
            },
            {
              "id": "f007",
              "name": "message",
              "type": "string",
              "value": "={{ $('Build Context for AI Agent1').item.json.user.message }}"
            },
            {
              "id": "f008",
              "name": "intent",
              "type": "string",
              "value": "={{ $('Information Extractor1').first().json.output.intent }}"
            },
            {
              "id": "f009",
              "name": "response",
              "type": "string",
              "value": "={{ $('Generate Response1').item.json.output }}"
            },
            {
              "id": "f010",
              "name": "needs_human",
              "type": "string",
              "value": "={{ String($('Information Extractor1').first().json.output.needs_human) }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "e405b452-2988-4bf5-b9a2-81a7062af1d9",
      "name": "Log Conversation1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        5584,
        2576
      ],
      "parameters": {
        "columns": {
          "value": {
            "intent": "={{ $json.intent }}",
            "channel": "={{ $json.channel }}",
            "message": "={{ $json.message }}",
            "user_id": "={{ $json.user_id }}",
            "response": "={{ $json.response }}",
            "username": "={{ $json.username }}",
            "timestamp": "={{ $json.timestamp }}",
            "user_name": "={{ $json.user_name }}",
            "needs_human": "={{ $json.needs_human }}",
            "message_type": "={{ $json.message_type }}"
          },
          "schema": [
            {
              "id": "timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "channel",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "channel",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "sender",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "sender",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "message",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "message",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "response",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "response",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "needs_human",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "needs_human",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "user_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "user_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "username",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "username",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "user_name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "user_name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "message_type",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "message_type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "intent",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "intent",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1112054313,
          "cachedResultUrl": "",
          "cachedResultName": "Conversation_Log"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID",
          "cachedResultUrl": "",
          "cachedResultName": "YOUR_SHEET"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "a43e9b37-c6c8-4f23-828b-03353325a434",
      "name": "Alert Owner?1",
      "type": "n8n-nodes-base.if",
      "position": [
        5808,
        2576
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "human-check",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.needs_human }}",
              "rightValue": "true"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "2d7e4f62-767c-4b5a-8b63-3f4d60be742c",
      "name": "Alert Owner (Telegram)1",
      "type": "n8n-nodes-base.telegram",
      "notes": "Get your Chat ID by sending a message to @userinfobot on Telegram.\nSet it directly in this node or use an n8n environment variable.",
      "position": [
        6032,
        2496
      ],
      "parameters": {
        "text": "=\ud83d\udea8 *WhatsApp \u2014 Customer needs attention*\n\n\ud83d\udc64 *Name:* {{ $json.username }}\n\ud83d\udcf1 *Phone:* {{ $json.user_id }}\n\ud83d\udce9 *Type:* {{ $json.message_type }}\n\ud83c\udfaf *Intent:* {{ $json.intent }}\n\n*Message:*\n{{ $json.message }}\n\n*BOT RESPONSE:*\n{{ $json.response }}\n\n---\n\ud83d\udd50 {{ $json.timestamp }}",
        "chatId": "=ADD_YOUR_TELEGRAM_CHAT_ID",
        "additionalFields": {
          "parse_mode": "HTML",
          "appendAttribution": false
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "b3d554f9-9420-431b-84bc-1e94edf92844",
      "name": "Wait1",
      "type": "n8n-nodes-base.wait",
      "position": [
        4224,
        2592
      ],
      "parameters": {
        "amount": 10
      },
      "typeVersion": 1.1
    },
    {
      "id": "c8534b1c-864c-4c16-b0cf-78d7350e21f3",
      "name": "Format Cleaner1",
      "type": "n8n-nodes-base.code",
      "position": [
        4752,
        2568
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Strip markdown formatting from AI response\nlet text = $input.item.json.output || '';\n\n// Remove bold/italic\ntext = text.replace(/\\*\\*(.*?)\\*\\*/g, '$1');\ntext = text.replace(/\\*(.*?)\\*/g, '$1');\ntext = text.replace(/_(.*?)_/g, '$1');\n\n// Replace em dashes with comma\ntext = text.replace(/ \u2014 /g, ', ');\ntext = text.replace(/\u2014/g, ', ');\n\n// Remove markdown headers\ntext = text.replace(/^#{1,3}\\s+/gm, '');\n\n// Clean up extra blank lines (max 1)\ntext = text.replace(/\\n{3,}/g, '\\n\\n');\n\nreturn { json: { ...$input.item.json, output: text.trim() } };\n"
      },
      "typeVersion": 2
    },
    {
      "id": "4eca12e6-2f1b-4820-beca-b4ec624ddb8b",
      "name": "Skip Event1",
      "type": "n8n-nodes-base.if",
      "position": [
        1008,
        2464
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "59df61c1-eed2-452c-bbdb-ee44dcfe22d4",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.skip }}",
              "rightValue": true
            },
            {
              "id": "f26cad4c-ca7f-478b-8f0d-23b6b2844db5",
              "operator": {
                "type": "string",
                "operation": "empty",
                "singleValue": true
              },
              "leftValue": "={{ $json.text }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "9b0e79f0-44fe-45c9-b888-51fcd57c6e91",
      "name": "Update Lead Status1",
      "type": "n8n-nodes-base.notion",
      "position": [
        6704,
        2480
      ],
      "parameters": {
        "pageId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Normalize and Look Match1').first().json.page_id }}"
        },
        "options": {},
        "resource": "databasePage",
        "operation": "update",
        "propertiesUi": {
          "propertyValues": [
            {
              "key": "Last Contact|date",
              "date": "={{ $now }}"
            },
            {
              "key": "WA Status|select",
              "selectValue": "={{ $('Information Extractor1').first().json.reply_sentiment === 'positive' ? 'Positive Reply' : $('Information Extractor1').first().json.reply_sentiment === 'negative' ? 'Negative Reply' : 'Neutral Reply' }}"
            },
            {
              "key": "Status|status",
              "statusValue": "In Contact"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "f7ea0462-b7d1-47a1-b051-b4d4762df934",
      "name": "Respond 1",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        6928,
        2576
      ],
      "parameters": {
        "options": {
          "responseCode": 200
        },
        "respondWith": "json",
        "responseBody": "={{ { status: 'ok' } }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "12d91158-530b-484a-b29c-f8521580fcae",
      "name": "Phone Match1",
      "type": "n8n-nodes-base.if",
      "position": [
        6480,
        2576
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "63f5d6c9-d9bd-417a-8c6c-669dfe172776",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $('Normalize and Look Match1').first().json.matched }}",
              "rightValue": "={{ $json.matched }}"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "2a113cff-bafe-4545-9760-bfe61fc3dc31",
      "name": "Sentiment Analysis1",
      "type": "@n8n/n8n-nodes-langchain.sentimentAnalysis",
      "position": [
        1888,
        2640
      ],
      "parameters": {
        "options": {
          "categories": "Automatic reply, Human message"
        },
        "inputText": "={{ $json.text }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "3268891e-33cf-4448-8f7a-cb85a4ad7436",
      "name": "Anthropic Chat Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "position": [
        1904,
        2832
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "claude-haiku-4-5-20251001",
          "cachedResultName": "Claude Haiku 4.5"
        },
        "options": {
          "temperature": 0.4
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "18031833-35ea-4035-aedb-6fc1ce63704f",
      "name": "Create a New Lead1",
      "type": "n8n-nodes-base.notion",
      "position": [
        6704,
        2672
      ],
      "parameters": {
        "title": "={{ $('Detect Message Type1').first().json.sender_name }}",
        "options": {},
        "resource": "databasePage",
        "databaseId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_NOTION_DATABASE_ID",
          "cachedResultUrl": "",
          "cachedResultName": "YOUR_DATABASE_NAME"
        },
        "propertiesUi": {
          "propertyValues": [
            {
              "key": "Phone|phone_number",
              "phoneValue": "={{ $('Detect Message Type1').first().json.phone }}"
            },
            {
              "key": "Status|status",
              "statusValue": "Lead"
            },
            {
              "key": "Lead Source|select",
              "selectValue": "WhatsApp"
            },
            {
              "key": "Last Contact|date",
              "date": "={{ $now }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "abf54eea-476e-4ed3-b9a3-5cb902d49c4e",
      "name": "Check Leads1",
      "type": "n8n-nodes-base.notion",
      "position": [
        2256,
        2716
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "key": "Phone|phone_number",
              "condition": "is_not_empty"
            }
          ]
        },
        "options": {},
        "resource": "databasePage",
        "operation": "getAll",
        "returnAll": true,
        "databaseId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_NOTION_DATABASE_ID",
          "cachedResultUrl": "",
          "cachedResultName": "YOUR_DATABASE_NAME"
        },
        "filterType": "manual"
      },
      "typeVersion": 2.2
    },
    {
      "id": "7c93f297-1a9a-467c-82b2-c86d3e01cb91",
      "name": "Bot Status1",
      "type": "n8n-nodes-base.if",
      "position": [
        2704,
        2716
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "80a59ea4-0d28-4d54-a57f-9094f7b3dbc4",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.bot_status }}",
              "rightValue": "Paused"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "80354c1e-c740-40d1-bf2f-b8a00c11f2d0",
      "name": "Normalize Phone and Look Match1",
      "type": "n8n-nodes-base.code",
      "position": [
        2480,
        2716
      ],
      "parameters": {
        "jsCode": "// ============================================\n// NORMALIZE AND LOOK MATCH \u2014 Get Lead Early\n// Reads n8n Notion node output (flattened: property_*)\n// ============================================\n\nconst webhookPhone = $('Detect Message Type1').first().json.phone;\nconst notionLeads  = $input.all();\n\nconst messageData = {\n  phone:         $('Detect Message Type1').first().json.phone,\n  sender_name:   $('Detect Message Type1').first().json.sender_name,\n  message_type:  $('Detect Message Type1').first().json.message_type,\n  instance_name: $('Detect Message Type1').first().json.instance_name,\n  text: $('Sentiment Analysis1').first().json.text\n        || $('Detect Message Type1').first().json.text\n        || ''\n};\n\nconst normalize = (p) => {\n  let n = String(p || '').replace(/[^0-9]/g, '');\n  // Add country-specific phone normalization here if needed\n  return n;\n};\n\nconst normalizedWebhook = normalize(webhookPhone);\n\nconst match = notionLeads.find(item => {\n  const notionPhone = normalize(item.json.property_phone || '');\n  return notionPhone.length >= 10 && notionPhone === normalizedWebhook;\n});\n\nif (match) {\n  return [{\n    json: {\n      ...messageData,\n      is_known_lead:        true,\n      matched:              true,\n      page_id:              match.json.id || '',\n      bot_status:           match.json.property_wa_chat_bot_status || 'Active',\n      lead_status:          match.json.property_status || '',\n      personalization_note: match.json.property_ai_personalization_note || '',\n      pain_point:           match.json.property_ai_notes || '',\n    }\n  }];\n} else {\n  return [{\n    json: {\n      ...messageData,\n      is_known_lead: false,\n      matched:       false,\n      page_id:       '',\n      bot_status:    'Active',\n    }\n  }];\n}"
      },
      "typeVersion": 2
    },
    {
      "id": "4d731b71-884a-402b-b525-6a852fbbc12e",
      "name": "Respond OK (skip)4",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        2096,
        2352
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ { status: 'ok', reason: $('Detect Message Type1').item.json.reason } }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "5b9408ba-e17f-47f1-8ab3-ab16a7b1db58",
      "name": "Respond OK (skip)5",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        2880,
        2864
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ { status: 'ok', reason: $('Detect Message Type1').item.json.reason } }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "4d658378-4f36-4a08-b00b-7600c5cd092f",
      "name": "Pause ChatBot1",
      "type": "n8n-nodes-base.notion",
      "position": [
        6256,
        2496
      ],
      "parameters": {
        "pageId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Normalize and Look Match1').first().json.page_id }}"
        },
        "options": {},
        "resource": "databasePage",
        "operation": "update",
        "propertiesUi": {
          "propertyValues": [
            {
              "key": "WA ChatBot Status|select",
              "selectValue": "=Paused"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "1dc8ceee-ab12-416f-8a04-efe793930a6a",
      "name": "No Operation, do nothing3",
      "type": "n8n-nodes-base.noOp",
      "position": [
        3056,
        2864
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "7c995f97-9eb1-40c1-9387-1c518985f2e1",
      "name": "No Operation, do nothing4",
      "type": "n8n-nodes-base.noOp",
      "position": [
        1456,
        2320
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "910d569a-fbbf-40d7-b2f8-b61b7f129bbd",
      "name": "No Operation, do nothing5",
      "type": "n8n-nodes-base.noOp",
      "position": [
        2272,
        2352
      ],
      "parameters": {},
      "typeVersion": 1
    }
  ],
  "connections": {
    "Wait1": {
      "main": [
        [
          {
            "node": "Generate Response1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Voice?1": {
      "main": [
        [
          {
            "node": "Prepare Audio Binary1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Sentiment Analysis1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read FAQs1": {
      "main": [
        [
          {
            "node": "Build Context for AI Agent1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Bot Status1": {
      "main": [
        [
          {
            "node": "Information Extractor1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond OK (skip)5",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Skip Event1": {
      "main": [
        [
          {
            "node": "Respond OK (skip)3",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Is Voice?1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Leads1": {
      "main": [
        [
          {
            "node": "Normalize Phone and Look Match1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Phone Match1": {
      "main": [
        [
          {
            "node": "Update Lead Status1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Create a New Lead1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Alert Owner?1": {
      "main": [
        [
          {
            "node": "Alert Owner (Telegram)1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Phone Match1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pause ChatBot1": {
      "main": [
        [
          {
            "node": "Phone Match1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Products1": {
      "main": [
        [
          {
            "node": "Read FAQs1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Cleaner1": {
      "main": [
        [
          {
            "node": "Send WhatsApp Reply (Evolution)1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format for Log1": {
      "main": [
        [
          {
            "node": "Log Conversation1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Conversation1": {
      "main": [
        [
          {
            "node": "Alert Owner?1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Transcribe Audio1": {
      "main": [
        [
          {
            "node": "Sentiment Analysis1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create a New Lead1": {
      "main": [
        [
          {
            "node": "Respond 1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Evolution Webhook1": {
      "main": [
        [
          {
            "node": "Detect Message Type1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Response1": {
      "main": [
        [
          {
            "node": "Format Cleaner1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Respond OK (skip)3": {
      "main": [
        [
          {
            "node": "No Operation, do nothing4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Respond OK (skip)4": {
      "main": [
        [
          {
            "node": "No Operation, do nothing5",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Respond OK (skip)5": {
      "main": [
        [
          {
            "node": "No Operation, do nothing3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Business Info1": {
      "main": [
        [
          {
            "node": "Read Products1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sentiment Analysis1": {
      "main": [
        [
          {
            "node": "Respond OK (skip)4",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Check Leads1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Lead Status1": {
      "main": [
        [
          {
            "node": "Respond 1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Conversation Memory1": {
      "ai_memory": [
        [
          {
            "node": "Generate Response1",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Detect Message Type1": {
      "main": [
        [
          {
            "node": "Skip Event1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Anthropic Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "Sentiment Analysis1",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Audio Binary1": {
      "main": [
        [
          {
            "node": "Transcribe Audio1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Information Extractor1": {
      "main": [
        [
          {
            "node": "Read Business Info1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Alert Owner (Telegram)1": {
      "main": [
        [
          {
            "node": "Pause ChatBot1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Anthropic Model (Response)1": {
      "ai_languageModel": [
        [
          {
            "node": "Generate Response1",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Build Context for AI Agent1": {
      "main": [
        [
          {
            "node": "Wait1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Anthropic Model (Extractor)1": {
      "ai_languageModel": [
        [
          {
            "node": "Information Extractor1",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Phone and Look Match1": {
      "main": [
        [
          {
            "node": "Bot Status1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send WhatsApp Reply (Evolution)1": {
      "main": [
        [
          {
            "node": "Format for Log1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}