{"workflow":{"id":12763,"name":"Book WhatsApp consultations and sync contacts with Airtable and Google Calendar","views":174,"recentViews":0,"totalViews":174,"createdAt":"2026-01-16T20:05:54.414Z","description":"##### WhatsApp Booking Flow | Consultation Scheduling\n\nThis n8n template automates appointment booking via WhatsApp Flows with real-time calendar availability, AI-powered intent classification, and CRM synchronization. It transforms manual booking conversations into a seamless self-service experience directly within WhatsApp.\n\n##### Who is this for?\n\nService businesses wanting WhatsApp-based appointment booking\nConsultants and agencies offering scheduled calls or consultations\nTeams using Airtable for CRM and Google Calendar for scheduling\nBusinesses looking to reduce booking friction with conversational commerce\n\n##### What problem does this workflow solve?\n\nAppointment booking typically involves back-and-forth messaging to find available times, collect customer details, and confirm bookings. This workflow eliminates that friction by providing an interactive booking flow within WhatsApp that checks real-time calendar availability, collects customer information, and automatically syncs bookings to your CRM and calendar.\n\n##### How it works\n\n**Webhook Entry Point:** A single webhook handles both GET (Meta verification) and POST (messages/flows) requests - required due to Meta's single-webhook-per-app restriction.\n\n**Message Routing:** Incoming requests are classified as either regular WhatsApp messages or encrypted Flow requests, then routed accordingly.\n\n**WhatsApp Flow Handling:** Flow requests are decrypted using RSA/AES-GCM encryption. The workflow handles multiple Flow actions:\n- INIT: Returns consultation types and date constraints\n- SERVICE_SELECTION: Processes service and customer details\n- DATE_TIME_SELECTION: Queries calendar availability for selected date\n- CONFIRMATION: Displays booking summary\n- COMPLETE_BOOKING: Finalizes the booking\n\n**AI Agent:** For regular text messages, an OpenAI-powered agent classifies user intent. When booking intent is detected, it triggers the consultation template with the WhatsApp Flow.\n\n**Booking Process:** On completion, the workflow:\n1. Creates or updates customer in Airtable\n2. Creates booking record with event details\n3. Creates Google Calendar event\n4. Sends WhatsApp confirmation message\n\n##### Good to Know\n\n**Verify Token:** The `WHATSAPP_VERIFY_TOKEN` environment variable is required for Meta webhook verification. Set this to any secure string and use the same value in Meta Developer Portal.\n\n**Cloud vs Self-hosted:** Cloud n8n instances are easier to configure as they have public URLs. Self-hosted instances require additional setup for public accessibility.\n\n**Hostinger/Docker Setup:** For self-hosted instances, configure public webhook access in your `docker-compose.yml` or reverse proxy configuration.\n\n**Meta Prerequisites:** You'll need:\n- Facebook Account\n- Meta Developer App\n- WhatsApp Business Account (linked to Developer App)\n- WhatsApp Business Phone Number\n\n**Health Checks:** Meta sends periodic ping requests to verify webhook availability. The workflow handles these automatically with a 200 response.\n\n**Single Webhook Restriction:** Meta only allows one webhook URL per WhatsApp app, which is why all message types flow through a single endpoint with routing logic.\n\n**Encryption:** WhatsApp Flows use end-to-end encryption. The workflow handles RSA decryption (for AES key exchange) and AES-128-GCM encryption/decryption for Flow data.\n\n##### Requirements\n\n- Meta Business Account with WhatsApp Business API access\n- WhatsApp Business App in Meta Developer Portal\n- n8n instance (cloud or self-hosted with public URL)\n- OpenAI API key (for intent classification with GPT-4o)\n- Airtable account with base containing: Customers, Services, Bookings tables\n- Google Calendar with OAuth credentials\n\n##### Environment Variables\n\n| Variable | Description |\n|----------|-------------|\n| `WHATSAPP_VERIFY_TOKEN` | Webhook verification token (match in Meta Developer Portal) |\n| `WHATSAPP_PRIVATE_KEY` | RSA private key for Flow encryption (PEM format) |\n| `WHATSAPP_PRIVATE_KEY_PASSPHRASE` | Passphrase for the RSA private key |\n| `GOOGLE_CALENDAR_ID` | Calendar ID for availability checks and event creation |\n\n##### Setup\n\n1. **Meta Business Setup**\n   - Create a Meta Developer App at developers.facebook.com\n   - Add WhatsApp product to your app\n   - Set up a WhatsApp Business Account\n   - Generate a permanent access token\n\n2. **Import Workflow**\n   - Import `personal-booking-whatsapp-flow.json` into n8n\n   - Replace placeholder credential IDs with your actual credentials\n\n3. **Configure Credentials**\n   - WhatsApp: HTTP Bearer Auth with your access token\n   - OpenAI: API key for GPT-4o\n   - Airtable: OAuth2 authentication\n   - Google Calendar: OAuth2 authentication\n\n4. **Set Environment Variables**\n   - Configure all variables listed above in n8n settings\n\n5. **Configure Webhook in Meta**\n   - Navigate to WhatsApp &gt; Configuration in Developer Portal\n   - Set webhook URL to your n8n webhook endpoint\n   - Enter your verify token\n   - Subscribe to messages webhook field\n\n6. **Create WhatsApp Flow**\n   - In WhatsApp Manager, create a new Flow\n   - Use the JSON from `whatsapp-flow.json` as your Flow definition\n   - Publish the Flow and note the Flow ID\n\n7. **Create Message Template**\n   - Create a template with a Flow button component\n   - Link it to your published Flow\n   - Submit for approval\n\n##### Airtable Schema\n\n**Customers Table:**\n- `customer_name` (text)\n- `customer_email` (email)\n- `phone_number` (text)\n- `created_at` (date)\n\n**Services Table:**\n- `service_name` (text)\n- `service_key` (text) - e.g., \"30_min\", \"60_min\"\n- `duration_minutes` (number)\n\n**Bookings Table:**\n- `customer_email` (text)\n- `service_type` (linked to Services)\n- `event_date` (date)\n- `event_time` (text)\n- `booking_status` (select: Pending, Confirmed, Cancelled)\n- `calendar_event_id` (text)\n- `created_at` (date)\n\n##### Customizing this workflow\n\n**Consultation Types:** Modify the INIT handler code node to add/change consultation options and durations.\n\n**Business Hours:** Adjust the calendar availability logic in the date refresh handler to match your working hours.\n\n**AI Agent Prompts:** Customize the system prompt in the AI Agent node to match your business context and available services.\n\n**Messaging Templates:** Create additional WhatsApp templates for different services (quotes, information requests) and add corresponding tools to the AI agent.\n\n**CRM Fields:** Extend the Airtable schema and update the booking creation nodes to capture additional customer data.\n\nMade by [www.fenrirlabs.nl](https://www.fenrirlabs.nl)\n","workflow":{"meta":{"instanceId":"8a81807068944e7cdab35765fab04adb58c0488e2c001db9bdb11561cf1b0e8c"},"nodes":[{"id":"c46f192b-d4f6-4227-979b-d257151a33d9","name":"GET: Verify Webhook","type":"n8n-nodes-base.webhook","position":[80,144],"webhookId":"whatsapp-webhook-get","parameters":{"path":"whatsapp-webhook","options":{},"responseMode":"responseNode"},"typeVersion":2},{"id":"f70a36e8-e057-42ca-b3cc-a3b3dc868464","name":"Verify Token","type":"n8n-nodes-base.code","position":[304,144],"parameters":{"jsCode":"// Handle GET verification request from WhatsApp\nconst query = $input.first().json.query || {};\nconst VERIFY_TOKEN = $env.WHATSAPP_VERIFY_TOKEN || 'your_verify_token_here';\n\nif (query['hub.mode'] !== 'subscribe') {\n  throw new Error('Invalid hub.mode');\n}\n\nif (query['hub.verify_token'] !== VERIFY_TOKEN) {\n  throw new Error('Invalid verify token');\n}\n\nreturn {\n  json: {\n    challenge: query['hub.challenge']\n  }\n};"},"typeVersion":2},{"id":"32acd5a4-c26c-4bf8-9f40-9a49a19dfdac","name":"Return Challenge","type":"n8n-nodes-base.respondToWebhook","position":[544,144],"parameters":{"options":{"responseCode":200},"respondWith":"text","responseBody":"={{ $json.challenge }}"},"typeVersion":1.1},{"id":"6283825a-5b66-40d4-94b9-dc9dc24cefb5","name":"Is Regular Message?","type":"n8n-nodes-base.if","position":[-128,560],"parameters":{"options":{},"conditions":{"options":{"version":1,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"bde30647-9802-41d4-a346-3355ab24dca6","operator":{"type":"boolean","operation":"equals"},"leftValue":"={{ $json.isRegularMessage }}","rightValue":true}]}},"typeVersion":2},{"id":"525cce30-ed11-4735-9bc3-536d2143efc1","name":"Return 200 OK (Regular Message)","type":"n8n-nodes-base.respondToWebhook","position":[784,544],"parameters":{"options":{"responseCode":200},"respondWith":"json","responseBody":"={{ JSON.stringify({ status: 'received' }) }}"},"typeVersion":1.1},{"id":"d5aeb338-cab9-4e24-a216-cabb36213f46","name":"POST: Receive Messages1","type":"n8n-nodes-base.webhook","position":[-576,560],"webhookId":"whatsapp-webhook-post","parameters":{"path":"whatsapp-webhook","options":{},"httpMethod":"POST","responseMode":"responseNode"},"typeVersion":2},{"id":"c062dd3a-da07-4f8a-954d-d6672a8f422a","name":"Decrypt WhatsApp Request1","type":"n8n-nodes-base.code","position":[-352,560],"parameters":{"jsCode":"const crypto = require('crypto');\n\nconst PRIVATE_KEY = $env.WHATSAPP_PRIVATE_KEY || '';\nconst PASSPHRASE = $env.WHATSAPP_PRIVATE_KEY_PASSPHRASE || '';\n\nlet privateKey = PRIVATE_KEY;\nif (!PRIVATE_KEY.includes('\\n') && PRIVATE_KEY.includes('\\\\n')) {\n  privateKey = PRIVATE_KEY.replace(/\\\\n/g, '\\n');\n}\n\nconst body = $input.first().json.body;\n\n// Check if this is a regular WhatsApp message (not a Flow request)\nif (body?.entry) {\n  const entry = body.entry[0];\n  const changes = entry?.changes?.[0];\n  const value = changes?.value;\n  const messages = value?.messages;\n\n  if (messages && messages.length > 0) {\n    const message = messages[0];\n    const from = message.from;\n    const messageType = message.type;\n\n    // Handle interactive button replies\n    if (messageType === 'interactive' && message.interactive?.type === 'button_reply') {\n      const buttonId = message.interactive.button_reply.id;\n      const buttonTitle = message.interactive.button_reply.title;\n      const parts = buttonId.split('_');\n      const action = parts[0];\n      const recordId = parts.slice(1).join('_');\n\n      return {\n        json: {\n          messageType: 'button_reply',\n          action: action,\n          recordId: recordId,\n          customerPhone: from,\n          buttonTitle: buttonTitle,\n          isRegularMessage: true\n        }\n      };\n    }\n\n    // Handle text messages\n    if (messageType === 'text') {\n      return {\n        json: {\n          messageType: 'text_message',\n          from: from,\n          text: message.text?.body || '',\n          isRegularMessage: true\n        }\n      };\n    }\n\n    // Other message types\n    return {\n      json: {\n        messageType: messageType,\n        from: from,\n        raw: message,\n        isRegularMessage: true\n      }\n    };\n  }\n\n  // Status update or other non-message webhook\n  return {\n    json: {\n      messageType: 'status_update',\n      isRegularMessage: true,\n      raw: body\n    }\n  };\n}\n\n// This is an encrypted Flow request - decrypt it\nfunction decryptRequest(body) {\n  const { encrypted_aes_key, encrypted_flow_data, initial_vector } = body;\n\n  const decryptedAesKey = crypto.privateDecrypt(\n    {\n      key: privateKey,\n      passphrase: PASSPHRASE,\n      oaepHash: 'sha256',\n      padding: crypto.constants.RSA_PKCS1_OAEP_PADDING\n    },\n    Buffer.from(encrypted_aes_key, 'base64')\n  );\n\n  const flowDataBuffer = Buffer.from(encrypted_flow_data, 'base64');\n  const ivBuffer = Buffer.from(initial_vector, 'base64');\n\n  const authTag = flowDataBuffer.subarray(-16);\n  const encryptedData = flowDataBuffer.subarray(0, -16);\n\n  const decipher = crypto.createDecipheriv('aes-128-gcm', decryptedAesKey, ivBuffer);\n  decipher.setAuthTag(authTag);\n\n  const decrypted = Buffer.concat([\n    decipher.update(encryptedData),\n    decipher.final()\n  ]);\n\n  return {\n    decryptedData: JSON.parse(decrypted.toString('utf8')),\n    aesKey: decryptedAesKey,\n    iv: ivBuffer\n  };\n}\n\nfunction encryptResponse(response, aesKey, iv) {\n  const flippedIv = Buffer.alloc(iv.length);\n  for (let i = 0; i < iv.length; i++) {\n    flippedIv[i] = ~iv[i] & 0xff;\n  }\n  const cipher = crypto.createCipheriv('aes-128-gcm', aesKey, flippedIv);\n  const encrypted = Buffer.concat([\n    cipher.update(JSON.stringify(response), 'utf8'),\n    cipher.final(),\n    cipher.getAuthTag()\n  ]);\n  return encrypted.toString('base64');\n}\n\nconst { decryptedData, aesKey, iv } = decryptRequest(body);\n\nif (decryptedData.action === 'ping') {\n  const pingResponse = {\n    version: decryptedData.version,\n    data: { status: 'active' }\n  };\n\n  return {\n    json: {\n      action: 'ping',\n      response: encryptResponse(pingResponse, aesKey, iv),\n      isRegularMessage: false\n    }\n  };\n}\n\nreturn {\n  json: {\n    decrypted: decryptedData,\n    action: decryptedData.action,\n    screen: decryptedData.screen,\n    data: decryptedData.data || {},\n    flow_token: decryptedData.flow_token,\n    aesKeyBase64: aesKey.toString('base64'),\n    ivBase64: iv.toString('base64'),\n    version: decryptedData.version,\n    isRegularMessage: false\n  }\n};"},"typeVersion":2},{"id":"b37944ec-53ee-4548-8b1f-7bc2c99a44e1","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[-608,304],"parameters":{"color":7,"width":416,"height":480,"content":"## Whatsapp Single Entry Point: Webhook\n\nBecause of Meta's Single App to Single Webhook restriction, all messages come through the same webhook (GET / POST). \n\nConditional statements after this are then used to filter what happens"},"typeVersion":1},{"id":"bfc3eb34-787e-4959-9402-73cc0d218f0a","name":"Sticky Note1","type":"n8n-nodes-base.stickyNote","position":[16,1136],"parameters":{"color":7,"width":3072,"height":1280,"content":"## WhatsApp Flow: Booking\n\nFlows are attached to messaging templates and are activated by the WhatsApp User\n\nAt each stage of the WhatsApp Flow, data is being fed to the experience. E.g Services Available, Calendar Availability, & Confirmation. On complete - a booking is made into Airtable and a Calendar Event is made\n\n"},"typeVersion":1},{"id":"d6f2ba59-2038-49ca-8057-1e379baeed25","name":"Sticky Note2","type":"n8n-nodes-base.stickyNote","position":[32,384],"parameters":{"color":7,"width":1296,"height":656,"content":"## Intelligent Templating Assignment & Responses\n\nWe want to be able to fire off our template messages and flows that make sense to the customers requests. If they want more information, we can send them a link to the services website.\nIf they want to book - then a Calendar Flow, a quote, then a quote flow. "},"typeVersion":1},{"id":"b3551661-9a21-412b-a0b0-b6500d74c05b","name":"Sticky Note4","type":"n8n-nodes-base.stickyNote","position":[32,32],"parameters":{"color":7,"width":704,"height":304,"content":"## Utility: WhatsApp Webhook Check\nRequired for setting up your n8n webhook in Meta"},"typeVersion":1},{"id":"9b87e762-c71d-4073-adb5-265a8f262aa1","name":"Return Ping Response","type":"n8n-nodes-base.respondToWebhook","position":[304,1312],"parameters":{"options":{"responseCode":200},"respondWith":"text","responseBody":"={{ $json.response }}"},"typeVersion":1.1},{"id":"a9bc81f7-d4c5-4351-abf4-42be2a295e82","name":"Handle INIT - Return Consultation Types","type":"n8n-nodes-base.code","position":[304,1472],"parameters":{"jsCode":"// Handle INIT action - return initial screen data\nconst formatDate = (d) => d.toISOString().split('T')[0];\nconst today = new Date();\nconst maxDate = new Date();\nmaxDate.setDate(today.getDate() + 30);\n\nconst response = {\n  version: $json.version || '3.0',\n  screen: 'SERVICE_SELECTION',\n  data: {\n    consultation_types: [\n      { id: '30_min', title: '30 Minute Call' },\n      { id: '60_min', title: '60 Minute Call' }\n    ],\n    min_date: formatDate(today),\n    max_date: formatDate(maxDate)\n  }\n};\n\nreturn {\n  json: {\n    response,\n    aesKeyBase64: $json.aesKeyBase64,\n    ivBase64: $json.ivBase64\n  }\n};"},"typeVersion":2},{"id":"df7e496c-140b-49e8-89ca-e4cd68f07929","name":"Handle Confirm Booking","type":"n8n-nodes-base.code","position":[336,2016],"parameters":{"jsCode":"// Handle confirm booking - return CONFIRMATION screen data\nconst data = $json.data;\nconst aesKeyBase64 = $json.aesKeyBase64;\nconst ivBase64 = $json.ivBase64;\nconst version = $json.version;\n\nconst response = {\n  version: version || '3.0',\n  screen: 'CONFIRMATION',\n  data: {\n    customer_name: data.customer_name,\n    customer_email: data.customer_email || '',\n    consultation_type: data.consultation_type,\n    consultation_type_label: data.consultation_type_label,\n    appointment_date: data.appointment_date,\n    appointment_time: data.appointment_time\n  }\n};\n\nreturn {\n  json: {\n    response,\n    aesKeyBase64,\n    ivBase64\n  }\n};"},"typeVersion":2},{"id":"f4321129-d5bc-4d7b-ae11-6b1e6d7f39d4","name":"Send WhatsApp Confirmation","type":"n8n-nodes-base.httpRequest","position":[2128,2224],"parameters":{"url":"https://graph.facebook.com/v23.0/{{WHATSAPP_PHONE_NUMBER_ID}}/messages","method":"POST","options":{},"jsonBody":"={\n  \"messaging_product\": \"whatsapp\",\n  \"to\": \"{{ $('Check Customer Exists1').first().json.flowToken }}\",\n  \"type\": \"text\",\n  \"text\": {\n    \"body\": \"Your consultation has been confirmed!\\n\\n{{ $('Check Customer Exists1').first().json.booking.consultationLabel }}\\nDate: {{ $('Check Customer Exists1').first().json.booking.appointmentDateDisplay }}\\nTime: {{ $('Check Customer Exists1').first().json.booking.appointmentTimeDisplay }}\\n\\nWe look forward to speaking with you!\"\n  }\n}","sendBody":true,"specifyBody":"json","authentication":"genericCredentialType","genericAuthType":"httpBearerAuth"},"typeVersion":4.2},{"id":"269f5e8b-ab1b-49cc-b6b1-e4615e79b74a","name":"Merge Paths","type":"n8n-nodes-base.code","position":[2480,1824],"parameters":{"jsCode":"// Merge all paths to encryption node\nreturn $input.all();"},"typeVersion":2},{"id":"5c9e17c9-00e5-4d04-82ee-6adaed550ff4","name":"Encrypt Response","type":"n8n-nodes-base.code","position":[2688,1824],"parameters":{"jsCode":"// WhatsApp Flow Encryption - AES-128-GCM\nconst crypto = require('crypto');\n\nfunction encryptResponse(response, aesKeyBase64, ivBase64) {\n  const aesKey = Buffer.from(aesKeyBase64, 'base64');\n  const iv = Buffer.from(ivBase64, 'base64');\n  \n  const flippedIv = Buffer.alloc(iv.length);\n  for (let i = 0; i < iv.length; i++) {\n    flippedIv[i] = ~iv[i] & 0xff;\n  }\n  \n  const cipher = crypto.createCipheriv('aes-128-gcm', aesKey, flippedIv);\n  \n  const responseStr = JSON.stringify(response);\n  const encrypted = Buffer.concat([\n    cipher.update(responseStr, 'utf8'),\n    cipher.final(),\n    cipher.getAuthTag()\n  ]);\n  \n  return encrypted.toString('base64');\n}\n\nconst { response, aesKeyBase64, ivBase64 } = $json;\nconst encryptedResponse = encryptResponse(response, aesKeyBase64, ivBase64);\n\nreturn {\n  json: {\n    encryptedResponse\n  }\n};"},"typeVersion":2},{"id":"dd67e257-f900-4f75-a0a3-61e8b04cbad2","name":"Respond to Webhook","type":"n8n-nodes-base.respondToWebhook","position":[2912,1824],"parameters":{"options":{"responseHeaders":{"entries":[{"name":"Content-Type","value":"text/plain"}]}},"respondWith":"text","responseBody":"={{ $json.encryptedResponse }}"},"typeVersion":1.1},{"id":"439fa7ab-c609-4d15-9fed-943e59c5e0d9","name":"Switch on Action","type":"n8n-nodes-base.switch","position":[80,1328],"parameters":{"rules":{"values":[{"outputKey":"PING","conditions":{"options":{"version":1,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"993ed1e9-64a4-427a-b8fc-5256edde1946","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.action }}","rightValue":"ping"}]},"renameOutput":true},{"outputKey":"INIT","conditions":{"options":{"version":1,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"007df454-eab1-4b7a-9e1d-e82781e8e9a7","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.action }}","rightValue":"INIT"}]},"renameOutput":true},{"outputKey":"SERVICE_SELECTION","conditions":{"options":{"version":1,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"46e256b4-e275-43a4-9f6d-d33c338d463a","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.action }}","rightValue":"data_exchange"},{"id":"64878584-3e5b-40a1-a0bf-ee11cf51f416","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.screen }}","rightValue":"SERVICE_SELECTION"}]},"renameOutput":true},{"outputKey":"DATE_REFRESH","conditions":{"options":{"version":1,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"1a7e63d4-55c8-4ebb-b305-432c69198888","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.action }}","rightValue":"data_exchange"},{"id":"664fd262-0803-42ef-96f7-02b6ab477460","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.screen }}","rightValue":"DATE_TIME_SELECTION"},{"id":"880d3796-67fb-4ea7-860e-ef27172ee346","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.data.action_type }}","rightValue":"refresh_slots"}]},"renameOutput":true},{"outputKey":"CONFIRM_BOOKING","conditions":{"options":{"version":1,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"926d9d18-05bc-4156-861e-d3960ee07dca","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.action }}","rightValue":"data_exchange"},{"id":"73939400-1292-4b36-b681-1d1c8aa0486a","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.screen }}","rightValue":"DATE_TIME_SELECTION"},{"id":"72b44982-b940-4b53-a1c7-f1766b9958d5","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.data.action_type }}","rightValue":"confirm_booking"}]},"renameOutput":true},{"outputKey":"COMPLETE_BOOKING","conditions":{"options":{"version":1,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"ea4498c4-fb40-492a-8703-6bec94c1fde9","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.action }}","rightValue":"data_exchange"},{"id":"b1234567-1234-1234-1234-123456789abc","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.screen }}","rightValue":"CONFIRMATION"},{"id":"c1234567-1234-1234-1234-123456789def","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.data.action_type }}","rightValue":"complete_booking"}]},"renameOutput":true}]},"options":{"fallbackOutput":"none"}},"typeVersion":3},{"id":"3e908ab3-eb23-4134-936e-85d4b14cae94","name":"Switch","type":"n8n-nodes-base.switch","position":[144,528],"parameters":{"rules":{"values":[{"outputKey":"INTERACTIVE","conditions":{"options":{"version":3,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"b2fd458a-376d-4b87-817e-a92151d3b3ff","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.messageType }}","rightValue":"interactive"}]},"renameOutput":true},{"outputKey":"STATUS_UPDATE","conditions":{"options":{"version":3,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"903cf5bf-4180-40da-a7eb-6dd58b37a1b1","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.messageType }}","rightValue":"status_updated"}]},"renameOutput":true},{"outputKey":"TEXT_MESSAGE","conditions":{"options":{"version":3,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"bd24999f-353e-40b9-b64c-b6e6c04ac51e","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.messageType }}","rightValue":"text_message"}]},"renameOutput":true}]},"options":{}},"typeVersion":3.4},{"id":"95e15ae0-f4c2-489b-887d-3f2f41c73e11","name":"OpenAI Chat Model","type":"@n8n/n8n-nodes-langchain.lmChatOpenAi","position":[352,832],"parameters":{"model":{"__rl":true,"mode":"list","value":"gpt-4o","cachedResultName":"gpt-4o"},"options":{},"builtInTools":{}},"typeVersion":1.3},{"id":"52f72a66-1e70-4545-87b4-03f230148e3b","name":"whatsapp_consult_template","type":"n8n-nodes-base.httpRequestTool","position":[704,832],"parameters":{"url":"https://graph.facebook.com/v23.0/{{WHATSAPP_PHONE_NUMBER_ID}}/messages","method":"POST","options":{},"jsonBody":"={\n  \"messaging_product\": \"whatsapp\",\n  \"to\": \"{{ $('Is Regular Message?').item.json.from }}\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"personal_consultation_booking\",\n    \"language\": {\n      \"code\": \"en\"\n    },\n    \"components\": [\n      {\n        \"type\": \"button\",\n        \"sub_type\": \"flow\",\n        \"index\": \"0\",\n        \"parameters\": [\n          {\n            \"type\": \"action\",\n            \"action\": {\n              \"flow_token\": \"{{ $json.from }}\"\n            }\n          }\n        ]\n      }\n    ]\n  }\n}\n","sendBody":true,"specifyBody":"json","authentication":"genericCredentialType","genericAuthType":"httpBearerAuth","toolDescription":"whatsapp_consult_template - use when a user's intent is to book an appointment"},"typeVersion":4.3},{"id":"38e2ebc5-be9c-4a8e-ae25-d922ae1660f2","name":"whatsapp_message_tool","type":"n8n-nodes-base.httpRequestTool","position":[528,832],"parameters":{"url":"https://graph.facebook.com/v23.0/{{WHATSAPP_PHONE_NUMBER_ID}}/messages","method":"POST","options":{},"jsonBody":"={\n    \"messaging_product\": \"whatsapp\",    \n    \"recipient_type\": \"individual\",\n    \"to\": \"{{ $('Is Regular Message?').item.json.from }}\",\n    \"type\": \"text\",\n    \"text\": {\n        \"preview_url\": false,\n        \"body\": \"{{ $fromAI('JSON', ``, 'json') }}\"\n    }\n}","sendBody":true,"specifyBody":"json","authentication":"genericCredentialType","genericAuthType":"httpBearerAuth","toolDescription":"**whatsapp_message**\nUsage: When the user asks a questions, or you require more information to understand their intent. You can reply"},"typeVersion":4.3},{"id":"9833334d-fd52-4f0e-ab53-cb16e20463f8","name":"Search Existing Customer","type":"n8n-nodes-base.airtable","notes":"UPDATED: Now searches by customer_email instead of phone_number","onError":"continueRegularOutput","position":[416,2224],"parameters":{"base":{"__rl":true,"mode":"url","value":"=https://airtable.com/{{AIRTABLE_BASE_ID}}","__regex":"https://airtable.com/([a-zA-Z0-9]{2,})"},"table":{"__rl":true,"mode":"list","value":"{{AIRTABLE_CUSTOMERS_TABLE_ID}}","cachedResultUrl":"https://airtable.com/{{AIRTABLE_BASE_ID}}/{{AIRTABLE_CUSTOMERS_TABLE_ID}}","cachedResultName":"Customers"},"options":{},"operation":"search","authentication":"airtableOAuth2Api","filterByFormula":"={customer_email}='{{ $json.booking.customerEmail }}'"},"typeVersion":2.1,"alwaysOutputData":true},{"id":"dc192778-333b-42ec-8726-55a05427efa8","name":"Lookup Service","type":"n8n-nodes-base.airtable","notes":"NEW: Lookup service record by service_key (30_min or 60_min) to get record ID for linking","position":[1088,2224],"parameters":{"base":{"__rl":true,"mode":"url","value":"=https://airtable.com/{{AIRTABLE_BASE_ID}}","__regex":"https://airtable.com/([a-zA-Z0-9]{2,})"},"table":{"__rl":true,"mode":"list","value":"{{AIRTABLE_SERVICES_TABLE_ID}}","cachedResultUrl":"https://airtable.com/{{AIRTABLE_BASE_ID}}/{{AIRTABLE_SERVICES_TABLE_ID}}","cachedResultName":"Services"},"options":{"fields":["service_name"]},"operation":"search","authentication":"airtableOAuth2Api","filterByFormula":"={service_key}='{{ $('Check Customer Exists1').first().json.booking.serviceKey }}'"},"typeVersion":2.1},{"id":"0b9e6c1e-0106-41dc-b1c8-d0a53492b81b","name":"Prepare Booking Data1","type":"n8n-nodes-base.code","notes":"UPDATED: Added serviceKey, eventDate, eventTime fields for new schema","position":[208,2224],"parameters":{"jsCode":"// Prepare booking data for Airtable and Calendar\nconst data = $json.data;\n\n// Parse YYYY-MM-DD date string\nconst appointmentDate = new Date(data.appointment_date + 'T00:00:00');\nconst [hours, minutes] = data.appointment_time.split(':').map(Number);\n\nappointmentDate.setHours(hours, minutes, 0, 0);\n\nconst duration = data.consultation_type === '60_min' ? 60 : 30;\nconst endTime = new Date(appointmentDate);\nendTime.setMinutes(endTime.getMinutes() + duration);\n\nconst dateStr = appointmentDate.toLocaleDateString('en-US', {\n  weekday: 'long',\n  year: 'numeric',\n  month: 'long',\n  day: 'numeric'\n});\n\nconst timeStr = appointmentDate.toLocaleTimeString('en-US', {\n  hour: 'numeric',\n  minute: '2-digit',\n  hour12: true\n});\n\nreturn {\n  json: {\n    ...$json,\n    booking: {\n      customerName: data.customer_name,\n      customerEmail: data.customer_email || '',\n      consultationType: data.consultation_type,\n      consultationLabel: data.consultation_type_label,\n      serviceKey: data.consultation_type,\n      eventDate: data.appointment_date,\n      eventTime: data.appointment_time,\n      appointmentDateTime: appointmentDate.toISOString(),\n      appointmentEndTime: endTime.toISOString(),\n      appointmentDateDisplay: dateStr,\n      appointmentTimeDisplay: timeStr,\n      duration: duration,\n      smsReminder: data.sms_reminder || false,\n      emailReminder: data.email_reminder || false\n    }\n  }\n};"},"typeVersion":2},{"id":"b4b78f14-467e-4c8f-9a04-a24066931bd1","name":"Create Booking in Airtable1","type":"n8n-nodes-base.airtable","notes":"UPDATED: customer_email (text), service_type (linked), event_date, event_time, booking_status, flow_token. Removed reminder fields.","position":[1312,2224],"parameters":{"base":{"__rl":true,"mode":"url","value":"=https://airtable.com/{{AIRTABLE_BASE_ID}}","__regex":"https://airtable.com/([a-zA-Z0-9]{2,})"},"table":{"__rl":true,"mode":"list","value":"{{AIRTABLE_BOOKINGS_TABLE_ID}}","cachedResultUrl":"https://airtable.com/{{AIRTABLE_BASE_ID}}/{{AIRTABLE_BOOKINGS_TABLE_ID}}","cachedResultName":"Bookings"},"columns":{"value":{"created_at":"={{ $now }}","event_date":"={{ $('Prepare Booking Data1').item.json.decrypted.data.appointment_date }}","event_time":"={{ $('Prepare Booking Data1').item.json.decrypted.data.appointment_time }}","booking_status":"Pending","customer_email":"={{ $('Upsert Customer in Airtable').item.json.fields.customer_email }}"},"schema":[{"id":"booking_id","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"booking_id","defaultMatch":false,"canBeUsedToMatch":true},{"id":"customer_email","type":"string","display":true,"removed":false,"readOnly":false,"required":false,"displayName":"customer_email","defaultMatch":false,"canBeUsedToMatch":true},{"id":"service_type","type":"array","display":true,"removed":true,"readOnly":false,"required":false,"displayName":"service_type","defaultMatch":false,"canBeUsedToMatch":true},{"id":"calendar_event_id","type":"string","display":true,"removed":true,"readOnly":false,"required":false,"displayName":"calendar_event_id","defaultMatch":false,"canBeUsedToMatch":true},{"id":"event_date","type":"dateTime","display":true,"removed":false,"readOnly":false,"required":false,"displayName":"event_date","defaultMatch":false,"canBeUsedToMatch":true},{"id":"event_time","type":"string","display":true,"removed":false,"readOnly":false,"required":false,"displayName":"event_time","defaultMatch":false,"canBeUsedToMatch":true},{"id":"created_at","type":"dateTime","display":true,"removed":false,"readOnly":false,"required":false,"displayName":"created_at","defaultMatch":false,"canBeUsedToMatch":true},{"id":"booking_status","type":"options","display":true,"options":[{"name":"Pending","value":"Pending"},{"name":"Confirmed","value":"Confirmed"},{"name":"Cancelled","value":"Cancelled"}],"removed":false,"readOnly":false,"required":false,"displayName":"booking_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"special_requests","type":"string","display":true,"removed":false,"readOnly":false,"required":false,"displayName":"special_requests","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":[],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{"typecast":true},"operation":"create","authentication":"airtableOAuth2Api"},"typeVersion":2.1},{"id":"f199a9f1-f6ea-477b-bdd9-61a83a979837","name":"Update Booking with Calendar ID1","type":"n8n-nodes-base.airtable","notes":"UPDATED: status -> booking_status, now sets to 'Confirmed' after calendar event created","position":[1808,2224],"parameters":{"base":{"__rl":true,"mode":"url","value":"=https://airtable.com/{{AIRTABLE_BASE_ID}}","__regex":"https://airtable.com/([a-zA-Z0-9]{2,})"},"table":{"__rl":true,"mode":"list","value":"{{AIRTABLE_BOOKINGS_TABLE_ID}}","cachedResultUrl":"https://airtable.com/{{AIRTABLE_BASE_ID}}/{{AIRTABLE_BOOKINGS_TABLE_ID}}","cachedResultName":"Bookings"},"columns":{"value":{"id":"={{ $('Create Booking in Airtable1').item.json.id }}","booking_status":"Confirmed","calendar_event_id":"={{ $json.id }}"},"schema":[{"id":"id","type":"string","display":true,"removed":false,"readOnly":true,"required":false,"displayName":"id","defaultMatch":true},{"id":"booking_id","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"booking_id","defaultMatch":false,"canBeUsedToMatch":true},{"id":"customer_email","type":"string","display":true,"removed":true,"readOnly":false,"required":false,"displayName":"customer_email","defaultMatch":false,"canBeUsedToMatch":true},{"id":"service_type","type":"array","display":true,"removed":true,"readOnly":false,"required":false,"displayName":"service_type","defaultMatch":false,"canBeUsedToMatch":true},{"id":"calendar_event_id","type":"string","display":true,"removed":false,"readOnly":false,"required":false,"displayName":"calendar_event_id","defaultMatch":false,"canBeUsedToMatch":true},{"id":"event_date","type":"dateTime","display":true,"removed":true,"readOnly":false,"required":false,"displayName":"event_date","defaultMatch":false,"canBeUsedToMatch":true},{"id":"event_time","type":"string","display":true,"removed":true,"readOnly":false,"required":false,"displayName":"event_time","defaultMatch":false,"canBeUsedToMatch":true},{"id":"created_at","type":"dateTime","display":true,"removed":true,"readOnly":false,"required":false,"displayName":"created_at","defaultMatch":false,"canBeUsedToMatch":true},{"id":"booking_status","type":"options","display":true,"options":[{"name":"Pending","value":"Pending"},{"name":"Confirmed","value":"Confirmed"},{"name":"Cancelled","value":"Cancelled"}],"removed":false,"readOnly":false,"required":false,"displayName":"booking_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"special_requests","type":"string","display":true,"removed":true,"readOnly":false,"required":false,"displayName":"special_requests","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["id"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","authentication":"airtableOAuth2Api"},"typeVersion":2.1},{"id":"62c6c4dd-ab5e-4da9-986b-9625d1d9f4af","name":"Upsert Customer in Airtable","type":"n8n-nodes-base.airtable","notes":"UPDATED: Field names - customer_name, customer_email, customer_phone_number, date_created, last_interaction","position":[880,2224],"parameters":{"base":{"__rl":true,"mode":"url","value":"=https://airtable.com/{{AIRTABLE_BASE_ID}}"},"table":{"__rl":true,"mode":"list","value":"{{AIRTABLE_CUSTOMERS_TABLE_ID}}","cachedResultUrl":"https://airtable.com/{{AIRTABLE_BASE_ID}}/{{AIRTABLE_CUSTOMERS_TABLE_ID}}","cachedResultName":"Customers"},"columns":{"value":{"id":"={{ $json.existingCustomerId }}","date_created":"={{ $now }}","customer_name":"={{ $json.booking.customerName }}","customer_email":"={{ $json.booking.customerEmail }}","last_interaction":"={{ $now }}"},"schema":[{"id":"id","type":"string","display":true,"removed":false,"readOnly":true,"required":false,"displayName":"id","defaultMatch":true},{"id":"customer_id","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"customer_id","defaultMatch":false,"canBeUsedToMatch":true},{"id":"customer_name","type":"string","display":true,"removed":false,"readOnly":false,"required":false,"displayName":"customer_name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"customer_email","type":"string","display":true,"removed":false,"readOnly":false,"required":false,"displayName":"customer_email","defaultMatch":false,"canBeUsedToMatch":true},{"id":"customer_phone_number","type":"string","display":true,"removed":false,"readOnly":false,"required":false,"displayName":"customer_phone_number","defaultMatch":false,"canBeUsedToMatch":true},{"id":"date_created","type":"dateTime","display":true,"removed":false,"readOnly":false,"required":false,"displayName":"date_created","defaultMatch":false,"canBeUsedToMatch":true},{"id":"last_interaction","type":"dateTime","display":true,"removed":false,"readOnly":false,"required":false,"displayName":"last_interaction","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Service Requests","type":"array","display":true,"removed":true,"readOnly":false,"required":false,"displayName":"Service Requests","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["id"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{"typecast":true},"operation":"upsert","authentication":"airtableOAuth2Api"},"typeVersion":2.1},{"id":"70235408-3e69-4bc3-b3c2-3e5c6a492dce","name":"Google Calendar Events (Date)","type":"n8n-nodes-base.httpRequest","position":[544,1840],"parameters":{"url":"=https://www.googleapis.com/calendar/v3/calendars/{{ encodeURIComponent($json.calendarId) }}/events","options":{},"sendQuery":true,"authentication":"predefinedCredentialType","queryParameters":{"parameters":[{"name":"timeMin","value":"={{ $json.timeMin }}"},{"name":"timeMax","value":"={{ $json.timeMax }}"},{"name":"singleEvents","value":"true"},{"name":"orderBy","value":"startTime"},{"name":"maxResults","value":"50"}]},"nodeCredentialType":"googleCalendarOAuth2Api"},"typeVersion":4.2},{"id":"32cff87f-af74-4cae-97e9-acc2830f3cad","name":"Calculate Slots for Date","type":"n8n-nodes-base.code","position":[768,1840],"parameters":{"jsCode":"// Calculate slots for selected date using Events API (including pending/tentative)\nconst events = ($json.items || []).filter(e => e.status !== 'cancelled');\nconst prev = $('Prepare Date Refresh Request1').first().json;\nconst consultationType = prev.consultationType;\nconst selectedDateStr = prev.selectedDate;\nconst customerName = prev.customerName;\nconst customerEmail = prev.customerEmail;\nconst aesKeyBase64 = prev.aesKeyBase64;\nconst ivBase64 = prev.ivBase64;\nconst version = prev.version;\n\nconst slotDuration = consultationType === '60_min' ? 60 : 30;\nconst consultationLabel = consultationType === '60_min' ? '60 Minute Call' : '30 Minute Call';\n\nconst businessStart = 9;\nconst businessEnd = 17;\n\n// Convert events to busy time blocks\nconst busyTimes = events.map(event => {\n  // Handle all-day events\n  if (event.start.date) {\n    return {\n      start: new Date(event.start.date + 'T00:00:00'),\n      end: new Date(event.end.date + 'T00:00:00')\n    };\n  }\n  return {\n    start: new Date(event.start.dateTime),\n    end: new Date(event.end.dateTime)\n  };\n});\n\nconst selectedDate = new Date(selectedDateStr + 'T00:00:00');\nconst now = new Date();\nconst availableSlots = [];\n\nconst dayOfWeek = selectedDate.getDay();\nif (dayOfWeek !== 0 && dayOfWeek !== 6) {\n  for (let hour = businessStart; hour < businessEnd; hour++) {\n    for (let minute = 0; minute < 60; minute += 30) {\n      const slotEnd = hour + (minute + slotDuration) / 60;\n      if (slotEnd > businessEnd) continue;\n\n      const slotStart = new Date(selectedDate);\n      slotStart.setHours(hour, minute, 0, 0);\n\n      // Skip past slots\n      if (slotStart <= now) continue;\n\n      const slotEndTime = new Date(slotStart);\n      slotEndTime.setMinutes(slotEndTime.getMinutes() + slotDuration);\n\n      // Check for conflicts with any event (confirmed, tentative, etc.)\n      const isConflict = busyTimes.some(busy => {\n        return (slotStart < busy.end && slotEndTime > busy.start);\n      });\n\n      if (!isConflict) {\n        const timeStr = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;\n        const displayHour = hour > 12 ? hour - 12 : hour;\n        const ampm = hour >= 12 ? 'PM' : 'AM';\n        const displayMin = minute.toString().padStart(2, '0');\n\n        availableSlots.push({\n          id: timeStr,\n          title: `${displayHour}:${displayMin} ${ampm}`\n        });\n      }\n    }\n  }\n}\n\nconst formatDate = (d) => d.toISOString().split('T')[0];\nconst today = new Date();\nconst minDate = formatDate(today);\nconst maxDate = formatDate(new Date(today.getTime() + 30 * 24 * 60 * 60 * 1000));\n\nconst response = {\n  version: version || '3.0',\n  screen: 'DATE_TIME_SELECTION',\n  data: {\n    customer_name: customerName,\n    customer_email: customerEmail,\n    consultation_type: consultationType,\n    consultation_type_label: consultationLabel,\n    min_date: minDate,\n    max_date: maxDate,\n    available_slots: availableSlots\n  }\n};\n\nreturn {\n  json: {\n    response,\n    aesKeyBase64,\n    ivBase64\n  }\n};"},"typeVersion":2},{"id":"2ea36a0e-ea3d-4e30-afdf-0ba322f985ea","name":"Prepare Date Refresh Request1","type":"n8n-nodes-base.code","position":[336,1840],"parameters":{"jsCode":"// Prepare Google Calendar Events request for specific selected date\nconst selectedDate = $json.data.selected_date;\n\nconst date = new Date(selectedDate + 'T00:00:00');\n\nconst dayStart = new Date(date);\ndayStart.setHours(0, 0, 0, 0);\n\nconst dayEnd = new Date(date);\ndayEnd.setHours(23, 59, 59, 999);\n\nconst calendarId = $env.GOOGLE_CALENDAR_ID || 'primary';\n\nreturn {\n  json: {\n    ...$json,\n    selectedDate: selectedDate,\n    calendarId: calendarId,\n    timeMin: dayStart.toISOString(),\n    timeMax: dayEnd.toISOString(),\n    consultationType: $json.data.consultation_type,\n    customerName: $json.data.customer_name,\n    customerEmail: $json.data.customer_email || ''\n  }\n};"},"typeVersion":2},{"id":"c821c29b-7144-49ae-8a40-528c6ac9d561","name":"Check Customer Exists1","type":"n8n-nodes-base.code","notes":"UPDATED: Field names changed to customer_name, customer_email, customer_phone_number, date_created, last_interaction","position":[640,2224],"parameters":{"jsCode":"// Check if customer exists and prepare upsert data\nconst existingCustomers = $input.all();\nconst booking = $('Prepare Booking Data1').first().json.booking;\nconst flowToken = $('Prepare Booking Data1').first().json.flow_token;\nconst aesKeyBase64 = $('Prepare Booking Data1').first().json.aesKeyBase64;\nconst ivBase64 = $('Prepare Booking Data1').first().json.ivBase64;\nconst version = $('Prepare Booking Data1').first().json.version;\n\nconst customerExists = existingCustomers.length > 0 && existingCustomers[0].json.id;\nconst existingCustomerId = customerExists ? existingCustomers[0].json.id : null;\n\nreturn {\n  json: {\n    customerExists,\n    existingCustomerId,\n    booking,\n    flowToken,\n    aesKeyBase64,\n    ivBase64,\n    version,\n    customerData: {\n      customer_name: booking.customerName,\n      customer_email: booking.customerEmail,\n      customer_phone_number: flowToken,\n      date_created: new Date().toISOString().split('T')[0],\n      last_interaction: new Date().toISOString().split('T')[0]\n    }\n  }\n};"},"typeVersion":2},{"id":"955a58cf-8d98-4eda-83e2-f8d94c88a872","name":"Prepare Success Response1","type":"n8n-nodes-base.code","notes":"FIXED: Gets encryption keys from Check Customer Exists1 instead of Search Existing Customer","position":[2496,2208],"parameters":{"jsCode":"// Prepare success response\nconst aesKeyBase64 = $('Check Customer Exists1').first().json.aesKeyBase64;\nconst ivBase64 = $('Check Customer Exists1').first().json.ivBase64;\nconst version = $('Check Customer Exists1').first().json.version;\n\nconst response = {\n  version: version || '3.0',\n  screen: 'SUCCESS',\n  data: {\n    extension_message_response: {\n      params: {\n        flow_token: $('Check Customer Exists1').first().json.flowToken,\n        booking_confirmed: true\n      }\n    }\n  }\n};\n\nreturn {\n  json: {\n    response,\n    aesKeyBase64,\n    ivBase64\n  }\n};"},"typeVersion":2},{"id":"2df0aba0-0fb1-42ce-b71b-e3df3c7496f6","name":"Whatsapp Agent","type":"@n8n/n8n-nodes-langchain.agent","position":[352,640],"parameters":{"text":"={{ $json.text }}","options":{"systemMessage":"=## Role & Goal\n\nYou are a helpful WhatsApp Assistant for [Your Company Name]. Your goal is to respond to customers and determine the best response or WhatsApp template to serve them. \n\n## Task\n\nYou are given a text message from a user. You will then aim to understand how to help them. Based on their response, you will serve up a WhatsApp template or a message\n\n## Tools\n\nYou have access to the following tools:\n\n**whatsapp_consult_template**\nAction: Sends a whatsapp template that allows a user to book an appointment\nUsage: When the user's intent is to book a meeting\n\n**whatsapp_message**\nAction: Sends a text message back to the user\nUsage: When the user asks a questions, or you require more information to understand their intent\n\n## Examples\n- If a user messages \"Hello\", you respond with a greeting and asking how can [Your Company Name] help\n- If a user's intent is to book a consult, you will use the 'whatsapp_consult_template' too\n\n## Rules\n- MUST NOT do anything other than provide 'text' to the whatsapp_message tool. It should not be an object just pure text.\n\n"},"promptType":"define"},"typeVersion":3.1},{"id":"8cbbb696-c1fa-4ae7-af16-725b43f6591e","name":"Prepare Calendar Request","type":"n8n-nodes-base.code","position":[336,1680],"parameters":{"jsCode":"// Prepare Google Calendar Events request for today only\n// Configure timezone to match your calendar\nconst TIMEZONE = 'Europe/Amsterdam';\n\nconst now = new Date();\n\n// Set timeMax to end of current day\nconst endOfDay = new Date(now);\nendOfDay.setHours(23, 59, 59, 999);\n\nconst calendarId = $env.GOOGLE_CALENDAR_ID || 'primary';\n\nreturn {\n  json: {\n    ...$json,\n    calendarId: calendarId,\n    timezone: TIMEZONE,\n    timeMin: now.toISOString(),\n    timeMax: endOfDay.toISOString(),\n    consultationType: $json.data.consultation_type,\n    customerName: $json.data.customer_name,\n    customerEmail: $json.data.customer_email || ''\n  }\n};"},"typeVersion":2},{"id":"04b1b51e-26d8-4c3f-b61b-2af8301dcce2","name":"Google Calendar Events","type":"n8n-nodes-base.httpRequest","position":[544,1680],"parameters":{"url":"=https://www.googleapis.com/calendar/v3/calendars/{{ encodeURIComponent($json.calendarId) }}/events","options":{},"sendQuery":true,"authentication":"predefinedCredentialType","queryParameters":{"parameters":[{"name":"timeMin","value":"={{ $json.timeMin }}"},{"name":"timeMax","value":"={{ $json.timeMax }}"},{"name":"timeZone","value":"={{ $json.timezone }}"},{"name":"singleEvents","value":"true"},{"name":"orderBy","value":"startTime"},{"name":"maxResults","value":"250"}]},"nodeCredentialType":"googleCalendarOAuth2Api"},"typeVersion":4.2},{"id":"3711b099-f69e-4fd6-956a-cd62436a68e5","name":"Calculate Available Slots","type":"n8n-nodes-base.code","position":[768,1680],"parameters":{"jsCode":"// Calculate available slots using calendar timezone\nconst TIMEZONE = $('Prepare Calendar Request').first().json.timezone;\nconst BUSINESS_START = 9;\nconst BUSINESS_END = 17;\n\nconst events = ($json.items || []).filter(e => e.status !== 'cancelled');\nconst consultationType = $('Prepare Calendar Request').first().json.consultationType;\nconst customerName = $('Prepare Calendar Request').first().json.customerName;\nconst customerEmail = $('Prepare Calendar Request').first().json.customerEmail;\nconst aesKeyBase64 = $('Prepare Calendar Request').first().json.aesKeyBase64;\nconst ivBase64 = $('Prepare Calendar Request').first().json.ivBase64;\nconst version = $('Prepare Calendar Request').first().json.version;\n\nconst slotDuration = consultationType === '60_min' ? 60 : 30;\nconst consultationLabel = consultationType === '60_min' ? '60 Minute Call' : '30 Minute Call';\n\n// Get current time in the calendar's timezone\nconst nowUtc = new Date();\nconst tzFormatter = new Intl.DateTimeFormat('en-CA', {\n  timeZone: TIMEZONE,\n  year: 'numeric', month: '2-digit', day: '2-digit',\n  hour: '2-digit', minute: '2-digit', hour12: false\n});\nconst parts = tzFormatter.formatToParts(nowUtc);\nconst getPart = (type) => parts.find(p => p.type === type)?.value;\nconst currentHour = parseInt(getPart('hour'), 10);\nconst currentMinute = parseInt(getPart('minute'), 10);\nconst currentDateStr = `${getPart('year')}-${getPart('month')}-${getPart('day')}`;\n\n// Convert events to busy time blocks\nconst busyTimes = events.map(event => {\n  if (event.start.date) {\n    return {\n      start: new Date(event.start.date + 'T00:00:00'),\n      end: new Date(event.end.date + 'T00:00:00')\n    };\n  }\n  return {\n    start: new Date(event.start.dateTime),\n    end: new Date(event.end.dateTime)\n  };\n});\n\nconst availableSlots = [];\n\n// Check today only for available slots (up to 8 slots)\nfor (let dayOffset = 0; dayOffset < 1 && availableSlots.length < 8; dayOffset++) {\n  const checkDate = new Date(nowUtc);\n  checkDate.setDate(checkDate.getDate() + dayOffset);\n  \n  // Get date string in timezone\n  const dateFormatter = new Intl.DateTimeFormat('en-CA', {\n    timeZone: TIMEZONE,\n    year: 'numeric', month: '2-digit', day: '2-digit'\n  });\n  const dateParts = dateFormatter.formatToParts(checkDate);\n  const getDatePart = (type) => dateParts.find(p => p.type === type)?.value;\n  const checkDateStr = `${getDatePart('year')}-${getDatePart('month')}-${getDatePart('day')}`;\n  \n  // Get day of week (0=Sun, 6=Sat)\n  const dayOfWeek = new Date(checkDateStr + 'T12:00:00').getDay();\n  if (dayOfWeek === 0 || dayOfWeek === 6) continue;\n\n  for (let hour = BUSINESS_START; hour < BUSINESS_END; hour++) {\n    for (let minute = 0; minute < 60; minute += 30) {\n      const slotEndHour = hour + Math.floor((minute + slotDuration) / 60);\n      const slotEndMin = (minute + slotDuration) % 60;\n      if (slotEndHour > BUSINESS_END || (slotEndHour === BUSINESS_END && slotEndMin > 0)) continue;\n\n      // Skip past slots for today\n      if (checkDateStr === currentDateStr) {\n        if (hour < currentHour || (hour === currentHour && minute <= currentMinute)) {\n          continue;\n        }\n      }\n\n      // Create slot times for conflict checking\n      const slotStartStr = `${checkDateStr}T${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}:00`;\n      const slotStart = new Date(slotStartStr);\n      const slotEnd = new Date(slotStart);\n      slotEnd.setMinutes(slotEnd.getMinutes() + slotDuration);\n\n      // Check for conflicts\n      const isConflict = busyTimes.some(busy => {\n        return (slotStart < busy.end && slotEnd > busy.start);\n      });\n\n      if (!isConflict && availableSlots.length < 8) {\n        const timeStr = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;\n        const displayHour = hour > 12 ? hour - 12 : hour;\n        const ampm = hour >= 12 ? 'PM' : 'AM';\n        const displayMin = minute.toString().padStart(2, '0');\n\n        availableSlots.push({\n          id: timeStr,\n          title: `${displayHour}:${displayMin} ${ampm}`\n        });\n      }\n    }\n  }\n}\n\nconst formatDate = (d) => {\n  const f = new Intl.DateTimeFormat('en-CA', {\n    timeZone: TIMEZONE,\n    year: 'numeric', month: '2-digit', day: '2-digit'\n  });\n  return f.format(d).split('/').join('-');\n};\n\nconst today = new Date();\nconst minDate = formatDate(today);\nconst maxDate = formatDate(new Date(today.getTime() + 30 * 24 * 60 * 60 * 1000));\n\nconst response = {\n  version: version || '3.0',\n  screen: 'DATE_TIME_SELECTION',\n  data: {\n    customer_name: customerName,\n    customer_email: customerEmail,\n    consultation_type: consultationType,\n    consultation_type_label: consultationLabel,\n    min_date: minDate,\n    max_date: maxDate,\n    available_slots: availableSlots\n  }\n};\n\nreturn {\n  json: {\n    response,\n    aesKeyBase64,\n    ivBase64\n  }\n};"},"typeVersion":2},{"id":"6fe22c3d-822f-48bc-8dec-59e052db19da","name":"Create an event","type":"n8n-nodes-base.googleCalendar","position":[1520,2224],"parameters":{"end":"={{ $('Check Customer Exists1').first().json.booking.appointmentEndTime }}","start":"={{ $('Check Customer Exists1').first().json.booking.appointmentDateTime }}","calendar":{"__rl":true,"mode":"list","value":"{{GOOGLE_CALENDAR_ID}}","cachedResultName":"{{GOOGLE_CALENDAR_ID}}"},"additionalFields":{"summary":"=\"Consultation: {{ $('Check Customer Exists1').first().json.booking.customerName }}","attendees":["={{ $json.fields.customer_email }}"],"visibility":"default","description":"={{ $('Check Customer Exists1').first().json.booking.consultationLabel }} consultation with {{ $('Check Customer Exists1').first().json.booking.customerName }}","sendUpdates":"all","guestsCanInviteOthers":true,"guestsCanSeeOtherGuests":true}},"typeVersion":1.3},{"id":"ad24e842-7f34-49c2-b87a-71b8640c9aee","name":"Sticky Note3","type":"n8n-nodes-base.stickyNote","position":[-1200,944],"parameters":{"width":848,"height":4992,"content":"## WhatsApp Flow JSON\nCopy and paste this flow into WhatsApp Flow builder for a simple 30 / 60 min services\n```json\n{\n  \"version\": \"6.0\",\n  \"data_api_version\": \"3.0\",\n  \"routing_model\": {\n    \"SERVICE_SELECTION\": [\"DATE_TIME_SELECTION\"],\n    \"DATE_TIME_SELECTION\": [\"CONFIRMATION\"],\n    \"CONFIRMATION\": []\n  },\n  \"screens\": [\n    {\n      \"id\": \"SERVICE_SELECTION\",\n      \"title\": \"Book a Consultation\",\n      \"data\": {\n        \"consultation_types\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"object\",\n            \"properties\": {\n              \"id\": { \"type\": \"string\" },\n              \"title\": { \"type\": \"string\" }\n            }\n          },\n          \"__example__\": [\n            { \"id\": \"30_min\", \"title\": \"30 Minute Call\" },\n            { \"id\": \"60_min\", \"title\": \"60 Minute Call\" }\n          ]\n        }\n      },\n      \"layout\": {\n        \"type\": \"SingleColumnLayout\",\n        \"children\": [\n          {\n            \"type\": \"TextHeading\",\n            \"text\": \"Personal Business Consultation\"\n          },\n          {\n            \"type\": \"TextBody\",\n            \"text\": \"Please select your consultation type and provide your details.\"\n          },\n          {\n            \"type\": \"Form\",\n            \"name\": \"service_form\",\n            \"children\": [\n              {\n                \"type\": \"Dropdown\",\n                \"name\": \"consultation_type\",\n                \"label\": \"Type\",\n                \"required\": true,\n                \"data-source\": \"${data.consultation_types}\"\n              },\n              {\n                \"type\": \"TextInput\",\n                \"name\": \"customer_name\",\n                \"label\": \"Full Name\",\n                \"required\": true,\n                \"input-type\": \"text\",\n                \"helper-text\": \"Enter your full name\"\n              },\n              {\n                \"type\": \"TextInput\",\n                \"name\": \"customer_email\",\n                \"label\": \"Email\",\n                \"required\": true,\n                \"input-type\": \"email\",\n                \"helper-text\": \"We'll send a confirmation to this email\"\n              }\n            ]\n          },\n          {\n            \"type\": \"Footer\",\n            \"label\": \"Select Date & Time\",\n            \"on-click-action\": {\n              \"name\": \"data_exchange\",\n              \"payload\": {\n                \"consultation_type\": \"${form.consultation_type}\",\n                \"customer_name\": \"${form.customer_name}\",\n                \"customer_email\": \"${form.customer_email}\"\n              }\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"id\": \"DATE_TIME_SELECTION\",\n      \"title\": \"Select Date & Time\",\n      \"data\": {\n        \"customer_name\": {\n          \"type\": \"string\",\n          \"__example__\": \"John Doe\"\n        },\n        \"consultation_type\": {\n          \"type\": \"string\",\n          \"__example__\": \"30_min\"\n        },\n        \"consultation_type_label\": {\n          \"type\": \"string\",\n          \"__example__\": \"30 Minute Call\"\n        },\n        \"customer_email\": {\n          \"type\": \"string\",\n          \"__example__\": \"john@example.com\"\n        },\n        \"min_date\": {\n          \"type\": \"string\",\n          \"__example__\": \"2025-01-15\"\n        },\n        \"max_date\": {\n          \"type\": \"string\",\n          \"__example__\": \"2025-02-14\"\n        },\n        \"available_slots\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"object\",\n            \"properties\": {\n              \"id\": { \"type\": \"string\" },\n              \"title\": { \"type\": \"string\" }\n            }\n          },\n          \"__example__\": [\n            { \"id\": \"09:00\", \"title\": \"9:00 AM\" },\n            { \"id\": \"10:00\", \"title\": \"10:00 AM\" },\n            { \"id\": \"11:00\", \"title\": \"11:00 AM\" },\n            { \"id\": \"14:00\", \"title\": \"2:00 PM\" },\n            { \"id\": \"15:00\", \"title\": \"3:00 PM\" }\n          ]\n        }\n      },\n      \"layout\": {\n        \"type\": \"SingleColumnLayout\",\n        \"children\": [\n          {\n            \"type\": \"TextHeading\",\n            \"text\": \"Choose Your Appointment\"\n          },\n          {\n            \"type\": \"TextBody\",\n            \"text\": \"Select a date and available time slot for your consultation.\"\n          },\n          {\n            \"type\": \"Form\",\n            \"name\": \"datetime_form\",\n            \"children\": [\n              {\n                \"type\": \"DatePicker\",\n                \"name\": \"appointment_date\",\n                \"label\": \"Date\",\n                \"required\": true,\n                \"min-date\": \"${data.min_date}\",\n                \"max-date\": \"${data.max_date}\",\n                \"helper-text\": \"Select a date within the next 30 days\",\n                \"on-select-action\": {\n                  \"name\": \"data_exchange\",\n                  \"payload\": {\n                    \"selected_date\": \"${form.appointment_date}\",\n                    \"consultation_type\": \"${data.consultation_type}\",\n                    \"customer_name\": \"${data.customer_name}\",\n                    \"customer_email\": \"${data.customer_email}\",\n                    \"action_type\": \"refresh_slots\"\n                  }\n                }\n              },\n              {\n                \"type\": \"Dropdown\",\n                \"name\": \"appointment_time\",\n                \"label\": \"Time\",\n                \"required\": true,\n                \"data-source\": \"${data.available_slots}\"\n              }\n            ]\n          },\n          {\n            \"type\": \"Footer\",\n            \"label\": \"Review Booking\",\n            \"on-click-action\": {\n              \"name\": \"data_exchange\",\n              \"payload\": {\n                \"customer_name\": \"${data.customer_name}\",\n                \"customer_email\": \"${data.customer_email}\",\n                \"consultation_type\": \"${data.consultation_type}\",\n                \"consultation_type_label\": \"${data.consultation_type_label}\",\n                \"appointment_date\": \"${form.appointment_date}\",\n                \"appointment_time\": \"${form.appointment_time}\",\n                \"action_type\": \"confirm_booking\"\n              }\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"id\": \"CONFIRMATION\",\n      \"title\": \"Confirm Booking\",\n      \"terminal\": true,\n      \"data\": {\n        \"customer_name\": {\n          \"type\": \"string\",\n          \"__example__\": \"John Doe\"\n        },\n        \"customer_email\": {\n          \"type\": \"string\",\n          \"__example__\": \"john@example.com\"\n        },\n        \"consultation_type\": {\n          \"type\": \"string\",\n          \"__example__\": \"30_min\"\n        },\n        \"consultation_type_label\": {\n          \"type\": \"string\",\n          \"__example__\": \"30 Minute Call\"\n        },\n        \"appointment_date\": {\n          \"type\": \"string\",\n          \"__example__\": \"2025-01-15\"\n        },\n        \"appointment_time\": {\n          \"type\": \"string\",\n          \"__example__\": \"10:00\"\n        }\n      },\n      \"layout\": {\n        \"type\": \"SingleColumnLayout\",\n        \"children\": [\n          {\n            \"type\": \"TextHeading\",\n            \"text\": \"Booking Summary\"\n          },\n          {\n            \"type\": \"TextSubheading\",\n            \"text\": \"Please review your booking details\"\n          },\n          {\n            \"type\": \"TextCaption\",\n            \"text\": \"Name\"\n          },\n          {\n            \"type\": \"TextBody\",\n            \"text\": \"${data.customer_name}\"\n          },\n          {\n            \"type\": \"TextCaption\",\n            \"text\": \"Consultation\"\n          },\n          {\n            \"type\": \"TextBody\",\n            \"text\": \"${data.consultation_type_label}\"\n          },\n          {\n            \"type\": \"TextCaption\",\n            \"text\": \"Date\"\n          },\n          {\n            \"type\": \"TextBody\",\n            \"text\": \"${data.appointment_date}\"\n          },\n          {\n            \"type\": \"TextCaption\",\n            \"text\": \"Time\"\n          },\n          {\n            \"type\": \"TextBody\",\n            \"text\": \"${data.appointment_time}\"\n          },\n          {\n            \"type\": \"Form\",\n            \"name\": \"confirmation_form\",\n            \"children\": [\n              {\n                \"type\": \"OptIn\",\n                \"name\": \"sms_reminder\",\n                \"label\": \"Send me SMS reminders\",\n                \"required\": false\n              },\n              {\n                \"type\": \"OptIn\",\n                \"name\": \"email_reminder\",\n                \"label\": \"Send me email reminders\",\n                \"required\": false\n              }\n            ]\n          },\n          {\n            \"type\": \"Footer\",\n            \"label\": \"Confirm Booking\",\n            \"on-click-action\": {\n              \"name\": \"data_exchange\",\n              \"payload\": {\n                \"customer_name\": \"${data.customer_name}\",\n                \"customer_email\": \"${data.customer_email}\",\n                \"consultation_type\": \"${data.consultation_type}\",\n                \"consultation_type_label\": \"${data.consultation_type_label}\",\n                \"appointment_date\": \"${data.appointment_date}\",\n                \"appointment_time\": \"${data.appointment_time}\",\n                \"sms_reminder\": \"${form.sms_reminder}\",\n                \"email_reminder\": \"${form.email_reminder}\",\n                \"action_type\": \"complete_booking\"\n              }\n            }\n          }\n        ]\n      }\n    }\n  ]\n}\n```"},"typeVersion":1},{"id":"5925f871-5a5e-4218-8e82-28ee84f168dc","name":"Sticky Note5","type":"n8n-nodes-base.stickyNote","position":[-1200,304],"parameters":{"width":528,"height":624,"content":"## Book meetings with Whatsapp and sync to Airtable and Google Calendar\n\n#### How it works\n\nThis workflows has an AI Agent that aims to classify a user's intent via WhatsApp Messages, and if they wish to book a meeting, will send a WhatsApp template with a predefined flow, that allows them to book a consultation meeting of 30 / 60 mins.\n\nDuring the WhatsApp flow, Calendar Availability is provided.\n\nOn completion, the booking is synced to both Airtable and Google Calendar, and a confirmation message is sent.\n\n#### Setup\n\n- Create a Meta Developer App + Whatsapp Business Account\n- Create a User in your Developer App for Bearer Token\n- Create a Pass Phrase for Webhook Integration and strore as a variable in n8n\n- Generate a public RSA key and upload to WhatsApp Business Enryption endpoint\n- Set up Templates and Flows in Whatsapp (Flow JSON in Sticky)\n- Attach Flow to template"},"typeVersion":1}],"pinData":{},"connections":{"Switch":{"main":[[{"node":"Return 200 OK (Regular Message)","type":"main","index":0}],[{"node":"Return 200 OK (Regular Message)","type":"main","index":0}],[{"node":"Whatsapp Agent","type":"main","index":0}]]},"Merge Paths":{"main":[[{"node":"Encrypt Response","type":"main","index":0}]]},"Verify Token":{"main":[[{"node":"Return Challenge","type":"main","index":0}]]},"Lookup Service":{"main":[[{"node":"Create Booking in Airtable1","type":"main","index":0}]]},"Whatsapp Agent":{"main":[[{"node":"Return 200 OK (Regular Message)","type":"main","index":0}]]},"Create an event":{"main":[[{"node":"Update Booking with Calendar ID1","type":"main","index":0}]]},"Encrypt Response":{"main":[[{"node":"Respond to Webhook","type":"main","index":0}]]},"Switch on Action":{"main":[[{"node":"Return Ping Response","type":"main","index":0}],[{"node":"Handle INIT - Return Consultation Types","type":"main","index":0}],[{"node":"Prepare Calendar Request","type":"main","index":0}],[{"node":"Prepare Date Refresh Request1","type":"main","index":0}],[{"node":"Handle Confirm Booking","type":"main","index":0}],[{"node":"Prepare Booking Data1","type":"main","index":0}]]},"OpenAI Chat Model":{"ai_languageModel":[[{"node":"Whatsapp Agent","type":"ai_languageModel","index":0}]]},"GET: Verify Webhook":{"main":[[{"node":"Verify Token","type":"main","index":0}]]},"Is Regular Message?":{"main":[[{"node":"Switch","type":"main","index":0}],[{"node":"Switch on Action","type":"main","index":0}]]},"Prepare Booking Data1":{"main":[[{"node":"Search Existing Customer","type":"main","index":0}]]},"whatsapp_message_tool":{"ai_tool":[[{"node":"Whatsapp Agent","type":"ai_tool","index":0}]]},"Check Customer Exists1":{"main":[[{"node":"Upsert Customer in Airtable","type":"main","index":0}]]},"Google Calendar Events":{"main":[[{"node":"Calculate Available Slots","type":"main","index":0}]]},"Handle Confirm Booking":{"main":[[{"node":"Merge Paths","type":"main","index":0}]]},"POST: Receive Messages1":{"main":[[{"node":"Decrypt WhatsApp Request1","type":"main","index":0}]]},"Calculate Slots for Date":{"main":[[{"node":"Merge Paths","type":"main","index":0}]]},"Prepare Calendar Request":{"main":[[{"node":"Google Calendar Events","type":"main","index":0}]]},"Search Existing Customer":{"main":[[{"node":"Check Customer Exists1","type":"main","index":0}]]},"Calculate Available Slots":{"main":[[{"node":"Merge Paths","type":"main","index":0}]]},"Decrypt WhatsApp Request1":{"main":[[{"node":"Is Regular Message?","type":"main","index":0}]]},"Prepare Success Response1":{"main":[[{"node":"Merge Paths","type":"main","index":0}]]},"whatsapp_consult_template":{"ai_tool":[[{"node":"Whatsapp Agent","type":"ai_tool","index":0}]]},"Send WhatsApp Confirmation":{"main":[[{"node":"Prepare Success Response1","type":"main","index":0}]]},"Create Booking in Airtable1":{"main":[[{"node":"Create an event","type":"main","index":0}]]},"Upsert Customer in Airtable":{"main":[[{"node":"Lookup Service","type":"main","index":0}]]},"Google Calendar Events (Date)":{"main":[[{"node":"Calculate Slots for Date","type":"main","index":0}]]},"Prepare Date Refresh Request1":{"main":[[{"node":"Google Calendar Events (Date)","type":"main","index":0}]]},"Update Booking with Calendar ID1":{"main":[[{"node":"Send WhatsApp Confirmation","type":"main","index":0}]]},"Handle INIT - Return Consultation Types":{"main":[[{"node":"Merge Paths","type":"main","index":0}]]}}},"lastUpdatedBy":1,"workflowInfo":{"nodeCount":41,"nodeTypes":{"n8n-nodes-base.if":{"count":1},"n8n-nodes-base.code":{"count":13},"n8n-nodes-base.switch":{"count":2},"n8n-nodes-base.webhook":{"count":2},"n8n-nodes-base.airtable":{"count":5},"n8n-nodes-base.stickyNote":{"count":6},"n8n-nodes-base.httpRequest":{"count":3},"n8n-nodes-base.googleCalendar":{"count":1},"@n8n/n8n-nodes-langchain.agent":{"count":1},"n8n-nodes-base.httpRequestTool":{"count":2},"n8n-nodes-base.respondToWebhook":{"count":4},"@n8n/n8n-nodes-langchain.lmChatOpenAi":{"count":1}}},"status":"published","readyToDemo":null,"user":{"name":"Blaine Holt","username":"fenrirlabsnl","bio":"","verified":false,"links":["www.fenrirlabs.nl"],"avatar":"https://gravatar.com/avatar/36a86d7f3f768d95021eb27ab9bc73a4988a1fab2e697071ceef7a48d2673ac3?r=pg&d=retro&size=200"},"nodes":[{"id":2,"icon":"file:airtable.svg","name":"n8n-nodes-base.airtable","codex":{"data":{"resources":{"generic":[{"url":"https://n8n.io/blog/2021-goals-level-up-your-vocabulary-with-vonage-and-n8n/","icon":"🎯","label":"2021 Goals: Level Up Your Vocabulary With Vonage and n8n"},{"url":"https://n8n.io/blog/2021-the-year-to-automate-the-new-you-with-n8n/","icon":"☀️","label":"2021: The Year to Automate the New You with n8n"},{"url":"https://n8n.io/blog/how-to-build-a-low-code-self-hosted-url-shortener/","icon":"🔗","label":"How to build a low-code, self-hosted URL shortener in 3 steps"},{"url":"https://n8n.io/blog/how-to-get-started-with-crm-automation-and-no-code-workflow-ideas/","icon":"👥","label":"How to get started with CRM automation (with 3 no-code workflow ideas"},{"url":"https://n8n.io/blog/automate-google-apps-for-productivity/","icon":"💡","label":"15 Google apps you can combine and automate to increase productivity"},{"url":"https://n8n.io/blog/building-an-expense-tracking-app-in-10-minutes/","icon":"📱","label":"Building an expense tracking app in 10 minutes"},{"url":"https://n8n.io/blog/why-this-product-manager-loves-workflow-automation-with-n8n/","icon":"🧠","label":"Why this Product Manager loves workflow automation with n8n"},{"url":"https://n8n.io/blog/learn-to-build-powerful-api-endpoints-using-webhooks/","icon":"🧰","label":"Learn to Build Powerful API Endpoints Using Webhooks"},{"url":"https://n8n.io/blog/sending-sms-the-low-code-way-with-airtable-twilio-programmable-sms-and-n8n/","icon":"📱","label":"Sending SMS the Low-Code Way with Airtable, Twilio Programmable SMS, and n8n"},{"url":"https://n8n.io/blog/automating-conference-organization-processes-with-n8n/","icon":"🙋‍♀️","label":"Automating Conference Organization Processes with n8n"},{"url":"https://n8n.io/blog/benefits-of-automation-and-n8n-an-interview-with-hubspots-hugh-durkin/","icon":"🎖","label":"Benefits of automation and n8n: An interview with HubSpot's Hugh Durkin"},{"url":"https://n8n.io/blog/how-goomer-automated-their-operations-with-over-200-n8n-workflows/","icon":"🛵","label":"How Goomer automated their operations with over 200 n8n workflows"},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.airtable/"}],"credentialDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/credentials/airtable/"}]},"categories":["Data & Storage"],"nodeVersion":"1.0","codexVersion":"1.0"}},"group":"[\"input\"]","defaults":{"name":"Airtable"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMDAgMTcwIj48cGF0aCBmaWxsPSIjZmNiNDAwIiBkPSJNODkgNC44IDE2LjIgMzQuOWMtNC4xIDEuNy00IDcuNC4xIDkuMWw3My4yIDI5YzYuNCAyLjYgMTMuNiAyLjYgMjAgMGw3My4yLTI5YzQuMS0xLjYgNC4xLTcuNC4xLTkuMWwtNzMtMzAuMUMxMDMuMiAyIDk1LjcgMiA4OSA0LjgiLz48cGF0aCBmaWxsPSIjMThiZmZmIiBkPSJNMTA1LjkgODguOXY3Mi41YzAgMy40IDMuNSA1LjggNi43IDQuNWw4MS42LTMxLjdjMS45LS43IDMuMS0yLjUgMy4xLTQuNVY1Ny4yYzAtMy40LTMuNS01LjgtNi43LTQuNUwxMDkgODQuM2MtMS45LjgtMy4xIDIuNi0zLjEgNC42Ii8+PHBhdGggZmlsbD0iI2Y4MmI2MCIgZD0ibTg2LjkgOTIuNi0yNC4yIDExLjctMi41IDEuMkw5LjEgMTMwYy0zLjIgMS42LTcuNC0uOC03LjQtNC40VjU3LjVjMC0xLjMuNy0yLjQgMS42LTMuM3EuNi0uNiAxLjItLjljMS4yLS43IDMtLjkgNC40LS4zbDc3LjUgMzAuN2M0IDEuNSA0LjMgNy4xLjUgOC45Ii8+PHBhdGggZmlsbD0iI2JhMWU0NSIgZD0ibTg2LjkgOTIuNi0yNC4yIDExLjctNTkuNC01MHEuNi0uNiAxLjItLjljMS4yLS43IDMtLjkgNC40LS4zbDc3LjUgMzAuN2M0IDEuNCA0LjMgNyAuNSA4LjgiLz48L3N2Zz4="},"displayName":"Airtable","typeVersion":2,"nodeCategories":[{"id":3,"name":"Data & Storage"}]},{"id":19,"icon":"file:httprequest.svg","name":"n8n-nodes-base.httpRequest","codex":{"data":{"alias":["API","Request","URL","Build","cURL"],"resources":{"generic":[{"url":"https://n8n.io/blog/2021-the-year-to-automate-the-new-you-with-n8n/","icon":"☀️","label":"2021: The Year to Automate the New You with n8n"},{"url":"https://n8n.io/blog/why-business-process-automation-with-n8n-can-change-your-daily-life/","icon":"🧬","label":"Why business process automation with n8n can change your daily life"},{"url":"https://n8n.io/blog/automatically-pulling-and-visualizing-data-with-n8n/","icon":"📈","label":"Automatically pulling and visualizing data with n8n"},{"url":"https://n8n.io/blog/learn-how-to-automatically-cross-post-your-content-with-n8n/","icon":"✍️","label":"Learn how to automatically cross-post your content with n8n"},{"url":"https://n8n.io/blog/automatically-adding-expense-receipts-to-google-sheets-with-telegram-mindee-twilio-and-n8n/","icon":"🧾","label":"Automatically Adding Expense Receipts to Google Sheets with Telegram, Mindee, Twilio, and n8n"},{"url":"https://n8n.io/blog/running-n8n-on-ships-an-interview-with-maranics/","icon":"🛳","label":"Running n8n on ships: An interview with Maranics"},{"url":"https://n8n.io/blog/what-are-apis-how-to-use-them-with-no-code/","icon":" 🪢","label":"What are APIs and how to use them with no code"},{"url":"https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/","icon":"⚡️","label":"5 tasks you can automate with the new Notion API "},{"url":"https://n8n.io/blog/world-poetry-day-workflow/","icon":"📜","label":"Celebrating World Poetry Day with a daily poem in Telegram"},{"url":"https://n8n.io/blog/automate-google-apps-for-productivity/","icon":"💡","label":"15 Google apps you can combine and automate to increase productivity"},{"url":"https://n8n.io/blog/automate-designs-with-bannerbear-and-n8n/","icon":"🎨","label":"Automate Designs with Bannerbear and n8n"},{"url":"https://n8n.io/blog/how-uproc-scraped-a-multi-page-website-with-a-low-code-workflow/","icon":" 🕸️","label":"How uProc scraped a multi-page website with a low-code workflow"},{"url":"https://n8n.io/blog/building-an-expense-tracking-app-in-10-minutes/","icon":"📱","label":"Building an expense tracking app in 10 minutes"},{"url":"https://n8n.io/blog/5-workflow-automations-for-mattermost-that-we-love-at-n8n/","icon":"🤖","label":"5 workflow automations for Mattermost that we love at n8n"},{"url":"https://n8n.io/blog/how-to-use-the-http-request-node-the-swiss-army-knife-for-workflow-automation/","icon":"🧰","label":"How to use the HTTP Request Node - The Swiss Army Knife for Workflow Automation"},{"url":"https://n8n.io/blog/learn-how-to-use-webhooks-with-mattermost-slash-commands/","icon":"🦄","label":"Learn how to use webhooks with Mattermost slash commands"},{"url":"https://n8n.io/blog/how-a-membership-development-manager-automates-his-work-and-investments/","icon":"📈","label":"How a Membership Development Manager automates his work and investments"},{"url":"https://n8n.io/blog/a-low-code-bitcoin-ticker-built-with-questdb-and-n8n-io/","icon":"📈","label":"A low-code bitcoin ticker built with QuestDB and n8n.io"},{"url":"https://n8n.io/blog/how-to-set-up-a-ci-cd-pipeline-with-no-code/","icon":"🎡","label":"How to set up a no-code CI/CD pipeline with GitHub and TravisCI"},{"url":"https://n8n.io/blog/automations-for-activists/","icon":"✨","label":"How Common Knowledge use workflow automation for activism"},{"url":"https://n8n.io/blog/creating-scheduled-text-affirmations-with-n8n/","icon":"🤟","label":"Creating scheduled text affirmations with n8n"},{"url":"https://n8n.io/blog/how-goomer-automated-their-operations-with-over-200-n8n-workflows/","icon":"🛵","label":"How Goomer automated their operations with over 200 n8n workflows"},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/"}]},"categories":["Development","Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers"]}}},"group":"[\"output\"]","defaults":{"name":"HTTP Request","color":"#0004F5"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00MCAyMEM0MCA4Ljk1MzE0IDMxLjA0NjkgMCAyMCAwQzguOTUzMTQgMCAwIDguOTUzMTQgMCAyMEMwIDMxLjA0NjkgOC45NTMxNCA0MCAyMCA0MEMzMS4wNDY5IDQwIDQwIDMxLjA0NjkgNDAgMjBaTTIwIDM2Ljk0NThDMTguODg1MiAzNi45NDU4IDE3LjEzNzggMzUuOTY3IDE1LjQ5OTggMzIuNjk4NUMxNC43OTY0IDMxLjI5MTggMTQuMTk2MSAyOS41NDMxIDEzLjc1MjYgMjcuNjg0N0gyNi4xODk4QzI1LjgwNDUgMjkuNTQwMyAyNS4yMDQ0IDMxLjI5MDEgMjQuNTAwMiAzMi42OTg1QzIyLjg2MjIgMzUuOTY3IDIxLjExNDggMzYuOTQ1OCAyMCAzNi45NDU4Wk0xMi45MDY0IDIwQzEyLjkwNjQgMjEuNjA5NyAxMy4wMDg3IDIzLjE2NCAxMy4yMDAzIDI0LjYzMDVIMjYuNzk5N0MyNi45OTEzIDIzLjE2NCAyNy4wOTM2IDIxLjYwOTcgMjcuMDkzNiAyMEMyNy4wOTM2IDE4LjM5MDMgMjYuOTkxMyAxNi44MzYgMjYuNzk5NyAxNS4zNjk1SDEzLjIwMDNDMTMuMDA4NyAxNi44MzYgMTIuOTA2NCAxOC4zOTAzIDEyLjkwNjQgMjBaTTIwIDMuMDU0MTlDMjEuMTE0OSAzLjA1NDE5IDIyLjg2MjIgNC4wMzA3OCAyNC41MDAxIDcuMzAwMzlDMjUuMjA2NiA4LjcxNDA4IDI1LjgwNzIgMTAuNDA2NyAyNi4xOTIgMTIuMzE1M0gxMy43NTAxQzE0LjE5MzMgMTAuNDA0NyAxNC43OTQyIDguNzEyNTQgMTUuNDk5OCA3LjMwMDY0QzE3LjEzNzcgNC4wMzA4MyAxOC44ODUxIDMuMDU0MTkgMjAgMy4wNTQxOVpNMzAuMTQ3OCAyMEMzMC4xNDc4IDE4LjQwOTkgMzAuMDU0MyAxNi44NjE3IDI5LjgyMjcgMTUuMzY5NUgzNi4zMDQyQzM2LjcyNTIgMTYuODQyIDM2Ljk0NTggMTguMzk2NCAzNi45NDU4IDIwQzM2Ljk0NTggMjEuNjAzNiAzNi43MjUyIDIzLjE1OCAzNi4zMDQyIDI0LjYzMDVIMjkuODIyN0MzMC4wNTQzIDIzLjEzODMgMzAuMTQ3OCAyMS41OTAxIDMwLjE0NzggMjBaTTI2LjI3NjcgNC4yNTUxMkMyNy42MzY1IDYuMzYwMTkgMjguNzExIDkuMTMyIDI5LjM3NzQgMTIuMzE1M0gzNS4xMDQ2QzMzLjI1MTEgOC42NjggMzAuMTA3IDUuNzgzNDYgMjYuMjc2NyA0LjI1NTEyWk0xMC42MjI2IDEyLjMxNTNINC44OTI5M0M2Ljc1MTQ3IDguNjY3ODQgOS44OTM1MSA1Ljc4MzQxIDEzLjcyMzIgNC4yNTUxM0MxMi4zNjM1IDYuMzYwMjEgMTEuMjg5IDkuMTMyMDEgMTAuNjIyNiAxMi4zMTUzWk0zLjA1NDE5IDIwQzMuMDU0MTkgMjEuNjAzIDMuMjc3NDMgMjMuMTU3NSAzLjY5NDg0IDI0LjYzMDVIMTAuMTIxN0M5Ljk0NjE5IDIzLjE0MiA5Ljg1MjIyIDIxLjU5NDMgOS44NTIyMiAyMEM5Ljg1MjIyIDE4LjQwNTcgOS45NDYxOSAxNi44NTggMTAuMTIxNyAxNS4zNjk1SDMuNjk0ODRDMy4yNzc0MyAxNi44NDI1IDMuMDU0MTkgMTguMzk3IDMuMDU0MTkgMjBaTTI2LjI3NjYgMzUuNzQyN0MyNy42MzY1IDMzLjYzOTMgMjguNzExIDMwLjg2OCAyOS4zNzc0IDI3LjY4NDdIMzUuMTA0NkMzMy4yNTEgMzEuMzMyMiAzMC4xMDY4IDM0LjIxNzkgMjYuMjc2NiAzNS43NDI3Wk0xMy43MjM0IDM1Ljc0MjdDOS44OTM2OSAzNC4yMTc5IDYuNzUxNTUgMzEuMzMyNCA0Ljg5MjkzIDI3LjY4NDdIMTAuNjIyNkMxMS4yODkgMzAuODY4IDEyLjM2MzUgMzMuNjM5MyAxMy43MjM0IDM1Ljc0MjdaIiBmaWxsPSIjM0E0MkU5Ii8+Cjwvc3ZnPgo="},"displayName":"HTTP Request","typeVersion":4,"nodeCategories":[{"id":5,"name":"Development"},{"id":9,"name":"Core Nodes"}]},{"id":20,"icon":"fa:map-signs","name":"n8n-nodes-base.if","codex":{"data":{"alias":["Router","Filter","Condition","Logic","Boolean","Branch"],"details":"The IF node can be used to implement binary conditional logic in your workflow. You can set up one-to-many conditions to evaluate each item of data being inputted into the node. That data will either evaluate to TRUE or FALSE and route out of the node accordingly.\n\nThis node has multiple types of conditions: Bool, String, Number, and Date & Time.","resources":{"generic":[{"url":"https://n8n.io/blog/learn-to-automate-your-factorys-incident-reporting-a-step-by-step-guide/","icon":"🏭","label":"Learn to Automate Your Factory's Incident Reporting: A Step by Step Guide"},{"url":"https://n8n.io/blog/2021-the-year-to-automate-the-new-you-with-n8n/","icon":"☀️","label":"2021: The Year to Automate the New You with n8n"},{"url":"https://n8n.io/blog/why-business-process-automation-with-n8n-can-change-your-daily-life/","icon":"🧬","label":"Why business process automation with n8n can change your daily life"},{"url":"https://n8n.io/blog/create-a-toxic-language-detector-for-telegram/","icon":"🤬","label":"Create a toxic language detector for Telegram in 4 step"},{"url":"https://n8n.io/blog/no-code-ecommerce-workflow-automations/","icon":"store","label":"6 e-commerce workflows to power up your Shopify s"},{"url":"https://n8n.io/blog/how-to-build-a-low-code-self-hosted-url-shortener/","icon":"🔗","label":"How to build a low-code, self-hosted URL shortener in 3 steps"},{"url":"https://n8n.io/blog/automate-your-data-processing-pipeline-in-9-steps-with-n8n/","icon":"⚙️","label":"Automate your data processing pipeline in 9 steps"},{"url":"https://n8n.io/blog/how-to-get-started-with-crm-automation-and-no-code-workflow-ideas/","icon":"👥","label":"How to get started with CRM automation (with 3 no-code workflow ideas"},{"url":"https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/","icon":"⚡️","label":"5 tasks you can automate with the new Notion API "},{"url":"https://n8n.io/blog/automate-google-apps-for-productivity/","icon":"💡","label":"15 Google apps you can combine and automate to increase productivity"},{"url":"https://n8n.io/blog/automation-for-maintainers-of-open-source-projects/","icon":"🏷️","label":"How to automatically manage contributions to open-source projects"},{"url":"https://n8n.io/blog/how-uproc-scraped-a-multi-page-website-with-a-low-code-workflow/","icon":" 🕸️","label":"How uProc scraped a multi-page website with a low-code workflow"},{"url":"https://n8n.io/blog/5-workflow-automations-for-mattermost-that-we-love-at-n8n/","icon":"🤖","label":"5 workflow automations for Mattermost that we love at n8n"},{"url":"https://n8n.io/blog/why-this-product-manager-loves-workflow-automation-with-n8n/","icon":"🧠","label":"Why this Product Manager loves workflow automation with n8n"},{"url":"https://n8n.io/blog/sending-automated-congratulations-with-google-sheets-twilio-and-n8n/","icon":"🙌","label":"Sending Automated Congratulations with Google Sheets, Twilio, and n8n "},{"url":"https://n8n.io/blog/how-to-set-up-a-ci-cd-pipeline-with-no-code/","icon":"🎡","label":"How to set up a no-code CI/CD pipeline with GitHub and TravisCI"},{"url":"https://n8n.io/blog/benefits-of-automation-and-n8n-an-interview-with-hubspots-hugh-durkin/","icon":"🎖","label":"Benefits of automation and n8n: An interview with HubSpot's Hugh Durkin"},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.if/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Flow"]}}},"group":"[\"transform\"]","defaults":{"name":"If","color":"#408000"},"iconData":{"icon":"map-signs","type":"icon"},"displayName":"If","typeVersion":2,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":47,"icon":"file:webhook.svg","name":"n8n-nodes-base.webhook","codex":{"data":{"alias":["HTTP","API","Build","WH"],"resources":{"generic":[{"url":"https://n8n.io/blog/learn-how-to-automatically-cross-post-your-content-with-n8n/","icon":"✍️","label":"Learn how to automatically cross-post your content with n8n"},{"url":"https://n8n.io/blog/running-n8n-on-ships-an-interview-with-maranics/","icon":"🛳","label":"Running n8n on ships: An interview with Maranics"},{"url":"https://n8n.io/blog/how-to-build-a-low-code-self-hosted-url-shortener/","icon":"🔗","label":"How to build a low-code, self-hosted URL shortener in 3 steps"},{"url":"https://n8n.io/blog/what-are-apis-how-to-use-them-with-no-code/","icon":" 🪢","label":"What are APIs and how to use them with no code"},{"url":"https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/","icon":"⚡️","label":"5 tasks you can automate with the new Notion API "},{"url":"https://n8n.io/blog/how-a-digital-strategist-uses-n8n-for-online-marketing/","icon":"💻","label":"How a digital strategist uses n8n for online marketing"},{"url":"https://n8n.io/blog/the-ultimate-guide-to-automate-your-video-collaboration-with-whereby-mattermost-and-n8n/","icon":"📹","label":"The ultimate guide to automate your video collaboration with Whereby, Mattermost, and n8n"},{"url":"https://n8n.io/blog/how-to-automatically-give-kudos-to-contributors-with-github-slack-and-n8n/","icon":"👏","label":"How to automatically give kudos to contributors with GitHub, Slack, and n8n"},{"url":"https://n8n.io/blog/5-workflow-automations-for-mattermost-that-we-love-at-n8n/","icon":"🤖","label":"5 workflow automations for Mattermost that we love at n8n"},{"url":"https://n8n.io/blog/why-this-product-manager-loves-workflow-automation-with-n8n/","icon":"🧠","label":"Why this Product Manager loves workflow automation with n8n"},{"url":"https://n8n.io/blog/creating-custom-incident-response-workflows-with-n8n/","label":"How to automate every step of an incident response workflow"},{"url":"https://n8n.io/blog/learn-to-build-powerful-api-endpoints-using-webhooks/","icon":"🧰","label":"Learn to Build Powerful API Endpoints Using Webhooks"},{"url":"https://n8n.io/blog/learn-how-to-use-webhooks-with-mattermost-slash-commands/","icon":"🦄","label":"Learn how to use webhooks with Mattermost slash commands"},{"url":"https://n8n.io/blog/how-goomer-automated-their-operations-with-over-200-n8n-workflows/","icon":"🛵","label":"How Goomer automated their operations with over 200 n8n workflows"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.webhook/"}]},"categories":["Development","Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers"]}}},"group":"[\"trigger\"]","defaults":{"name":"Webhook"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCI+PHBhdGggZmlsbD0iIzM3NDc0ZiIgZD0iTTM1IDM3Yy0yLjIgMC00LTEuOC00LTRzMS44LTQgNC00IDQgMS44IDQgNC0xLjggNC00IDQiLz48cGF0aCBmaWxsPSIjMzc0NzRmIiBkPSJNMzUgNDNjLTMgMC01LjktMS40LTcuOC0zLjdsMy4xLTIuNWMxLjEgMS40IDIuOSAyLjMgNC43IDIuMyAzLjMgMCA2LTIuNyA2LTZzLTIuNy02LTYtNmMtMSAwLTIgLjMtMi45LjdsLTEuNyAxTDIzLjMgMTZsMy41LTEuOSA1LjMgOS40YzEtLjMgMi0uNSAzLS41IDUuNSAwIDEwIDQuNSAxMCAxMFM0MC41IDQzIDM1IDQzIi8+PHBhdGggZmlsbD0iIzM3NDc0ZiIgZD0iTTE0IDQzQzguNSA0MyA0IDM4LjUgNCAzM2MwLTQuNiAzLjEtOC41IDcuNS05LjdsMSAzLjlDOS45IDI3LjkgOCAzMC4zIDggMzNjMCAzLjMgMi43IDYgNiA2czYtMi43IDYtNnYtMmgxNXY0SDIzLjhjLS45IDQuNi01IDgtOS44IDgiLz48cGF0aCBmaWxsPSIjZTkxZTYzIiBkPSJNMTQgMzdjLTIuMiAwLTQtMS44LTQtNHMxLjgtNCA0LTQgNCAxLjggNCA0LTEuOCA0LTQgNCIvPjxwYXRoIGZpbGw9IiMzNzQ3NGYiIGQ9Ik0yNSAxOWMtMi4yIDAtNC0xLjgtNC00czEuOC00IDQtNCA0IDEuOCA0IDQtMS44IDQtNCA0Ii8+PHBhdGggZmlsbD0iI2U5MWU2MyIgZD0ibTE1LjcgMzQtMy40LTIgNS45LTkuN2MtMi0xLjktMy4yLTQuNS0zLjItNy4zIDAtNS41IDQuNS0xMCAxMC0xMHMxMCA0LjUgMTAgMTBjMCAuOS0uMSAxLjctLjMgMi41bC0zLjktMWMuMS0uNS4yLTEgLjItMS41IDAtMy4zLTIuNy02LTYtNnMtNiAyLjctNiA2YzAgMi4xIDEuMSA0IDIuOSA1LjFsMS43IDF6Ii8+PC9zdmc+"},"displayName":"Webhook","typeVersion":2,"nodeCategories":[{"id":5,"name":"Development"},{"id":9,"name":"Core Nodes"}]},{"id":112,"icon":"fa:map-signs","name":"n8n-nodes-base.switch","codex":{"data":{"alias":["Router","If","Path","Filter","Condition","Logic","Branch","Case"],"resources":{"generic":[{"url":"https://n8n.io/blog/2021-the-year-to-automate-the-new-you-with-n8n/","icon":"☀️","label":"2021: The Year to Automate the New You with n8n"},{"url":"https://n8n.io/blog/how-to-get-started-with-crm-automation-and-no-code-workflow-ideas/","icon":"👥","label":"How to get started with CRM automation (with 3 no-code workflow ideas"},{"url":"https://n8n.io/blog/build-your-own-virtual-assistant-with-n8n-a-step-by-step-guide/","icon":"👦","label":"Build your own virtual assistant with n8n: A step by step guide"},{"url":"https://n8n.io/blog/automation-for-maintainers-of-open-source-projects/","icon":"🏷️","label":"How to automatically manage contributions to open-source projects"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.switch/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Flow"]}}},"group":"[\"transform\"]","defaults":{"name":"Switch","color":"#506000"},"iconData":{"icon":"map-signs","type":"icon"},"displayName":"Switch","typeVersion":3,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":317,"icon":"file:googleCalendar.svg","name":"n8n-nodes-base.googleCalendar","codex":{"data":{"resources":{"generic":[{"url":"https://n8n.io/blog/how-to-host-virtual-coffee-breaks-with-n8n/","icon":"☕️","label":"How to host virtual coffee breaks with n8n"},{"url":"https://n8n.io/blog/supercharging-your-conference-registration-process-with-n8n/","icon":"🎫","label":"Supercharging your conference registration process with n8n"},{"url":"https://n8n.io/blog/automate-google-apps-for-productivity/","icon":"💡","label":"15 Google apps you can combine and automate to increase productivity"},{"url":"https://n8n.io/blog/your-business-doesnt-need-you-to-operate/","icon":" 🖥️","label":"Hey founders! Your business doesn't need you to operate"},{"url":"https://n8n.io/blog/5-workflow-automations-for-mattermost-that-we-love-at-n8n/","icon":"🤖","label":"5 workflow automation for Mattermost that we love at n8n"},{"url":"https://n8n.io/blog/tracking-time-spent-in-meetings-with-google-calendar-twilio-and-n8n/","icon":"🗓","label":"Tracking Time Spent in Meetings With Google Calendar, Twilio, and n8n"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlecalendar/"}],"credentialDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/credentials/google/oauth-single-service/"}]},"categories":["Productivity"],"nodeVersion":"1.0","codexVersion":"1.0"}},"group":"[\"input\"]","defaults":{"name":"Google Calendar"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiB2aWV3Qm94PSIwIDAgODEgODIiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0ibm9uZSI+PHBhdGggZD0iTTYxLjA1MiAxOC45NDdIMTguOTQ3djQyLjEwNWg0Mi4xMDV6Ii8+PHBhdGggZmlsbD0iI2VhNDMzNSIgZD0iTTYxLjA1MyA4MCA4MCA2MS4wNTNINjEuMDUzeiIvPjxwYXRoIGZpbGw9IiNmYmJjMDQiIGQ9Ik04MCAxOC45NDdINjEuMDUzdjQyLjEwNUg4MHoiLz48cGF0aCBmaWxsPSIjMzRhODUzIiBkPSJNNjEuMDUyIDYxLjA1M0gxOC45NDdWODBoNDIuMTA1eiIvPjxwYXRoIGZpbGw9IiMxODgwMzgiIGQ9Ik0wIDYxLjA1M3YxMi42MzJBNi4zMTQgNi4zMTQgMCAwIDAgNi4zMTYgODBoMTIuNjMyVjYxLjA1M3oiLz48cGF0aCBmaWxsPSIjMTk2N2QyIiBkPSJNODAgMTguOTQ3VjYuMzE2QTYuMzE0IDYuMzE0IDAgMCAwIDczLjY4NSAwSDYxLjA1M3YxOC45NDd6Ii8+PHBhdGggZmlsbD0iIzQyODVmNCIgZD0iTTYxLjA1MyAwSDYuMzE2QTYuMzE0IDYuMzE0IDAgMCAwIDAgNi4zMTZ2NTQuNzM3aDE4Ljk0N1YxOC45NDdoNDIuMTA1VjB6TTI3LjU4NCA1MS42MTFjLTEuNTc0LTEuMDYzLTIuNjYzLTIuNjE2LTMuMjU4LTQuNjY4bDMuNjUzLTEuNTA1cS40OTggMS44OTQgMS43MzcgMi45MzdjMS4yMzkgMS4wNDMgMS44MjEgMS4wMzcgMi45ODkgMS4wMzdxMS43OTIgMCAzLjA3OS0xLjA4OWMxLjI4Ny0xLjA4OSAxLjI5LTEuNjUzIDEuMjktMi43NzRhMy40NCAzLjQ0IDAgMCAwLTEuMzU4LTIuODExYy0uOTA1LS43MjctMi4wNDItMS4wODktMy40LTEuMDg5aC0yLjExMXYtMy42MTZIMzIuMXExLjc1MiAwIDIuOTUzLS45NDdjMS4yMDEtLjk0NyAxLjItMS40OTUgMS4yLTIuNTk1cTAtMS40NjctMS4wNzQtMi4zNDJjLTEuMDc0LS44NzUtMS42MjEtLjg3OS0yLjcyMS0uODc5cS0xLjYxLS4wMDItMi41NTguODU4Yy0uOTQ4Ljg2LTEuMTA2IDEuMzAxLTEuMzc5IDIuMTExbC0zLjYxNi0xLjUwNWMuNDc5LTEuMzU4IDEuMzU4LTIuNTU4IDIuNjQ3LTMuNTk1czIuOTM3LTEuNTU4IDQuOTM3LTEuNTU4cTIuMjItLjAwMiAzLjk4OS44NThjMS43NjkuODYgMi4xMDUgMS4zNjggMi43NzQgMi4zNzlzMSAyLjE1MyAxIDMuNDE2cTAgMS45MzItLjkzMiAzLjI3NGMtLjkzMiAxLjM0Mi0xLjM4NCAxLjU3OS0yLjI4OSAyLjA1OHYuMjE2YTYuOTUgNi45NSAwIDAgMSAyLjkzNyAyLjI4OXExLjE0NiAxLjUzOCAxLjE0NyAzLjY4NGMuMDAxIDIuMTQ2LS4zNjMgMi43MTEtMS4wODkgMy44MzJzLTEuNzMyIDIuMDA1LTMuMDA1IDIuNjQ3Yy0xLjI3OS42NDItMi43MTYuOTY4LTQuMzExLjk2OC0xLjg0Ny4wMDUtMy41NTMtLjUyNi01LjEyNi0xLjU4OXptMjIuNDM3LTE4LjEyNi00LjAxIDIuOS0yLjAwNS0zLjA0MiA3LjE5NS01LjE4OWgyLjc1OHYyNC40NzloLTMuOTM3VjMzLjQ4NHoiLz48L2c+PC9zeW1ib2w+PC9zdmc+"},"displayName":"Google Calendar","typeVersion":1,"nodeCategories":[{"id":4,"name":"Productivity"}]},{"id":535,"icon":"file:webhook.svg","name":"n8n-nodes-base.respondToWebhook","codex":{"data":{"resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.respondtowebhook/"}]},"categories":["Core Nodes","Utility"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers"]}}},"group":"[\"transform\"]","defaults":{"name":"Respond to Webhook"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCI+PHBhdGggZmlsbD0iIzM3NDc0ZiIgZD0iTTM1IDM3Yy0yLjIgMC00LTEuOC00LTRzMS44LTQgNC00IDQgMS44IDQgNC0xLjggNC00IDQiLz48cGF0aCBmaWxsPSIjMzc0NzRmIiBkPSJNMzUgNDNjLTMgMC01LjktMS40LTcuOC0zLjdsMy4xLTIuNWMxLjEgMS40IDIuOSAyLjMgNC43IDIuMyAzLjMgMCA2LTIuNyA2LTZzLTIuNy02LTYtNmMtMSAwLTIgLjMtMi45LjdsLTEuNyAxTDIzLjMgMTZsMy41LTEuOSA1LjMgOS40YzEtLjMgMi0uNSAzLS41IDUuNSAwIDEwIDQuNSAxMCAxMFM0MC41IDQzIDM1IDQzIi8+PHBhdGggZmlsbD0iIzM3NDc0ZiIgZD0iTTE0IDQzQzguNSA0MyA0IDM4LjUgNCAzM2MwLTQuNiAzLjEtOC41IDcuNS05LjdsMSAzLjlDOS45IDI3LjkgOCAzMC4zIDggMzNjMCAzLjMgMi43IDYgNiA2czYtMi43IDYtNnYtMmgxNXY0SDIzLjhjLS45IDQuNi01IDgtOS44IDgiLz48cGF0aCBmaWxsPSIjZTkxZTYzIiBkPSJNMTQgMzdjLTIuMiAwLTQtMS44LTQtNHMxLjgtNCA0LTQgNCAxLjggNCA0LTEuOCA0LTQgNCIvPjxwYXRoIGZpbGw9IiMzNzQ3NGYiIGQ9Ik0yNSAxOWMtMi4yIDAtNC0xLjgtNC00czEuOC00IDQtNCA0IDEuOCA0IDQtMS44IDQtNCA0Ii8+PHBhdGggZmlsbD0iI2U5MWU2MyIgZD0ibTE1LjcgMzQtMy40LTIgNS45LTkuN2MtMi0xLjktMy4yLTQuNS0zLjItNy4zIDAtNS41IDQuNS0xMCAxMC0xMHMxMCA0LjUgMTAgMTBjMCAuOS0uMSAxLjctLjMgMi41bC0zLjktMWMuMS0uNS4yLTEgLjItMS41IDAtMy4zLTIuNy02LTYtNnMtNiAyLjctNiA2YzAgMi4xIDEuMSA0IDIuOSA1LjFsMS43IDF6Ii8+PC9zdmc+"},"displayName":"Respond to Webhook","typeVersion":2,"nodeCategories":[{"id":7,"name":"Utility"},{"id":9,"name":"Core Nodes"}]},{"id":565,"icon":"fa:sticky-note","name":"n8n-nodes-base.stickyNote","codex":{"data":{"alias":["Comments","Notes","Sticky"],"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers"]}}},"group":"[\"input\"]","defaults":{"name":"Sticky Note","color":"#FFD233"},"iconData":{"icon":"sticky-note","type":"icon"},"displayName":"Sticky Note","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":834,"icon":"file:code.svg","name":"n8n-nodes-base.code","codex":{"data":{"alias":["cpde","Javascript","JS","Python","Script","Custom Code","Function"],"details":"The Code node allows you to execute JavaScript in your workflow.","resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.code/"}]},"categories":["Development","Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers","Data Transformation"]}}},"group":"[\"transform\"]","defaults":{"name":"Code"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xMTcxXzQ0MSkiPgo8cGF0aCBkPSJNMTcwLjI4MyA0OEgxOTYuNUMyMDMuMTI3IDQ4IDIwOC41IDQyLjYyNzQgMjA4LjUgMzZWMTJDMjA4LjUgNS4zNzI1OCAyMDMuMTI3IDAgMTk2LjUgMEgxNzAuMjgzQzEyNi4xIDAgOTAuMjgzIDM1LjgxNzIgOTAuMjgzIDgwVjE3NkM5MC4yODMgMjA2LjkyOCA2NS4yMTA5IDIzMiAzNC4yODMgMjMySDIzQzE2LjM3MjYgMjMyIDExIDIzNy4zNzIgMTEgMjQ0VjI2OEMxMSAyNzQuNjI3IDE2LjM3MjQgMjgwIDIyLjk5OTYgMjgwTDM0LjI4MyAyODBDNjUuMjEwOSAyODAgOTAuMjgzIDMwNS4wNzIgOTAuMjgzIDMzNlY0NDBDOTAuMjgzIDQ3OS43NjQgMTIyLjUxOCA1MTIgMTYyLjI4MyA1MTJIMTk2LjVDMjAzLjEyNyA1MTIgMjA4LjUgNTA2LjYyNyAyMDguNSA1MDBWNDc2QzIwOC41IDQ2OS4zNzMgMjAzLjEyNyA0NjQgMTk2LjUgNDY0SDE2Mi4yODNDMTQ5LjAyOCA0NjQgMTM4LjI4MyA0NTMuMjU1IDEzOC4yODMgNDQwVjMzNkMxMzguMjgzIDMwOS4wMjIgMTI4LjAxMSAyODQuNDQzIDExMS4xNjQgMjY1Ljk2MUMxMDYuMTA5IDI2MC40MTYgMTA2LjEwOSAyNTEuNTg0IDExMS4xNjQgMjQ2LjAzOUMxMjguMDExIDIyNy41NTcgMTM4LjI4MyAyMDIuOTc4IDEzOC4yODMgMTc2VjgwQzEzOC4yODMgNjIuMzI2OSAxNTIuNjEgNDggMTcwLjI4MyA0OFoiIGZpbGw9IiNGRjk5MjIiLz4KPHBhdGggZD0iTTMwNSAzNkMzMDUgNDIuNjI3NCAzMTAuMzczIDQ4IDMxNyA0OEgzNDIuOTc5QzM2MC42NTIgNDggMzc0Ljk3OCA2Mi4zMjY5IDM3NC45NzggODBWMTc2QzM3NC45NzggMjAyLjk3OCAzODUuMjUxIDIyNy41NTcgNDAyLjA5OCAyNDYuMDM5QzQwNy4xNTMgMjUxLjU4NCA0MDcuMTUzIDI2MC40MTYgNDAyLjA5OCAyNjUuOTYxQzM4NS4yNTEgMjg0LjQ0MyAzNzQuOTc4IDMwOS4wMjIgMzc0Ljk3OCAzMzZWNDMyQzM3NC45NzggNDQ5LjY3MyAzNjAuNjUyIDQ2NCAzNDIuOTc5IDQ2NEgzMTdDMzEwLjM3MyA0NjQgMzA1IDQ2OS4zNzMgMzA1IDQ3NlY1MDBDMzA1IDUwNi42MjcgMzEwLjM3MyA1MTIgMzE3IDUxMkgzNDIuOTc5QzM4Ny4xNjEgNTEyIDQyMi45NzggNDc2LjE4MyA0MjIuOTc4IDQzMlYzMzZDNDIyLjk3OCAzMDUuMDcyIDQ0OC4wNTEgMjgwIDQ3OC45NzkgMjgwSDQ5MEM0OTYuNjI3IDI4MCA1MDIgMjc0LjYyOCA1MDIgMjY4VjI0NEM1MDIgMjM3LjM3MyA0OTYuNjI4IDIzMiA0OTAgMjMyTDQ3OC45NzkgMjMyQzQ0OC4wNTEgMjMyIDQyMi45NzggMjA2LjkyOCA0MjIuOTc4IDE3NlY4MEM0MjIuOTc4IDM1LjgxNzIgMzg3LjE2MSAwIDM0Mi45NzkgMEgzMTdDMzEwLjM3MyAwIDMwNSA1LjM3MjU4IDMwNSAxMlYzNloiIGZpbGw9IiNGRjk5MjIiLz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF8xMTcxXzQ0MSI+CjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo="},"displayName":"Code","typeVersion":2,"nodeCategories":[{"id":5,"name":"Development"},{"id":9,"name":"Core Nodes"}]},{"id":1119,"icon":"fa:robot","name":"@n8n/n8n-nodes-langchain.agent","codex":{"data":{"alias":["LangChain","Chat","Conversational","Plan and Execute","ReAct","Tools"],"resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.agent/"}]},"categories":["AI","Langchain"],"subcategories":{"AI":["Agents","Root Nodes"]}}},"group":"[\"transform\"]","defaults":{"name":"AI Agent","color":"#404040"},"iconData":{"icon":"robot","type":"icon"},"displayName":"AI Agent","typeVersion":3,"nodeCategories":[{"id":25,"name":"AI"},{"id":26,"name":"Langchain"}]},{"id":1153,"icon":"file:openAiLight.svg","name":"@n8n/n8n-nodes-langchain.lmChatOpenAi","codex":{"data":{"resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.lmchatopenai/"}]},"categories":["AI","Langchain"],"subcategories":{"AI":["Language Models","Root Nodes"],"Language Models":["Chat Models (Recommended)"]}}},"group":"[\"transform\"]","defaults":{"name":"OpenAI Chat Model"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTM2Ljg2NzEgMTYuMzcxOEMzNy43NzQ2IDEzLjY0OCAzNy40NjIxIDEwLjY2NDIgMzYuMDEwOCA4LjE4NjYxQzMzLjgyODIgNC4zODY1MyAyOS40NDA3IDIuNDMxNDkgMjUuMTU1NiAzLjM1MTUxQzIzLjI0OTMgMS4yMDM5NiAyMC41MTA1IC0wLjAxNzMxNDggMTcuNjM5MiAwLjAwMDE4NTUzM0MxMy4yNTkxIC0wLjAwOTgxNDY4IDkuMzcyNzMgMi44MTAyNSA4LjAyNTIgNi45Nzc4M0M1LjIxMTM5IDcuNTU0MSAyLjc4MjU4IDkuMzE1MzggMS4zNjEzIDExLjgxMTdDLTAuODM3NDkzIDE1LjYwMTggLTAuMzM2MjMyIDIwLjM3OTQgMi42MDEzMyAyMy42Mjk0QzEuNjkzODEgMjYuMzUzMiAyLjAwNjMyIDI5LjMzNzEgMy40NTc2IDMxLjgxNDZDNS42NDAxNSAzNS42MTQ3IDEwLjAyNzcgMzcuNTY5NyAxNC4zMTI4IDM2LjY0OTdDMTYuMjE3OSAzOC43OTczIDE4Ljk1NzkgNDAuMDE4NSAyMS44MjkyIDM5Ljk5OThDMjYuMjExOCA0MC4wMTEgMzAuMDk5NCAzNy4xODg1IDMxLjQ0NjkgMzMuMDE3MUMzNC4yNjA4IDMyLjQ0MDkgMzYuNjg5NiAzMC42Nzk2IDM4LjExMDggMjguMTgzM0M0MC4zMDcxIDI0LjM5MzIgMzkuODA0NiAxOS42MTk0IDM2Ljg2ODMgMTYuMzY5M0wzNi44NjcxIDE2LjM3MThaTTIxLjgzMTcgMzcuMzg2QzIwLjA3OCAzNy4zODg1IDE4LjM3OTIgMzYuNzc0NyAxNy4wMzI5IDM1LjY1MDlDMTcuMDk0MSAzNS42MTg0IDE3LjIwMDQgMzUuNTU5NyAxNy4yNjkxIDM1LjUxNzJMMjUuMjM0MyAzMC45MTcxQzI1LjY0MTggMzAuNjg1OCAyNS44OTE4IDMwLjI1MjEgMjUuODg5MyAyOS43ODMzVjE4LjU1NDNMMjkuMjU1NyAyMC40OTgxQzI5LjI5MTkgMjAuNTE1NiAyOS4zMTU3IDIwLjU1MDYgMjkuMzIwNyAyMC41OTA2VjI5Ljg4OTZDMjkuMzE1NyAzNC4wMjQ3IDI1Ljk2NjggMzcuMzc3MiAyMS44MzE3IDM3LjM4NlpNNS43MjY0IDMwLjUwNzFDNC44NDc2MyAyOC45ODk2IDQuNTMxMzcgMjcuMjEwOCA0LjgzMjYzIDI1LjQ4NDVDNC44OTEzOCAyNS41MTk1IDQuOTk1MTMgMjUuNTgzMiA1LjA2ODg4IDI1LjYyNTdMMTMuMDM0MSAzMC4yMjU4QzEzLjQzNzggMzAuNDYyMSAxMy45Mzc4IDMwLjQ2MjEgMTQuMzQyOCAzMC4yMjU4TDI0LjA2NjggMjQuNjEwN1YyOC40OTgzQzI0LjA2OTMgMjguNTM4MyAyNC4wNTA1IDI4LjU3NyAyNC4wMTkzIDI4LjYwMkwxNS45Njc5IDMzLjI1MDlDMTIuMzgxNSAzNS4zMTU5IDcuODAxNDQgMzQuMDg4NCA1LjcyNzY1IDMwLjUwNzFINS43MjY0Wk0zLjYzMDEgMTMuMTIwNUM0LjUwNTEyIDExLjYwMDQgNS44ODY0IDEwLjQzNzkgNy41MzE0NCA5LjgzNDE1QzcuNTMxNDQgOS45MDI5IDcuNTI3NjkgMTAuMDI0MiA3LjUyNzY5IDEwLjEwOTJWMTkuMzEwNkM3LjUyNTE5IDE5Ljc3ODEgNy43NzUxOSAyMC4yMTE5IDguMTgxNDUgMjAuNDQzMUwxNy45MDU0IDI2LjA1N0wxNC41MzkxIDI4LjAwMDhDMTQuNTA1MyAyOC4wMjMzIDE0LjQ2MjggMjguMDI3IDE0LjQyNTMgMjguMDEwOEw2LjM3MjY2IDIzLjM1ODJDMi43OTM4MyAyMS4yODU2IDEuNTY2MzEgMTYuNzA2OCAzLjYyODg1IDEzLjEyMTdMMy42MzAxIDEzLjEyMDVaTTMxLjI4ODIgMTkuNTU2OUwyMS41NjQyIDEzLjk0MTdMMjQuOTMwNiAxMS45OTkyQzI0Ljk2NDMgMTEuOTc2NyAyNS4wMDY4IDExLjk3MjkgMjUuMDQ0MyAxMS45ODkyTDMzLjA5NyAxNi42MzhDMzYuNjgyMSAxOC43MDkzIDM3LjkxMDggMjMuMjk1NyAzNS44Mzk1IDI2Ljg4MDhDMzQuOTYzMyAyOC4zOTgzIDMzLjU4MzIgMjkuNTYwOCAzMS45Mzk1IDMwLjE2NThWMjAuNjg5NEMzMS45NDMyIDIwLjIyMTkgMzEuNjk0NSAxOS43ODk0IDMxLjI4OTQgMTkuNTU2OUgzMS4yODgyWk0zNC42MzgzIDE0LjUxNDJDMzQuNTc5NSAxNC40NzggMzQuNDc1OCAxNC40MTU1IDM0LjQwMiAxNC4zNzNMMjYuNDM2OCA5Ljc3Mjg5QzI2LjAzMzEgOS41MzY2NCAyNS41MzMxIDkuNTM2NjQgMjUuMTI4MSA5Ljc3Mjg5TDE1LjQwNDEgMTUuMzg4VjExLjUwMDRDMTUuNDAxNiAxMS40NjA0IDE1LjQyMDQgMTEuNDIxNyAxNS40NTE2IDExLjM5NjdMMjMuNTAzIDYuNzUxNThDMjcuMDg5NCA0LjY4Mjc5IDMxLjY3NDUgNS45MTQwNiAzMy43NDIgOS41MDE2NEMzNC42MTU4IDExLjAxNjcgMzQuOTMyIDEyLjc5MDUgMzQuNjM1OCAxNC41MTQySDM0LjYzODNaTTEzLjU3NDEgMjEuNDQzMUwxMC4yMDY1IDE5LjQ5OTRDMTAuMTcwMiAxOS40ODE5IDEwLjE0NjUgMTkuNDQ2OCAxMC4xNDE1IDE5LjQwNjhWMTAuMTA3OUMxMC4xNDQgNS45Njc4MSAxMy41MDI4IDIuNjEyNzQgMTcuNjQyOSAyLjYxNTI0QzE5LjM5NDIgMi42MTUyNCAyMS4wODkyIDMuMjMwMjUgMjIuNDM1NSA0LjM1MDI4QzIyLjM3NDMgNC4zODI3OCAyMi4yNjkzIDQuNDQxNTMgMjIuMTk5MiA0LjQ4NDAzTDE0LjIzNDEgOS4wODQxM0MxMy44MjY2IDkuMzE1MzggMTMuNTc2NiA5Ljc0Nzg5IDEzLjU3OTEgMTAuMjE2N0wxMy41NzQxIDIxLjQ0MDZWMjEuNDQzMVpNMTUuNDAyOSAxNy41MDA2TDE5LjczNDIgMTQuOTk5M0wyNC4wNjU1IDE3LjQ5OTNWMjIuNTAwN0wxOS43MzQyIDI1LjAwMDdMMTUuNDAyOSAyMi41MDA3VjE3LjUwMDZaIiBmaWxsPSIjN0Q3RDg3Ii8+Cjwvc3ZnPgo="},"displayName":"OpenAI Chat Model","typeVersion":1,"nodeCategories":[{"id":25,"name":"AI"},{"id":26,"name":"Langchain"}]}],"categories":[{"id":40,"name":"Support Chatbot"},{"id":47,"name":"AI Chatbot"}],"image":[]}}