{
  "id": "qhFZ5grN9fdPR5Nw",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Hotel Guest WhatsApp Concierge",
  "tags": [],
  "nodes": [
    {
      "id": "d8cfe290-976d-4fcf-97e6-3fe0eb974dfd",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        0
      ],
      "parameters": {
        "width": 480,
        "height": 1168,
        "content": "## Hotel Guest WhatsApp Concierge\n\n### How it works\n\nTriggers when a new WhatsApp message is received from a user.\nLooks up the sender's contact details in Google Sheets to identify them.\nRetrieves the user's active reservations and pending service requests for context.\nProcesses the request using an AI agent equipped with various hotel management tools.\nSends the AI-generated response back to the user via WhatsApp.\nSeparately triggers on Google Sheets updates to synchronize external data.\n\n### Setup steps\n\n- [ ] Connect your WhatsApp Business API credentials in the trigger and send message nodes.\n- [ ] Authenticate Google Sheets nodes with a service account or OAuth credentials.\n- [ ] Connect your Google Gemini API credentials in the chat model node.\n- [ ] Configure a Redis instance for chat memory if you wish to persist conversation history.\n- [ ] Ensure all referenced Google Sheets have the required columns for reservations, service requests, and users.\n\n### Customization\n\nYou can add or remove specific tools inside the AI Agent depending on the hotel services you offer.\n\n@[youtube](q2vPN9eub1o)"
      },
      "typeVersion": 1
    },
    {
      "id": "6e13e9d6-582e-4035-825e-7a0306d1519b",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        560,
        32
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 304,
        "content": "## Receive and verify message\n\nTriggers on new WhatsApp messages and performs an initial check on the message content."
      },
      "typeVersion": 1
    },
    {
      "id": "2d5a82f4-0616-4c1a-a268-28c50be07be4",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1008,
        16
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 304,
        "content": "## Identify user contact\n\nLooks up the sender's contact details in Google Sheets and branches based on the result."
      },
      "typeVersion": 1
    },
    {
      "id": "2103bae8-fc33-4753-8527-0ccf81306ed7",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1456,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 304,
        "content": "## Retrieve guest data\n\nRetrieves the user's active reservations and pending service requests to provide context."
      },
      "typeVersion": 1
    },
    {
      "id": "5691b124-648f-4a5c-a970-5e80f7619380",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2128,
        16
      ],
      "parameters": {
        "color": 7,
        "width": 1440,
        "height": 480,
        "content": "## Process AI and respond\n\nUses an AI agent with specialized tools to handle the guest's request and sends the reply via WhatsApp."
      },
      "typeVersion": 1
    },
    {
      "id": "864615b4-1cda-48a5-ba22-a55bbbf37dc1",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        560,
        560
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 320,
        "content": "## Synchronize external data\n\nListens for direct updates in Google Sheets and appends or updates rows separately from the main flow."
      },
      "typeVersion": 1
    },
    {
      "id": "73906029-7718-4995-89a7-8094fc868973",
      "name": "When WhatsApp Received",
      "type": "n8n-nodes-base.whatsAppTrigger",
      "position": [
        608,
        160
      ],
      "parameters": {
        "options": {
          "messageStatusUpdates": [
            "all"
          ]
        },
        "updates": [
          "messages"
        ]
      },
      "credentials": {
        "whatsAppTriggerApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "be0b1057-6833-44c1-aa22-8fb7780e8357",
      "name": "Check Message Body",
      "type": "n8n-nodes-base.if",
      "position": [
        832,
        160
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "eb417180-cf35-4c4f-a0e2-55d37bd65c70",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.messages[0].text.body }}",
              "rightValue": ""
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.3
    },
    {
      "id": "ca5f5f4e-5c62-4b2a-8115-a2fa3956dc6d",
      "name": "Read Guest Details Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1056,
        144
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $('When WhatsApp Received').item.json.contacts[0].wa_id }}",
              "lookupColumn": "number"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/16FkrLkrW2Te8mUGQ70qWX3PMZV3yqJAZx1zoBB07Zuk/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "16FkrLkrW2Te8mUGQ70qWX3PMZV3yqJAZx1zoBB07Zuk",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/16FkrLkrW2Te8mUGQ70qWX3PMZV3yqJAZx1zoBB07Zuk/edit?usp=drivesdk",
          "cachedResultName": "Guest Details"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7,
      "alwaysOutputData": true
    },
    {
      "id": "bdef39f7-080f-41c0-b391-be4742d294bc",
      "name": "Hotel Concierge Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        2704,
        128
      ],
      "parameters": {
        "text": "=CURRENT DATE & TIME (UTC): {{ $now.toISO() }}\nCURRENT DATE & TIME (IST, UTC+5:30): {{ $now.plus({hours: 5, minutes: 30}).toFormat(\"EEEE, MMMM d yyyy 'at' h:mm a\") }}\n\n---\nGUEST WHATSAPP NUMBER: {{ $('When WhatsApp Received').item.json.contacts[0].wa_id }}\nGUEST DISPLAY NAME (from WhatsApp): {{ $('When WhatsApp Received').item.json.contacts[0].profile.name }}\n\nGUEST PROFILE (from our system):\n{{ $('Read Guest Details Sheet').item.json.row_number != null ? JSON.stringify($('Read Guest Details Sheet').item.json, null, 2) : 'NOT FOUND IN SYSTEM \u2014 treat as new guest' }}\n\nALL RESERVATIONS ON FILE:\n{{ $('Read Reservations Sheet').all().length > 0 && $('Read Reservations Sheet').first().json.row_number != null ? JSON.stringify($('Read Reservations Sheet').all().map(i => i.json), null, 2) : 'NONE' }}\n\nACTIVE RESERVATIONS RIGHT NOW (check_in <= current IST datetime < check_out):\n{{ $json.active_reservations.length > 0 ? JSON.stringify($json.active_reservations, null, 2) : 'NONE \u2014 guest is not currently checked in' }}\n\nPENDING SERVICE REQUESTS:\n{{ $('Read Service Requests Sheet').all().length > 0 && $('Read Service Requests Sheet').first().json.row_number != null ? JSON.stringify($('Read Service Requests Sheet').all().map(i => i.json), null, 2) : 'NONE' }}\n\nCONTEXT FLAGS:\n- Is New Guest (not in our system): {{ $json.is_new_profile }}\n- Is Currently Checked In: {{ $json.has_active_reservation }}\n- Has Pending Service Requests: {{ $json.has_pending_requests }}\n- Agent Instruction: {{ $json.system_context }}\n\nAVAILABLE ROOMS:\nR01, R02, R03, R04, R05\n\n---\nGUEST MESSAGE:\n{{ $('When WhatsApp Received').item.json.messages[0].text.body }}",
        "options": {
          "systemMessage": "You are Aria, a warm and efficient WhatsApp concierge for The Grand Luxe Hotel. You communicate only via WhatsApp so keep responses short, friendly, and mobile-readable. Use line breaks, bold, and occasional emojis (\ud83d\udc4b \ud83d\udcc5 \u2705 \ud83c\udfe8) but never long walls of text.\n\nThe current IST date and time are provided in every message. Always use IST when discussing times with guests. Never say \"I don't know the time\" \u2014 it is always in your context.\n\n---\n\n## TOOLS YOU HAVE\n\n1. **Get Hotel Amenities** \u2014 Google Doc with room types, pricing, policies, facilities, restaurant/spa/pool hours, and available in-room services/menus. Call this before answering ANY question about the property, prices, policies, or confirming a service request. Never guess this information.\n\n2. **Create or update the reservation** \u2014 Appends or updates a row in the Reservations sheet. Fields: reservation_id (auto-set), guest_number (auto-set), guest_email (auto-set), check_in (YYYY-MM-DD HH:MM), Check_out (YYYY-MM-DD HH:MM), room_number, num_guests, special_requests, status.\n\n3. **Create a new user** \u2014 Appends a row to Guest Details sheet. Fields filled by AI: name, email. Number and DND are auto-set. Only call this when Is New Guest = true.\n\n4. **Create a service Request** \u2014 Appends a row to Service Requests sheet. Fields filled by AI: service_type, service_name, date (ISO datetime in IST when the service is needed), room_number. request_id, guest_number, guest_email, completed are auto-set.\n\n---\n\n## STEP-BY-STEP DECISION FLOW \u2014 FOLLOW THIS EXACTLY\n\n### ON EVERY MESSAGE, first check:\n\n**Is New Guest = true?**\n\u2192 Greet them warmly by their WhatsApp display name\n\u2192 Tell them you need a few details to get them set up\n\u2192 Ask for: full name, email address\n\u2192 Once they provide both, call **Create a new user**\n\u2192 Confirm: \"\u2705 Got it! Welcome to The Grand Luxe, [name]. Now let me help you with...\"\n\u2192 Then continue to help with whatever they originally asked\n\n**Is New Guest = false?**\n\u2192 Greet them by their name from the guest profile\n\u2192 If Is Currently Checked In = true: open with a check-in greeting first (\"Hope you're enjoying your stay! How can I help?\") before addressing their request\n\u2192 Then handle their request per the flows below\n\n---\n\n### BOOKING A ROOM\n\nCollect ALL of the following before doing anything else. Ask for missing items one message at a time \u2014 don't bombard them with a list of 6 questions at once:\n\n1. \u2705 Check-in **date** (ask: \"What date would you like to check in?\")\n2. \u2705 Check-in **time** (ask: \"And what time do you plan to arrive? Our standard check-in is 2:00 PM IST but we can accommodate early check-in based on availability.\")\n3. \u2705 Check-out **date** (ask: \"And when will you be checking out?\")\n4. \u2705 Check-out **time** (ask: \"What time do you expect to check out? Standard check-out is 11:00 AM IST.\")\n5. \u2705 Number of guests\n6. \u2705 Any special requests (dietary, accessibility, occasion, etc.) \u2014 this can be \"None\"\n\nOnce you have all 6:\n\u2192 Call **Get Hotel Amenities** to get room options and pricing\n\u2192 Present available room types with a brief description and price per night\n\u2192 Ask them to choose a room\n\u2192 Once they choose a room: check availability using the ALL RESERVATIONS ON FILE data already provided in your context. A room is UNAVAILABLE if any existing reservation has the same room_number AND status != \"cancelled\" AND check_in < requested check_out AND check_out > requested check_in (i.e. any date overlap).\n  - If the room IS taken for those dates: apologize and suggest the other available rooms\n  - If the room is free: proceed to confirmation summary\n\n\u2192 Send a confirmation summary BEFORE writing anything:\n\ud83d\udccb Booking Summary\nRoom: [room type] ([room number])\nCheck-in: [day, date] at [time] IST\nCheck-out: [day, date] at [time] IST\nDuration: [X nights]\nGuests: [n]\nSpecial requests: [requests or None]\nTotal: \u20b9[price]\nReply CONFIRM to complete your booking or let me know if you'd like to change anything.\n\n\u2192 Only after they reply CONFIRM: call **Create or update the reservation** with:\n  - check_in: YYYY-MM-DD HH:MM (24hr, IST)\n  - Check_out: YYYY-MM-DD HH:MM (24hr, IST)\n  - room_number: the room code (R01\u2013R05)\n  - num_guests: number\n  - special_requests: their requests\n  - status: confirmed\n\n\u2192 Reply with: \"\u2705 You're all set! Reservation *[reservation_id]* confirmed for [dates]. We look forward to welcoming you! \ud83c\udfe8\"\n\n---\n\n### MODIFYING AN EXISTING RESERVATION\n\n\u2192 All their reservations are already in the context \u2014 do NOT call any tool to fetch them\n\u2192 Ask which reservation they want to change and what they want to change\n\u2192 If changing dates/room: run the availability check again for the new dates against the ALL RESERVATIONS ON FILE data\n\u2192 Confirm changes with them before writing\n\u2192 Call **Create or update the reservation** to update \u2014 the reservation_id will match the existing row\n\n---\n\n### CANCELLING A RESERVATION\n\n\u2192 Identify the reservation from context\n\u2192 Ask: \"Are you sure you want to cancel reservation *[ID]* for [dates]? This cannot be undone.\"\n\u2192 On confirmation: call **Create or update the reservation** with status: cancelled\n\u2192 Confirm cancellation\n\n---\n\n### IN-STAY SERVICE REQUEST (Is Currently Checked In = true)\n\nCollect:\n1. What service they need (housekeeping, room service, maintenance, wake-up call, luggage, etc.)\n2. Specific detail (e.g., \"extra towels\", \"chicken sandwich and a Coke\", \"AC not working\")\n3. What time they need it (ask explicitly: \"When would you like this? Right now, or at a specific time?\")\n4. Confirm their room number (you have it from active reservation context \u2014 just verify)\n\n\u2192 BEFORE logging the request: Call **Get Hotel Amenities** to verify if the requested service or item is actually available at the hotel.\n  - If NOT available: Apologize and let them know we don't offer that specific service/item. Do not create a request.\n  - If available: Proceed below.\n\n\u2192 Call **Create a service Request** with:\n  - service_type: category (e.g., \"Housekeeping\", \"Room Service\", \"Maintenance\")\n  - service_name: specific detail (e.g., \"Extra towels requested\")\n  - date: ISO datetime in IST of when the service is needed (if \"now\" use current IST time)\n  - room_number: their room\n\n\u2192 Reply: \"\u2705 Got it! Your [service] request (Ref: *[request_id]*) has been logged and our team will attend to you shortly. Is there anything else I can help with?\"\n\n---\n\n### GUEST ASKS ABOUT PENDING REQUESTS\n\n\u2192 Read from PENDING SERVICE REQUESTS in context \u2014 do NOT call any tool\n\u2192 Format them naturally: \"You have [n] pending request(s): [list]\"\n\n---\n\n### GUEST ASKS ABOUT HOTEL / PRICING / POLICIES / AMENITIES\n\n\u2192 Always call **Get Hotel Amenities** first\n\u2192 Answer only from what the document says\n\u2192 Never make up prices, timings, or policies\n\n---\n\n### OUT OF SCOPE QUESTION\n\n\u2192 \"I'm only able to help with reservations, in-stay services, and questions about the property. Is there anything along those lines I can help you with? \ud83d\ude0a\"\n\n---\n\n## STRICT RULES\n\n- Never share one guest's data with another\n- Never write to any sheet without explicit guest confirmation first (except creating a new user profile)\n- Always store check_in and Check_out in format: YYYY-MM-DD HH:MM (24-hour IST)\n- Always display dates to guests in human format: \"Monday, 30 June 2025 at 2:00 PM\"\n- Never reveal raw sheet data, row numbers, or internal field names to the guest\n- If a guest without an active reservation asks for in-room service, say: \"In-stay services are available once you're checked in. Would you like to make a reservation?\"\n- The model name you are running on doesn't matter \u2014 never mention it\n- If a guest sends a non-text message (image, audio, sticker), reply: \"I can only read text messages. Please type your request and I'll be happy to help! \ud83d\ude0a\""
        },
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "73bb71f5-05e2-401a-ba8c-51f09a1c3ae3",
      "name": "Gemini AI Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        2176,
        352
      ],
      "parameters": {
        "options": {},
        "modelName": "models/gemini-3.1-flash-lite"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "ef14d628-d579-4f30-ad6e-70f4651f8c9b",
      "name": "Read Reservations Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1504,
        128
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $('When WhatsApp Received').item.json.contacts[0].wa_id }}",
              "lookupColumn": "guest_number"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JJL5yDi1HOutfREgGQebHDDeFErgngJFjNiLmXSQYWM/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1JJL5yDi1HOutfREgGQebHDDeFErgngJFjNiLmXSQYWM",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JJL5yDi1HOutfREgGQebHDDeFErgngJFjNiLmXSQYWM/edit?usp=drivesdk",
          "cachedResultName": "Reservations"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7,
      "alwaysOutputData": true
    },
    {
      "id": "eb791f6d-aa28-4c8f-9d59-611a78f303e4",
      "name": "Filter Active Reservation",
      "type": "n8n-nodes-base.code",
      "position": [
        1952,
        128
      ],
      "parameters": {
        "jsCode": "// Loop dynamically over all incoming items\nreturn $input.all().map(item => {\n    \n    let isNewProfile = true;\n    let contextInstructions = [];\n    \n    // 1. Check if user exists (via 'Get Contact' node)\n    try {\n        const contactItems = $('Read Guest Details Sheet').all();\n        // If the 'Get Contact' node output data AND it has a row_number, it is NOT a new profile\n        if (contactItems.length > 0 && contactItems[0].json.row_number != null) {\n            isNewProfile = false;\n        }\n    } catch (error) {\n        // If node didn't execute, default to true\n        isNewProfile = true;\n    }\n\n    if (isNewProfile) {\n        contextInstructions.push(\"The user is not in the guest sheet. Start asking the user for their details to assist with the query and create a new account in the system.\");\n    } else {\n        contextInstructions.push(\"The user profile exists in the system.\");\n    }\n\n    // 2. Check for active reservations (via 'Get reservations' node)\n    let activeReservations = [];\n    try {\n        const reservationItems = $('Read Reservations Sheet').all();\n        \n        // Get today's local date dynamically in YYYY-MM-DD format\n        const now = new Date();\n        const year = now.getFullYear();\n        const month = String(now.getMonth() + 1).padStart(2, '0');\n        const day = String(now.getDate()).padStart(2, '0');\n        const todayStr = `${year}-${month}-${day}`;\n\n        // Filter valid reservations\n        activeReservations = reservationItems.map(req => req.json).filter(res => {\n            if (!res.check_in || !res.Check_out) return false;\n            \n            const checkInDate = res.check_in.split('T')[0];\n            const checkOutDate = res.Check_out.split('T')[0];\n            \n            return todayStr >= checkInDate && todayStr < checkOutDate;\n        });\n    } catch (error) {\n        activeReservations = [];\n    }\n\n    if (activeReservations.length === 0) {\n        contextInstructions.push(\"There are no active reservations for this user.\");\n    } else {\n        contextInstructions.push(`The user has ${activeReservations.length} active reservation(s).`);\n    }\n\n    // 3. Check for pending service requests (via 'Get Pending Service requests' node)\n    let pendingRequests = [];\n    try {\n        const serviceItems = $('Read Service Requests Sheet').all();\n        // Verify valid data was actually returned\n        if (serviceItems.length > 0 && serviceItems[0].json.row_number != null) {\n            pendingRequests = serviceItems.map(req => req.json);\n        }\n    } catch (error) {\n        pendingRequests = [];\n    }\n\n    if (pendingRequests.length === 0) {\n        contextInstructions.push(\"There are no pending service tasks. Ask the user if they need any new services.\");\n    } else {\n        contextInstructions.push(`The user has ${pendingRequests.length} pending service task(s) currently ongoing.`);\n    }\n\n    // Return the processed data back to n8n\n    return {\n        json: {\n            ...item.json,\n            is_new_profile: isNewProfile,\n            has_active_reservation: activeReservations.length > 0,\n            active_reservations: activeReservations,\n            has_pending_requests: pendingRequests.length > 0,\n            pending_requests: pendingRequests,\n            \n            // This string contains all the logical instructions for your AI agent\n            system_context: contextInstructions.join(\" \")\n        }\n    };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "d9761b7b-ece0-4e67-a2f9-b582a1ce7fcd",
      "name": "Read Service Requests Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1728,
        128
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $('When WhatsApp Received').item.json.contacts[0].wa_id }}",
              "lookupColumn": "guest_number"
            },
            {
              "lookupValue": "false",
              "lookupColumn": "completed"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nqPJKajjUaYxHpTX6tTmpWNsFyn5PJzxmORRer0Ypbw/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1nqPJKajjUaYxHpTX6tTmpWNsFyn5PJzxmORRer0Ypbw",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nqPJKajjUaYxHpTX6tTmpWNsFyn5PJzxmORRer0Ypbw/edit?usp=drivesdk",
          "cachedResultName": "Service Requests"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7,
      "alwaysOutputData": true
    },
    {
      "id": "b88a3f49-46fe-4392-b03d-8fef0abbe902",
      "name": "Get Hotel Aminities",
      "type": "n8n-nodes-base.googleDocsTool",
      "position": [
        2496,
        352
      ],
      "parameters": {
        "operation": "get",
        "documentURL": "https://docs.google.com/document/d/1FmZqMn6ZfV6rFkOw8bTBQZj-pCr-_FvQro30mgd3kkY/edit?usp=sharing",
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "7f9ced02-48ac-4668-b28b-48cedec68009",
      "name": "Upsert Reservation Tool",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        2672,
        352
      ],
      "parameters": {
        "columns": {
          "value": {
            "check_in": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('check_in', ``, 'string') }}",
            "Check_out": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Check_out', ``, 'string') }}",
            "guest_email": "={{ $('Read Guest Details Sheet').item.json.email }}",
            "room_number": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('room_number', ``, 'string') }}",
            "guest_number": "={{ $('Read Guest Details Sheet').item.json.number }}",
            "reservation_id": "={{ 're' + $now.valueOf() + Math.random().toString(36).substring(2, 6) }}",
            "number_of_guests": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('number_of_guests', ``, 'string') }}",
            "special_requests": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('special_requests', ``, 'string') }}"
          },
          "schema": [
            {
              "id": "reservation_id",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "reservation_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "room_number",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "room_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "number_of_guests",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "number_of_guests",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "guest_number",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "guest_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "guest_email",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "guest_email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "check_in",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "check_in",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Check_out",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Check_out",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "special_requests",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "special_requests",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "reservation_id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JJL5yDi1HOutfREgGQebHDDeFErgngJFjNiLmXSQYWM/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1JJL5yDi1HOutfREgGQebHDDeFErgngJFjNiLmXSQYWM",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JJL5yDi1HOutfREgGQebHDDeFErgngJFjNiLmXSQYWM/edit?usp=drivesdk",
          "cachedResultName": "Reservations"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "f2de2532-5f71-4a08-ad88-430751b13b85",
      "name": "Create User Tool",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        2848,
        352
      ],
      "parameters": {
        "columns": {
          "value": {
            "DND": "no",
            "name": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('name', ``, 'string') }}",
            "email": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('email', ``, 'string') }}",
            "number": "={{ $('When WhatsApp Received').item.json.contacts[0].wa_id }}"
          },
          "schema": [
            {
              "id": "name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "number",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "DND",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "DND",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "number"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/16FkrLkrW2Te8mUGQ70qWX3PMZV3yqJAZx1zoBB07Zuk/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "16FkrLkrW2Te8mUGQ70qWX3PMZV3yqJAZx1zoBB07Zuk",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/16FkrLkrW2Te8mUGQ70qWX3PMZV3yqJAZx1zoBB07Zuk/edit?usp=drivesdk",
          "cachedResultName": "Guest Details"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "88f53cdb-d716-45d9-a224-cca0d3664e17",
      "name": "Create Service Request Tool",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        3008,
        352
      ],
      "parameters": {
        "columns": {
          "value": {
            "date": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('date', ``, 'string') }}",
            "completed": "False",
            "request_id": "={{ 'req' + $now.valueOf() + Math.random().toString(36).substring(2, 6) }}",
            "guest_email": "={{ $('Read Guest Details Sheet').item.json.email }}",
            "guest_number": "={{ $('Read Guest Details Sheet').item.json.number }}",
            "service_name": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('service_name', ``, 'string') }}",
            "service_type": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('service_type', ``, 'string') }}"
          },
          "schema": [
            {
              "id": "request_id",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "request_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "guest_email",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "guest_email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "guest_number",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "guest_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "service_type",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "service_type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "service_name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "service_name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "completed",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "completed",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "request_id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nqPJKajjUaYxHpTX6tTmpWNsFyn5PJzxmORRer0Ypbw/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1nqPJKajjUaYxHpTX6tTmpWNsFyn5PJzxmORRer0Ypbw",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nqPJKajjUaYxHpTX6tTmpWNsFyn5PJzxmORRer0Ypbw/edit?usp=drivesdk",
          "cachedResultName": "Service Requests"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "0c775846-0b13-4f42-8501-2b8c27e3dafb",
      "name": "Send WhatsApp Response",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        3424,
        128
      ],
      "parameters": {
        "textBody": "={{ $json.output }}",
        "operation": "send",
        "phoneNumberId": "1083481144853090",
        "additionalFields": {},
        "recipientPhoneNumber": "={{ $('When WhatsApp Received').item.json.contacts[0].wa_id }}"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "da4c8a7d-f3a0-4300-938c-aa1de4eb4ac1",
      "name": "When Sheet Row Added",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "position": [
        608,
        720
      ],
      "parameters": {
        "event": "rowAdded",
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JJL5yDi1HOutfREgGQebHDDeFErgngJFjNiLmXSQYWM/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1JJL5yDi1HOutfREgGQebHDDeFErgngJFjNiLmXSQYWM",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JJL5yDi1HOutfREgGQebHDDeFErgngJFjNiLmXSQYWM/edit?usp=drivesdk",
          "cachedResultName": "Reservations"
        }
      },
      "credentials": {
        "googleSheetsTriggerOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "128c99a7-053b-40f0-9512-d4375023f1be",
      "name": "Sync Masked Reservations",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        832,
        720
      ],
      "parameters": {
        "columns": {
          "value": {
            "check_in": "={{ $json.check_in }}",
            "Check_out": "={{ $json.Check_out }}",
            "Room Number": "={{ $json.room_number }}",
            "reservation_id": "={{ $json.reservation_id }}"
          },
          "schema": [
            {
              "id": "reservation_id",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "reservation_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Room Number",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Room Number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "check_in",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "check_in",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Check_out",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Check_out",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "reservation_id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1KRLC8sO1jTBfRDCJ9lKYC6tn_Fj0HvMCfLSE0iT_lxs/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1KRLC8sO1jTBfRDCJ9lKYC6tn_Fj0HvMCfLSE0iT_lxs",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1KRLC8sO1jTBfRDCJ9lKYC6tn_Fj0HvMCfLSE0iT_lxs/edit?usp=drivesdk",
          "cachedResultName": "Masked Reservations"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "717878d0-c198-4cef-a659-80e61df16295",
      "name": "Load Chat Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryRedisChat",
      "position": [
        2336,
        352
      ],
      "parameters": {
        "sessionKey": "={{ $('When WhatsApp Received').item.json.contacts[0].wa_id }}",
        "sessionIdType": "customKey"
      },
      "credentials": {
        "redis": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.6
    },
    {
      "id": "c9675849-d695-447e-8296-6495bfac71ed",
      "name": "Fetch Active Reservations Tool",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        3168,
        352
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('values0_Value', ``, 'string') }}",
              "lookupColumn": "check_in"
            },
            {
              "lookupValue": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('values1_Value', ``, 'string') }}",
              "lookupColumn": "Check_out"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JJL5yDi1HOutfREgGQebHDDeFErgngJFjNiLmXSQYWM/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1JJL5yDi1HOutfREgGQebHDDeFErgngJFjNiLmXSQYWM",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JJL5yDi1HOutfREgGQebHDDeFErgngJFjNiLmXSQYWM/edit?usp=drivesdk",
          "cachedResultName": "Reservations"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "0608a553-167b-408d-92e9-72a101b06c87",
      "name": "Check If DND Enabled",
      "type": "n8n-nodes-base.if",
      "position": [
        1280,
        144
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "5dd745a6-5671-41f4-b9af-2f3cf93fd2f4",
              "operator": {
                "type": "boolean",
                "operation": "false",
                "singleValue": true
              },
              "leftValue": "={{ $json.DND }}",
              "rightValue": false
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "7702afa3-2ff7-42e4-9265-702b346d102b",
      "name": "Update DND Status Tool",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        3360,
        352
      ],
      "parameters": {
        "columns": {
          "value": {
            "DND": "yes",
            "name": "={{ $('When WhatsApp Received').item.json.contacts[0].wa_id }}"
          },
          "schema": [
            {
              "id": "name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "number",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "DND",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "DND",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "name"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/16FkrLkrW2Te8mUGQ70qWX3PMZV3yqJAZx1zoBB07Zuk/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "16FkrLkrW2Te8mUGQ70qWX3PMZV3yqJAZx1zoBB07Zuk",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/16FkrLkrW2Te8mUGQ70qWX3PMZV3yqJAZx1zoBB07Zuk/edit?usp=drivesdk",
          "cachedResultName": "Guest Details"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "98974a99-5cd6-45a0-93b9-7d597cecb0ba",
  "nodeGroups": [],
  "connections": {
    "Gemini AI Model": {
      "ai_languageModel": [
        [
          {
            "node": "Hotel Concierge Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Create User Tool": {
      "ai_tool": [
        [
          {
            "node": "Hotel Concierge Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Load Chat Memory": {
      "ai_memory": [
        [
          {
            "node": "Hotel Concierge Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Check Message Body": {
      "main": [
        [
          {
            "node": "Read Guest Details Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check If DND Enabled": {
      "main": [
        [
          {
            "node": "Read Reservations Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Sheet Row Added": {
      "main": [
        [
          {
            "node": "Sync Masked Reservations",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Hotel Concierge Agent": {
      "main": [
        [
          {
            "node": "Send WhatsApp Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update DND Status Tool": {
      "ai_tool": [
        [
          {
            "node": "Hotel Concierge Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "When WhatsApp Received": {
      "main": [
        [
          {
            "node": "Check Message Body",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Reservations Sheet": {
      "main": [
        [
          {
            "node": "Read Service Requests Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upsert Reservation Tool": {
      "ai_tool": [
        [
          {
            "node": "Hotel Concierge Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Read Guest Details Sheet": {
      "main": [
        [
          {
            "node": "Check If DND Enabled",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Active Reservation": {
      "main": [
        [
          {
            "node": "Hotel Concierge Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Service Request Tool": {
      "ai_tool": [
        [
          {
            "node": "Hotel Concierge Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Read Service Requests Sheet": {
      "main": [
        [
          {
            "node": "Filter Active Reservation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Active Reservations Tool": {
      "ai_tool": [
        [
          {
            "node": "Hotel Concierge Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    }
  }
}