{
  "id": "FpauF9jfmoRaFe8q",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Autonomous AI Crawler",
  "tags": [],
  "nodes": [
    {
      "id": "26524acb-036b-4a0d-ba5a-e11b96acba20",
      "name": "Main Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5120,
        1888
      ],
      "parameters": {
        "color": 7,
        "width": 560,
        "height": 1504,
        "content": "## \ud83d\udcac WhatsApp AI Sales Agent\n\nHandles every inbound WhatsApp sales conversation end-to-end: it answers product questions with RAG lookups against Pinecone, checks and books calendar appointments, and hands off to a human in Slack whenever it isn't confident.\n\n**Perfect for:** Sales and support teams running WhatsApp Business who want first-line replies automated without losing trickier conversations.\n\n---\n\n## How it works\n\n1. **Incoming WhatsApp Message** \u2014 Receives every inbound WhatsApp message via the WhatsApp Business trigger.\n2. **Is Text Message?** \u2014 Checks whether the message contains a text body.\n3. **Ignore Non-Text** \u2014 *(optional path)* Silently drops non-text messages (images, audio, stickers).\n4. **Sales Agent** \u2014 LangChain AI agent that reads the message and drives the conversation.\n5. **OpenAI Chat Model** \u2014 GPT-5 mini model powering the Sales Agent's reasoning.\n6. **Session Memory** \u2014 Buffer window memory keyed by the customer's phone number, holding the last 8 turns.\n7. **Product Catalog (RAG Tool)** \u2014 Pinecone vector store tool the agent calls to answer product questions.\n8. **Catalog Embeddings** \u2014 OpenAI embeddings model backing the Product Catalog tool.\n9. **Check Availability (Tool)** \u2014 Google Calendar tool the agent calls to check open slots.\n10. **Book Appointment (Tool)** \u2014 Google Calendar tool the agent calls to create the booking.\n11. **Parse Handoff Flag** \u2014 Code node that reads the agent's `HANDOFF:true/false` tag and strips it from the reply text.\n12. **Needs Human?** \u2014 Branches on the handoff flag.\n13. **Route to Human** \u2014 *(handoff path)* Posts the customer's message and draft reply to your sales Slack channel.\n14. **Reply on WhatsApp** \u2014 *(confident path)* Sends the agent's reply directly back to the customer.\n15. **Error Trigger** \u2014 Catches any failure anywhere in the workflow.\n16. **Notify Ops** \u2014 Posts an alert with the error message to your ops Slack channel.\n\n---\n\n## Setup (~20 minutes)\n\n1. **WhatsApp Business Cloud** \u2014 Connect your credential in *Incoming WhatsApp Message* and *Reply on WhatsApp*.\n2. **OpenAI** \u2014 Add your API key in *OpenAI Chat Model* and *Catalog Embeddings*.\n3. **Pinecone** \u2014 Set your index name in *Product Catalog (RAG Tool)*.\n4. **Google Calendar** \u2014 Connect your credential and set the calendar in *Check Availability (Tool)* and *Book Appointment (Tool)*.\n5. **Slack** \u2014 Point *Route to Human* at your sales channel and *Notify Ops* at your ops/alerts channel.\n> Several nodes still contain placeholder values (REPLACE_WITH_PINECONE_INDEX, REPLACE_WITH_CALENDAR_ID, REPLACE_WITH_CHANNEL_ID, REPLACE_WITH_PHONE_NUMBER_ID) \u2014 replace all of them before activating."
      },
      "typeVersion": 1
    },
    {
      "id": "62f4b463-04a7-493c-8188-274a34394f04",
      "name": "Section - Trigger & Intake",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5872,
        2128
      ],
      "parameters": {
        "color": 5,
        "width": 840,
        "height": 472,
        "content": "## 1\ufe0f\u20e3 Trigger & Intake\n\nThe **Incoming WhatsApp Message** trigger receives every inbound customer message. **Is Text Message?** checks whether it carries a text body, and **Ignore Non-Text** quietly drops anything else, such as images or voice notes, before it ever reaches the agent."
      },
      "typeVersion": 1
    },
    {
      "id": "066205ef-77e9-4cc0-a9bb-e04a413d6009",
      "name": "Section - AI Sales Agent & Tools",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6752,
        2000
      ],
      "parameters": {
        "color": 3,
        "width": 1000,
        "height": 936,
        "content": "## 2\ufe0f\u20e3 AI Sales Agent & Tools\n\nThe **Sales Agent** is powered by the **OpenAI Chat Model** and remembers each conversation through **Session Memory**, keyed to the customer's phone number. It calls **Product Catalog (RAG Tool)**, backed by **Catalog Embeddings** against Pinecone, to answer product questions, and uses **Check Availability (Tool)** and **Book Appointment (Tool)** to check and reserve slots on Google Calendar."
      },
      "typeVersion": 1
    },
    {
      "id": "89a8687e-06d5-4ccf-be28-02e91769c670",
      "name": "Section - Handoff Decision & Routing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        7776,
        1984
      ],
      "parameters": {
        "color": 6,
        "width": 840,
        "height": 536,
        "content": "## 3\ufe0f\u20e3 Handoff Decision & Routing\n\n**Parse Handoff Flag** reads the `HANDOFF:true/false` tag the agent appends to its reply and strips it out. **Needs Human?** branches on that flag: uncertain or out-of-scope replies go to **Route to Human** in Slack, while confident answers go straight to **Reply on WhatsApp**."
      },
      "typeVersion": 1
    },
    {
      "id": "e9a1d998-610b-47f1-9294-c564b5c45fe3",
      "name": "Section - Error Handling",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6016,
        2672
      ],
      "parameters": {
        "color": 4,
        "width": 600,
        "height": 360,
        "content": "## 4\ufe0f\u20e3 Error Handling\n\nThe **Error Trigger** fires whenever any node in the workflow fails, and **Notify Ops** posts an alert with the error message to the internal ops Slack channel so the team can react quickly."
      },
      "typeVersion": 1
    },
    {
      "id": "511df791-46ac-489c-9f8d-ca4937ffbed2",
      "name": "Incoming WhatsApp Message",
      "type": "n8n-nodes-base.whatsAppTrigger",
      "position": [
        5984,
        2288
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "f0760f7f-24b8-4f92-a9c1-43e03488e3cb",
      "name": "Is Text Message?",
      "type": "n8n-nodes-base.if",
      "position": [
        6224,
        2288
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-is-text",
              "operator": {
                "type": "string",
                "operation": "exists"
              },
              "leftValue": "={{ $json.messages?.[0]?.text?.body }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "3cc78c1c-206b-46c9-a9dd-62f64b9018ad",
      "name": "Ignore Non-Text",
      "type": "n8n-nodes-base.noOp",
      "position": [
        6576,
        2416
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "6a8ce86b-950d-4b63-849c-d79c741a1484",
      "name": "Sales Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        7088,
        2240
      ],
      "parameters": {
        "text": "={{ $json.messages[0].text.body }}",
        "options": {
          "systemMessage": "You are a friendly WhatsApp sales assistant. Use the Product Catalog tool to answer product questions accurately, use the Calendar tools to check availability and book appointments when the customer wants one. Keep replies under 60 words. If you are not confident in your answer or the request is out of scope (refunds, complaints, complex negotiation), end your reply with the exact tag HANDOFF:true on its own line; otherwise end with HANDOFF:false."
        },
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "44acb37c-9c86-48cf-80bb-24b00e0d826f",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        6992,
        2416
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-5-mini"
        },
        "options": {
          "temperature": 0.3
        },
        "builtInTools": {}
      },
      "typeVersion": 1.3
    },
    {
      "id": "5da1a883-7ef9-4f42-b20e-e8a08711eed0",
      "name": "Session Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        7104,
        2432
      ],
      "parameters": {
        "sessionKey": "={{ $('Incoming WhatsApp Message').item.json.messages[0].from }}",
        "sessionIdType": "customKey",
        "contextWindowLength": 8
      },
      "typeVersion": 1.4
    },
    {
      "id": "368f18c9-a44b-4ab0-89a1-e308a8294d01",
      "name": "Product Catalog (RAG Tool)",
      "type": "@n8n/n8n-nodes-langchain.vectorStorePinecone",
      "position": [
        7088,
        2608
      ],
      "parameters": {
        "mode": "retrieve-as-tool",
        "options": {},
        "pineconeIndex": {
          "__rl": true,
          "mode": "list",
          "value": "REPLACE_WITH_PINECONE_INDEX"
        },
        "toolDescription": "Search the product catalog for pricing, availability, specs and descriptions to answer customer product questions."
      },
      "typeVersion": 1.3
    },
    {
      "id": "704fe446-341f-40ec-bf8d-a58a63c39a5e",
      "name": "Catalog Embeddings",
      "type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
      "position": [
        7120,
        2720
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "88fba4e2-1522-4091-a6ec-a2d380f782eb",
      "name": "Check Availability (Tool)",
      "type": "n8n-nodes-base.googleCalendarTool",
      "position": [
        7264,
        2432
      ],
      "parameters": {
        "options": {
          "outputFormat": "availability"
        },
        "timeMax": "={{ $fromAI(\"timeMax\", \"ISO end of the interval to check\") }}",
        "timeMin": "={{ $fromAI(\"timeMin\", \"ISO start of the interval to check\") }}",
        "calendar": {
          "__rl": true,
          "mode": "list",
          "value": "REPLACE_WITH_CALENDAR_ID"
        },
        "resource": "calendar"
      },
      "typeVersion": 1.3
    },
    {
      "id": "060a36ff-877e-4541-96b0-5b17fd6995ef",
      "name": "Book Appointment (Tool)",
      "type": "n8n-nodes-base.googleCalendarTool",
      "position": [
        7424,
        2448
      ],
      "parameters": {
        "end": "={{ $fromAI(\"end\", \"ISO end datetime of the appointment\") }}",
        "start": "={{ $fromAI(\"start\", \"ISO start datetime of the appointment\") }}",
        "calendar": {
          "__rl": true,
          "mode": "list",
          "value": "REPLACE_WITH_CALENDAR_ID"
        },
        "additionalFields": {
          "summary": "={{ $fromAI(\"summary\", \"Short appointment title including customer name\") }}"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "564bdf2f-984e-455a-aca8-95fc8e1f00c8",
      "name": "Parse Handoff Flag",
      "type": "n8n-nodes-base.code",
      "position": [
        7888,
        2224
      ],
      "parameters": {
        "jsCode": "const raw = $json.output || '';\nconst needsHuman = /HANDOFF:\\s*true/i.test(raw);\nconst reply = raw.replace(/HANDOFF:\\s*(true|false)/i, '').trim();\nreturn [{ json: { reply, needsHuman } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "ace30bf8-02e6-40cc-82ed-448fedc49b9a",
      "name": "Needs Human?",
      "type": "n8n-nodes-base.if",
      "position": [
        8128,
        2224
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-handoff",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.needsHuman }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "85315fb2-c750-4c84-85da-2541447df09a",
      "name": "Route to Human",
      "type": "n8n-nodes-base.slack",
      "position": [
        8368,
        2144
      ],
      "parameters": {
        "text": "=\ud83d\ude4b Handoff needed from {{ $('Incoming WhatsApp Message').item.json.messages[0].from }}: \"{{ $('Incoming WhatsApp Message').item.json.messages[0].text.body }}\"\\nDraft reply: {{ $json.reply }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "REPLACE_WITH_CHANNEL_ID",
          "cachedResultName": "whatsapp-handoffs"
        },
        "otherOptions": {}
      },
      "typeVersion": 2.3
    },
    {
      "id": "3cf8b0e1-f076-4278-910a-a0bad54ec8c4",
      "name": "Reply on WhatsApp",
      "type": "n8n-nodes-base.whatsApp",
      "maxTries": 2,
      "position": [
        8368,
        2320
      ],
      "parameters": {
        "textBody": "={{ $json.reply }}",
        "operation": "send",
        "phoneNumberId": "REPLACE_WITH_PHONE_NUMBER_ID",
        "additionalFields": {},
        "recipientPhoneNumber": "={{ $('Incoming WhatsApp Message').item.json.messages[0].from }}"
      },
      "retryOnFail": true,
      "typeVersion": 1.1
    },
    {
      "id": "0e83514d-8ea3-4954-8ee3-6ed639981341",
      "name": "Error Trigger",
      "type": "n8n-nodes-base.errorTrigger",
      "position": [
        6128,
        2832
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "279ac870-8e05-4f4d-8104-30691a67f211",
      "name": "Notify Ops",
      "type": "n8n-nodes-base.slack",
      "position": [
        6368,
        2832
      ],
      "parameters": {
        "text": "=\ud83d\udea8 WhatsApp AI Sales Agent failed: {{ $json.execution.error.message }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "REPLACE_WITH_CHANNEL_ID",
          "cachedResultName": "ops-alerts"
        },
        "otherOptions": {}
      },
      "typeVersion": 2.3
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "24c10721-a8ec-457b-9d48-7959c28693f7",
  "nodeGroups": [],
  "connections": {
    "Sales Agent": {
      "main": [
        [
          {
            "node": "Parse Handoff Flag",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Needs Human?": {
      "main": [
        [
          {
            "node": "Route to Human",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Reply on WhatsApp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Error Trigger": {
      "main": [
        [
          {
            "node": "Notify Ops",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Session Memory": {
      "ai_memory": [
        [
          {
            "node": "Sales Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Is Text Message?": {
      "main": [
        [
          {
            "node": "Sales Agent",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Ignore Non-Text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Sales Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Catalog Embeddings": {
      "ai_embedding": [
        [
          {
            "node": "Product Catalog (RAG Tool)",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    },
    "Parse Handoff Flag": {
      "main": [
        [
          {
            "node": "Needs Human?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Book Appointment (Tool)": {
      "ai_tool": [
        [
          {
            "node": "Sales Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Check Availability (Tool)": {
      "ai_tool": [
        [
          {
            "node": "Sales Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Incoming WhatsApp Message": {
      "main": [
        [
          {
            "node": "Is Text Message?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Product Catalog (RAG Tool)": {
      "ai_tool": [
        [
          {
            "node": "Sales Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    }
  }
}