{
  "id": "TKCKu5no17nDCESW",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Handle WhatsApp support and Cal.com bookings with Gemini and Google Docs",
  "tags": [],
  "nodes": [
    {
      "id": "db06d1c1-abf6-4b60-9a07-217a3f1ee07b",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2400,
        -448
      ],
      "parameters": {
        "width": 480,
        "height": 896,
        "content": "## WhatsApp AI Support + Cal.com\n\n### How it works\n\nThis workflow handles incoming WhatsApp support messages, checks whether the conversation is within WhatsApp\u2019s 24-hour messaging window, and either sends a reopen template or continues with automated support. It pulls company knowledge from Google Docs, uses Gemini-powered AI agents to answer the user and extract booking intent, then attempts to create a Cal.com booking when enough details are available. It composes a final WhatsApp reply and separately logs AI-handled interactions to Google Sheets.\n\n### Setup steps\n\n- Configure WhatsApp Cloud API credentials for both the trigger and reply/template sending nodes, including the approved reopen template used outside the 24-hour window.\n- Connect Google Docs credentials and set the company knowledge document used by the Company Knowledge node.\n- Configure Google Gemini credentials for both AI agent chat model subnodes.\n- Add Cal.com API credentials or headers for the booking HTTP request, and verify the event type, attendee fields, timezone, and booking endpoint payload.\n- Connect Google Sheets credentials and choose the spreadsheet/sheet used for logging conversations.\n- Review the code nodes for expected WhatsApp payload fields, date/time normalization rules, and required booking fields.\n\n### Customization\n\nAdjust the company knowledge document, AI prompts, booking eligibility rules, timezone handling, Cal.com event details, WhatsApp reply wording, and Google Sheets log columns to match the business process."
      },
      "typeVersion": 1
    },
    {
      "id": "257de54a-22cf-4d7b-bc17-874c4fdacf8c",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1840,
        -80
      ],
      "parameters": {
        "color": 7,
        "width": 880,
        "height": 464,
        "content": "## WhatsApp intake gate\n\nReceives the inbound WhatsApp message, normalizes the payload, checks whether the conversation is still inside the 24-hour window, and sends a reopen template when normal replies are not allowed."
      },
      "typeVersion": 1
    },
    {
      "id": "e8d5313b-6638-442e-afbc-43f1fc5c5423",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -928,
        -256
      ],
      "parameters": {
        "color": 7,
        "height": 384,
        "content": "## Load company knowledge\n\nFetches the configured Google Docs knowledge source that feeds both the general support response flow and the booking extraction flow."
      },
      "typeVersion": 1
    },
    {
      "id": "136a5c17-f211-4e2b-9d9e-86133dbcb371",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -768,
        176
      ],
      "parameters": {
        "color": 7,
        "width": 832,
        "height": 496,
        "content": "## Extract booking intent\n\nBuilds the booking extraction prompt, runs a Gemini-backed AI agent, and parses the returned booking JSON into structured data."
      },
      "typeVersion": 1
    },
    {
      "id": "bf83d5d5-36bd-4444-a4ce-3403a4389682",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        128,
        112
      ],
      "parameters": {
        "color": 7,
        "width": 496,
        "height": 320,
        "content": "## Validate booking request\n\nNormalizes extracted date and time information, then determines whether the workflow has enough valid details to attempt a Cal.com booking."
      },
      "typeVersion": 1
    },
    {
      "id": "7bc105db-d563-4bdb-87ea-b9b4a9f5cf5c",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -304,
        -288
      ],
      "parameters": {
        "color": 7,
        "height": 336,
        "content": "## Build support context\n\nTransforms the retrieved company knowledge into usable context for the support AI response."
      },
      "typeVersion": 1
    },
    {
      "id": "4c4c2a6d-ee70-4ac6-9183-1de54e993dc4",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        672,
        -448
      ],
      "parameters": {
        "color": 7,
        "width": 576,
        "height": 512,
        "content": "## Generate AI answer\n\nPrepares the main support prompt and runs the Gemini-backed AI agent with conversation memory to generate the user-facing assistance response."
      },
      "typeVersion": 1
    },
    {
      "id": "2be8b5f9-6138-45b3-b497-1360fc658873",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        960,
        96
      ],
      "parameters": {
        "color": 7,
        "height": 752,
        "content": "## Handle booking outcome\n\nCreates the Cal.com booking when validation succeeds, or prepares a fallback booking reply when the request is missing required details or cannot be attempted."
      },
      "typeVersion": 1
    },
    {
      "id": "88ea8ab1-4c0b-4246-aabb-e6db1adc6296",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1632,
        -384
      ],
      "parameters": {
        "color": 7,
        "width": 464,
        "height": 304,
        "content": "## Log interaction details\n\nAdds a timestamp and records the AI-handled interaction in Google Sheets for tracking or reporting."
      },
      "typeVersion": 1
    },
    {
      "id": "7843ed32-821b-4edc-830d-38db1b5ad428",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1680,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 320,
        "content": "## Send final reply\n\nCombines the AI answer with any booking result or fallback message, then sends the final response back to the user on WhatsApp."
      },
      "typeVersion": 1
    },
    {
      "id": "7422bf0b-2bdc-49ad-9e07-d7acada4b7de",
      "name": "Extract Booking Details",
      "type": "n8n-nodes-base.code",
      "position": [
        -720,
        304
      ],
      "parameters": {
        "jsCode": "const user = $('Normalize Message Input').first().json;\nconst ctx = $('Build AI Context').first().json.docContext || '';\nconst prompt = [\n  'You extract booking details from WhatsApp messages.',\n  'Return only valid minified JSON. No markdown. No explanation.',\n  '{\"bookingIntent\":boolean,\"name\":\"\",\"email\":\"\",\"dateText\":\"\",\"timeText\":\"\",\"timezone\":\"\",\"notes\":\"\"}',\n  '',\n  'User message:',\n  user.body,\n  '',\n  'Business context:',\n  ctx\n].join('\\n');\nreturn [{ json: { extractorPrompt: prompt } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "a454753d-029a-4bdb-9dd5-bb5dd68ca679",
      "name": "Booking Information Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "maxTries": 2,
      "position": [
        -496,
        304
      ],
      "parameters": {
        "text": "={{ $json.extractorPrompt }}",
        "options": {
          "systemMessage": "Extract booking data only. Output strict JSON only."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "retryOnFail": false,
      "typeVersion": 1.7,
      "alwaysOutputData": true
    },
    {
      "id": "efa36982-d66e-4ecf-b9bb-846a00de5dc9",
      "name": "Parse Booking Data",
      "type": "n8n-nodes-base.code",
      "position": [
        -80,
        304
      ],
      "parameters": {
        "jsCode": "let raw = $('Booking Information Agent').first().json.output || '';\nconst match = raw.match(/\\{[\\s\\S]*\\}/);\nlet obj = { bookingIntent: false, name: '', email: '', dateText: '', timeText: '', timezone: 'Asia/Kolkata', notes: '' };\nif (match) {\n  try { obj = { ...obj, ...JSON.parse(match[0]) }; } catch (e) {}\n}\nobj.bookingIntent = !!obj.bookingIntent;\nobj.name = obj.name || $('Normalize Message Input').first().json.name || '';\nobj.email = obj.email || '';\nobj.dateText = obj.dateText || '';\nobj.timeText = obj.timeText || '';\nobj.timezone = obj.timezone || 'Asia/Kolkata';\nobj.notes = obj.notes || '';\nreturn [{ json: obj }];"
      },
      "typeVersion": 2
    },
    {
      "id": "8b85b77e-12a9-4280-9513-0fe707658a85",
      "name": "Normalize Date and Time",
      "type": "n8n-nodes-base.code",
      "position": [
        176,
        272
      ],
      "parameters": {
        "jsCode": "const { dateText, timeText, timezone, bookingIntent, email, name, notes } = $json;\nfunction normalizeDate(d) {\n  if (!d) return '';\n  const t = d.trim().toLowerCase();\n  const now = new Date();\n  const pad = n => String(n).padStart(2, '0');\n  if (t === 'today') return `${now.getFullYear()}-${pad(now.getMonth()+1)}-${pad(now.getDate())}`;\n  if (t === 'tomorrow') {\n    const x = new Date(now.getTime() + 86400000);\n    return `${x.getFullYear()}-${pad(x.getMonth()+1)}-${pad(x.getDate())}`;\n  }\n  if (/^\\d{4}-\\d{2}-\\d{2}$/.test(t)) return t;\n  if (/^\\d{1,2}[\\/\\-]\\d{1,2}[\\/\\-]\\d{2,4}$/.test(t)) {\n    const [dd, mm, yy] = t.split(/[\\/\\-]/);\n    const year = yy.length === 2 ? '20' + yy : yy;\n    return `${year}-${pad(mm)}-${pad(dd)}`;\n  }\n  const weekdays = ['sunday','monday','tuesday','wednesday','thursday','friday','saturday'];\n  if (weekdays.includes(t)) {\n    const target = weekdays.indexOf(t);\n    const current = now.getDay();\n    let diff = (target - current + 7) % 7;\n    if (diff === 0) diff = 7;\n    const x = new Date(now.getTime() + diff * 86400000);\n    return `${x.getFullYear()}-${pad(x.getMonth()+1)}-${pad(x.getDate())}`;\n  }\n  return '';\n}\nfunction normalizeTime(t) {\n  if (!t) return '';\n  const x = t.trim().toLowerCase();\n  if (x.includes('morning')) return '10:00';\n  if (x.includes('afternoon')) return '15:00';\n  if (x.includes('evening')) return '18:00';\n  const m = x.match(/(\\d{1,2})(?::(\\d{2}))?\\s*(am|pm)?/);\n  if (!m) return '';\n  let hour = parseInt(m[1], 10);\n  const min = m[2] || '00';\n  const ampm = m[3];\n  if (ampm === 'pm' && hour < 12) hour += 12;\n  if (ampm === 'am' && hour === 12) hour = 0;\n  return `${String(hour).padStart(2,'0')}:${min}`;\n}\nconst dateISO = normalizeDate(dateText);\nconst timeISO = normalizeTime(timeText);\nconst startISO = (dateISO && timeISO) ? `${dateISO}T${timeISO}:00.000Z` : '';\nconst canAttemptBooking = !!(bookingIntent && name && email && startISO);\nreturn [{ json: { bookingIntent, name, email, notes, timezone, dateText, timeText, dateISO, timeISO, startISO, canAttemptBooking } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "f6367998-0408-4fd6-a9ed-7650142e13ed",
      "name": "Check Booking Feasibility",
      "type": "n8n-nodes-base.if",
      "position": [
        480,
        272
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond_book",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.canAttemptBooking }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "2a4750b0-432d-45b7-affa-e75826241ea6",
      "name": "Handle Booking Failure",
      "type": "n8n-nodes-base.code",
      "position": [
        1024,
        688
      ],
      "parameters": {
        "jsCode": "const username = 'CAL_USERNAME';\nconst eventSlug = '30min';\nconst link = `https://cal.com/${username}/${eventSlug}`;\nconst missing = [];\nconst p = $('Normalize Date and Time').first().json;\nif (!p.name) missing.push('name');\nif (!p.email) missing.push('email');\nif (!p.dateISO) missing.push('date');\nif (!p.timeISO) missing.push('time');\nlet answer = '';\nif ($('Parse Booking Data').first().json.bookingIntent) {\n  answer = missing.length\n    ? `To book your meeting, please send your ${missing.join(', ')}. Or use this booking link: ${link}`\n    : `I could not confirm that slot automatically. Please use this booking link to choose an available time: ${link}`;\n} else {\n  answer = '';\n}\nreturn [{ json: { answer, intent: 'booking_link_or_missing_fields' } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "de404a01-0616-4306-8244-6dfe89afd820",
      "name": "When WhatsApp Message Received",
      "type": "n8n-nodes-base.whatsAppTrigger",
      "position": [
        -1792,
        80
      ],
      "parameters": {
        "options": {},
        "updates": [
          "messages"
        ]
      },
      "typeVersion": 1
    },
    {
      "id": "0cba39eb-cb1f-43b9-b954-8783273162c1",
      "name": "Normalize Message Input",
      "type": "n8n-nodes-base.code",
      "position": [
        -1568,
        80
      ],
      "parameters": {
        "jsCode": "const msg = $json.messages?.[0] || {};\nconst contact = $json.contacts?.[0] || {};\nconst tsSec = Number(msg.timestamp || 0);\nconst tsMs = tsSec * 1000;\nconst withinWindow = tsSec > 0 && (Date.now() - tsMs) < 24 * 60 * 60 * 1000;\nconst body = (msg.text?.body || '').trim();\nconst from = msg.from || contact.wa_id || '';\nconst name = contact.profile?.name || '';\nreturn [{ json: { withinWindow, body, from, name, raw: $json } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "008bb245-cca9-4943-a567-98d976ac83a0",
      "name": "Check 24h Message Window",
      "type": "n8n-nodes-base.if",
      "position": [
        -1344,
        80
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond_24h",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.withinWindow }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "0b774479-f632-4bfd-b209-177f329ed467",
      "name": "Send WhatsApp Reopen Message",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        -1104,
        224
      ],
      "parameters": {
        "template": "approved_reopen_template|en_US",
        "phoneNumberId": "BUSINESS_PHONE_NUMBER_ID",
        "recipientPhoneNumber": "={{ $json.from }}"
      },
      "typeVersion": 1
    },
    {
      "id": "0601ad2d-4495-4888-9392-3833b3fd1a0a",
      "name": "Retrieve Company Knowledge",
      "type": "n8n-nodes-base.googleDocs",
      "position": [
        -880,
        -32
      ],
      "parameters": {
        "operation": "get",
        "documentURL": "=YOUR_GOOGLE_DOC_ID"
      },
      "credentials": {
        "googleDocsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "d458ff7b-ed3d-4ff8-8d55-276839c5516e",
      "name": "Build AI Context",
      "type": "n8n-nodes-base.code",
      "position": [
        -256,
        -112
      ],
      "parameters": {
        "jsCode": "const doc = $json;\nlet text = doc.content || '';\nif (Array.isArray(doc.body?.content)) {\n  const runs = [];\n  for (const block of doc.body.content) {\n    const paras = block.paragraph?.elements || [];\n    for (const el of paras) {\n      const t = el.textRun?.content || '';\n      if (t.trim()) runs.push(t.replace(/\\n+/g, ' ').trim());\n    }\n  }\n  text = runs.join(' ').replace(/\\s+/g, ' ').trim();\n}\nconst MAX_CHARS = 3500;\nif (text.length > MAX_CHARS) text = text.slice(0, MAX_CHARS) + ' ...';\nreturn [{ json: { docContext: text } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "b410a80c-13b4-465d-870b-6ade9e933c22",
      "name": "Google Chat Model (Booking)",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -496,
        544
      ],
      "parameters": {
        "options": {},
        "modelName": "models/gemini-2.5-flash-preview-04-17-thinking"
      },
      "typeVersion": 1
    },
    {
      "id": "ecbd8d3e-bb25-4803-8ebd-06aad1244011",
      "name": "Store Simple Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        1040,
        -80
      ],
      "parameters": {
        "sessionKey": "={{ $('Normalize Message Input').item.json.from }}",
        "sessionIdType": "customKey"
      },
      "typeVersion": 1.3
    },
    {
      "id": "71dcc0b4-1082-470f-92c7-3c09c6a18f5b",
      "name": "Post Cal Booking",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1008,
        336
      ],
      "parameters": {
        "url": "https://api.cal.com/v2/bookings",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"eventTypeId\": CAL_EVENT_TYPE_ID,\n  \"start\": \"{{$json.startISO}}\",\n  \"attendee\": {\n    \"name\": \"{{$json.name}}\",\n    \"email\": \"{{$json.email}}\",\n    \"timeZone\": \"{{$json.timezone || 'Asia/Kolkata'}}\",\n    \"language\": \"en\"\n  },\n  \"metadata\": {\n    \"source\": \"whatsapp_n8n\",\n    \"dateText\": \"{{$json.dateText}}\",\n    \"timeText\": \"{{$json.timeText}}\",\n    \"notes\": \"{{$json.notes || ''}}\"\n  }\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=CAL_API_KEY"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "cal-api-version",
              "value": "2026-02-25"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "83a01c1f-17fe-48c5-baf1-a2f10b310f54",
      "name": "Prepare AI Booking Prompt",
      "type": "n8n-nodes-base.code",
      "position": [
        720,
        -288
      ],
      "parameters": {
        "jsCode": "const docContext = $('Build AI Context').first().json.docContext || '';\nconst user = $('Normalize Message Input').first().json;\nconst booking = $('Normalize Date and Time').first().json;\nconst finalPrompt = [\n  'You are Black Ball Sporting Club support on WhatsApp.',\n  'Reply in concise plain text only.',\n  'Use only the business context and recent conversation.',\n  'If user is trying to book but details are incomplete, ask only for the missing items.',\n  'Never invent pricing, policies, schedules, or contact details.',\n  'If unsure, say so and offer human help.',\n  '',\n  'Business context:',\n  docContext,\n  '',\n  `Customer name: ${user.name || ''}`,\n  `Customer message: ${user.body || ''}`,\n  `Booking intent: ${booking.bookingIntent}`,\n  `Extracted email: ${booking.email}`,\n  `Extracted date: ${booking.dateText}`,\n  `Extracted time: ${booking.timeText}`\n].join('\\n');\nreturn [{ json: { finalPrompt } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "a7365604-ec2a-4d7e-919a-5b7225b9811a",
      "name": "General Support Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "maxTries": 3,
      "position": [
        944,
        -288
      ],
      "parameters": {
        "text": "={{ $json.finalPrompt }}",
        "options": {
          "systemMessage": "You are a WhatsApp support and booking assistant. Keep replies short, clear, and human. No markdown, no bullet points, no headings. Never invent pricing, times, or policies. If booking details are incomplete, ask only for the missing fields."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "retryOnFail": false,
      "typeVersion": 1.7,
      "alwaysOutputData": true
    },
    {
      "id": "6c512260-aecf-4c51-87a4-13bf24b0265b",
      "name": "Compose WhatsApp Response",
      "type": "n8n-nodes-base.code",
      "position": [
        1728,
        160
      ],
      "parameters": {
        "jsCode": "let answer = '';\nlet intent = 'support';\nconst bookingIntent = $('Parse Booking Data').first().json.bookingIntent;\n\ntry {\n  const r = $('Post Cal Booking').first().json;\n  const statusCode = r.statusCode || 200;\n  if (statusCode >= 200 && statusCode < 300) {\n    const d = r.data || r;\n    const uid = d.uid || d.id || '';\n    const start = d.start || d.startTime || $('Normalize Date and Time').first().json.startISO || '';\n    answer = `Your meeting is booked. Booking ID: ${uid}. Time: ${start}.`;\n    intent = 'booking_confirmed';\n  } else if (statusCode === 400 || statusCode === 409) {\n    answer = $('Handle Booking Failure').first().json.answer;\n    intent = 'booking_slot_unavailable';\n  }\n} catch (e) {}\n\nif (!answer) {\n  if (bookingIntent) {\n    try {\n      const fallback = $('Handle Booking Failure').first().json.answer;\n      if (fallback) {\n        answer = fallback;\n        intent = $('Handle Booking Failure').first().json.intent;\n      }\n    } catch (e) {}\n  }\n}\n\nif (!answer) {\n  try {\n    answer = $('General Support Agent').first().json.output || 'Sorry, I could not process that right now.';\n    intent = 'support';\n  } catch (e) {\n    answer = 'Sorry, I could not process that right now.';\n  }\n}\n\nanswer = answer.replace(/[*_~`]+/g, '').replace(/\\n{3,}/g, '\\n\\n').trim();\nif (answer.length > 600) answer = answer.slice(0, 597) + '...';\nreturn [{ json: { answer, intent } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "776e8af3-ea8c-4371-ba85-e6ac5e5c45db",
      "name": "Send WhatsApp Response",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        1936,
        160
      ],
      "parameters": {
        "textBody": "={{ $json.answer }}",
        "operation": "send",
        "phoneNumberId": "BUSINESS_PHONE_NUMBER_ID",
        "additionalFields": {},
        "recipientPhoneNumber": "={{ $('Normalize Message Input').item.json.from }}"
      },
      "typeVersion": 1
    },
    {
      "id": "452d5638-cba0-4cb7-ba7a-d1cadf905489",
      "name": "Fetch Current Date & Time",
      "type": "n8n-nodes-base.dateTime",
      "position": [
        1680,
        -256
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 2
    },
    {
      "id": "bf50d21f-b09f-402d-b1da-7ddc4fc7ba6a",
      "name": "Append Log to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1952,
        -256
      ],
      "parameters": {
        "columns": {
          "value": {
            "User": "={{ $('Normalize Message Input').item.json.from }}",
            "Intent": "={{ $('Compose WhatsApp Response').item.json.intent }}",
            "Message": "={{ $('Normalize Message Input').item.json.body }}",
            "Response": "={{ $('Compose WhatsApp Response').item.json.answer }}",
            "Timestamp": "={{ $('Fetch Current Date & Time').item.json.timestamp }}"
          },
          "schema": [
            {
              "id": "Timestamp",
              "type": "string",
              "displayName": "Timestamp"
            },
            {
              "id": "User",
              "type": "string",
              "displayName": "User"
            },
            {
              "id": "Message",
              "type": "string",
              "displayName": "Message"
            },
            {
              "id": "Response",
              "type": "string",
              "displayName": "Response"
            },
            {
              "id": "Intent",
              "type": "string",
              "displayName": "Intent"
            }
          ],
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "append",
        "sheetName": "Sheet1",
        "documentId": "YOUR_SHEET_ID"
      },
      "typeVersion": 4.6
    },
    {
      "id": "e71650df-c73e-4b50-8b5e-54786f5fc376",
      "name": "Google Chat Model (Support)",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        864,
        -80
      ],
      "parameters": {
        "options": {},
        "modelName": "models/gemini-2.5-flash-preview-04-17-thinking"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "dfaa90b1-fed0-4671-8e9a-98ac6b508a30",
  "connections": {
    "Build AI Context": {
      "main": [
        [
          {
            "node": "Prepare AI Booking Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post Cal Booking": {
      "main": [
        [
          {
            "node": "Compose WhatsApp Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Booking Data": {
      "main": [
        [
          {
            "node": "Normalize Date and Time",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store Simple Memory": {
      "ai_memory": [
        [
          {
            "node": "General Support Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "General Support Agent": {
      "main": [
        [
          {
            "node": "Compose WhatsApp Response",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch Current Date & Time",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Handle Booking Failure": {
      "main": [
        [
          {
            "node": "Compose WhatsApp Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Booking Details": {
      "main": [
        [
          {
            "node": "Booking Information Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Date and Time": {
      "main": [
        [
          {
            "node": "Check Booking Feasibility",
            "type": "main",
            "index": 0
          },
          {
            "node": "Prepare AI Booking Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Message Input": {
      "main": [
        [
          {
            "node": "Check 24h Message Window",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check 24h Message Window": {
      "main": [
        [
          {
            "node": "Retrieve Company Knowledge",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send WhatsApp Reopen Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Booking Information Agent": {
      "main": [
        [
          {
            "node": "Parse Booking Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Booking Feasibility": {
      "main": [
        [
          {
            "node": "Post Cal Booking",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Handle Booking Failure",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compose WhatsApp Response": {
      "main": [
        [
          {
            "node": "Send WhatsApp Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Current Date & Time": {
      "main": [
        [
          {
            "node": "Append Log to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare AI Booking Prompt": {
      "main": [
        [
          {
            "node": "General Support Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retrieve Company Knowledge": {
      "main": [
        [
          {
            "node": "Build AI Context",
            "type": "main",
            "index": 0
          },
          {
            "node": "Extract Booking Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Chat Model (Booking)": {
      "ai_languageModel": [
        [
          {
            "node": "Booking Information Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Google Chat Model (Support)": {
      "ai_languageModel": [
        [
          {
            "node": "General Support Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "When WhatsApp Message Received": {
      "main": [
        [
          {
            "node": "Normalize Message Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}