AutomationFlowsAI & RAG › AI WhatsApp Booking Assistant with Gemini & Google Calendar

AI WhatsApp Booking Assistant with Gemini & Google Calendar

Original n8n title: Ai-powered Whatsapp Chatbot 🤖📅: Complete Booking Assistant with Gemini & Google

ByDavide Boizza @n3witalia on n8n.io

This workflow implements an AI-powered WhatsApp booking assistant for a hair salon. The system allows customers to book, reschedule, or cancel appointments automatically via text or voice messages on WhatsApp.

Event trigger★★★★★ complexityAI-powered47 nodesGoogle Calendar ToolGoogle Sheets ToolError TriggerGmailAgentWhatsApp TriggerHTTP RequestOpenAI
AI & RAG Trigger: Event Nodes: 47 Complexity: ★★★★★ AI nodes: yes Added:
AI WhatsApp Booking Assistant with Gemini & Google Calendar — n8n workflow card showing Google Calendar Tool, Google Sheets Tool, Error Trigger integration

This workflow corresponds to n8n.io template #13984 — we link there as the canonical source.

This workflow follows the Agent → Chat Trigger recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "MdmtzLYxHXpTdOUp",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "WhatsApp Complete AI Simple Booking",
  "tags": [],
  "nodes": [
    {
      "id": "fec9d00e-0a8a-4a1f-aafe-8b30055784b5",
      "name": "Update event",
      "type": "n8n-nodes-base.googleCalendarTool",
      "position": [
        720,
        704
      ],
      "parameters": {
        "eventId": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Event_ID', `The unique ID string from the search result `, 'string') }}",
        "calendar": {
          "__rl": true,
          "mode": "list",
          "value": "davide.boizza@gmail.com",
          "cachedResultName": "davide.boizza@gmail.com"
        },
        "operation": "update",
        "updateFields": {
          "end": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('End', `The new end date and time. This should be exactly 30 minutes after the start time.`, 'string') }}",
          "start": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Start', `The new start date and time for the appointment in ISO 8601 format (e.g., 2026-02-17T16:00:00).`, 'string') }}"
        },
        "descriptionType": "manual",
        "toolDescription": "When updating, always provide an end time 30 minutes after the start time unless specified otherwise."
      },
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "9f76daed-9757-4886-b3d5-890b456fcbe9",
      "name": "Delete event",
      "type": "n8n-nodes-base.googleCalendarTool",
      "position": [
        592,
        704
      ],
      "parameters": {
        "eventId": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Event_ID', `The unique ID string from the search result `, 'string') }}",
        "options": {},
        "calendar": {
          "__rl": true,
          "mode": "list",
          "value": "davide.boizza@gmail.com",
          "cachedResultName": "davide.boizza@gmail.com"
        },
        "operation": "delete"
      },
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "9096fdfc-4787-4598-a6f5-a477f7ace15b",
      "name": "Create event",
      "type": "n8n-nodes-base.googleCalendarTool",
      "position": [
        720,
        864
      ],
      "parameters": {
        "end": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('End', ``, 'string') }}",
        "start": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Start', ``, 'string') }}",
        "calendar": {
          "__rl": true,
          "mode": "list",
          "value": "davide.boizza@gmail.com",
          "cachedResultName": "davide.boizza@gmail.com"
        },
        "descriptionType": "manual",
        "toolDescription": "Use this tool to create an event - Add the user's name and the service",
        "additionalFields": {
          "summary": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Summary', `Title: Client's name - Service`, 'string') }}",
          "description": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Description', ``, 'string') }}"
        }
      },
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "f6316b89-d476-4001-9c23-f5a7e618293d",
      "name": "Get events",
      "type": "n8n-nodes-base.googleCalendarTool",
      "position": [
        592,
        848
      ],
      "parameters": {
        "options": {
          "timeZone": {
            "__rl": true,
            "mode": "list",
            "value": "Europe/Madrid",
            "cachedResultName": "Europe/Madrid"
          }
        },
        "timeMax": "={{ $now.plus({ days: 30 }).toFormat('yyyy-MM-dd') }}",
        "calendar": {
          "__rl": true,
          "mode": "list",
          "value": "davide.boizza@gmail.com",
          "cachedResultName": "davide.boizza@gmail.com"
        },
        "operation": "getAll",
        "returnAll": true,
        "descriptionType": "manual",
        "toolDescription": "=Use this tool to see if a specific slot is available. If this tool returns data, it means the slot is unavailable/busy. Only look for slots after {{ $now }}."
      },
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "8285ba3a-5dd8-4185-ab1f-32edb374309b",
      "name": "Add client",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        448,
        704
      ],
      "parameters": {
        "columns": {
          "value": {
            "Client": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Client', ``, 'string') }}",
            "Service": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Service', ``, 'string') }}",
            "Phone Number": "={{ $('Normalize').item.json.sessionId }}"
          },
          "schema": [
            {
              "id": "Phone Number",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Phone Number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Client",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Client",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Service",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Service",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Phone Number"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1DM24yhANppmCu92D3St6GpMh2He7L2feizajN0aLDrU/edit#gid=0",
          "cachedResultName": "Foglio1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1DM24yhANppmCu92D3St6GpMh2He7L2feizajN0aLDrU",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1DM24yhANppmCu92D3St6GpMh2He7L2feizajN0aLDrU/edit?usp=drivesdk",
          "cachedResultName": "Appointments"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "25c8ddac-69ac-4ea9-90ec-65e7070154d5",
      "name": "Workflow Error Handler",
      "type": "n8n-nodes-base.errorTrigger",
      "position": [
        -2160,
        112
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "fc9ffa55-6a99-4ed4-af4b-d12fa4e9e277",
      "name": "Notify: Workflow Error",
      "type": "n8n-nodes-base.gmail",
      "position": [
        -1056,
        112
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "=<h2>\ud83d\udd34 Workflow Error</h2><p>The LinkedIn PDF \u2192 JobAdder Sync workflow encountered an error:</p><pre style='background:#f5f5f5;padding:12px'>{{ $json.execution.error.message }}</pre><p><strong>Last node:</strong> {{ $json.execution.lastNodeExecuted }}</p><hr><em>Check your n8n execution log for full details.</em>",
        "options": {},
        "subject": "=\ud83d\udd34 Workflow Error: LinkedIn PDF \u2192 JobAdder Sync"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "87cc75dc-c025-42e8-bb43-7257a7542054",
      "name": "Virtual Assistant",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        272,
        432
      ],
      "parameters": {
        "text": "={{ $json.guardrailsInput }}",
        "options": {
          "systemMessage": "=# OVERVIEW\nYou are Emma from \"Cuts & Styles\", a virtual assistant. Your priority is to call the correct tools in the right order.\n\n# TOOLS\n- New client - Use this tool to check if the client exists in the system\n- Add client - Use this tool to add the client details to the system\n- Get events - Use this tool to search for events or available slots in the calendar\n- Create event - Use this tool to create a new event (check chat_history for user's details)\n- Update event - Use this tool if there's an existing event that needs to be rescheduled\n- Delete event - Use this tool if there's an existing event that needs to be cancelled/deleted\n- Escalation - Use this tool if the user is requesting anything else other than booking, rescheduling, or cancelling an appointment.\n\n# TASKS:\n1- Identify the user (mandatory every time)\n2- Check for existing appointments\n3- Book, reschedule, or delete appointments\n\n# INSTRUCTIONS FOR EACH TASK\n\n## IDENTIFY USER\n1. **ALWAYS** call `New client?` first to identify the user\n  - If the user doesn't exist yet, ask for their full name and hair type/preferences, then call `Add client`. Check chat_history if the user has already provided any of those details before asking.\n  - If the user exists, continue to step 2 (Check for existing appointments)\n\n## BOOKING INSTRUCTIONS:\n- **Always** call `Get events` before replying. Send the first 2 or 3 **available** slots to the user.\n- If the user is unavailable on those slot suggestions, provide 2-3 more available slots.\n- Once the user agrees to a specific slot, call `Create event`.\n- Confirm the date/time to the user.\n\n## RESCHEDULE INSTRUCTIONS:\n- Call `Get events` and search the appointment titles until you find the user's existing booking (using the user's full name). **NEVER** skip this step. If the user is rescheduling, you **must** find their existing appointment. The user might provide the wrong time/day \u2014 always verify the correct information.\n- Based on the information from `Get events`, provide 2-3 different available slots to the user. **Only suggest slots that are free**.\n- Once the user confirms a new day/time, call `Update event` and make the changes. **Make sure to update the correct event by double-checking the title contains the user's name.**\n\n## CANCEL/DELETE INSTRUCTIONS:\n- **DO NOT** reply to the user before calling `Get events`.\n- Call `Get events`, search for the user's existing appointment using their full name, and once found call `Delete event`. Delete **ONLY** the user's appointment. If there is more than one, ask the user to confirm which one to delete.\n\n## OTHER REQUESTS INSTRUCTIONS:\n- If the user is asking for anything other than booking, rescheduling, or cancelling an appointment, let them know you will inform the team and that someone will reach out soon. Don't ask for confirmation \u2014 **always** use the `Send a message in Gmail` tool to notify the team immediately.\n\n# DATE LOGIC\n- Today is {{ $now.toFormat('dd/LL/yyyy') }} at {{ $now.toFormat('HH:mm') }}.\n- If the user asks for a day of the week (e.g., \"Thursday\"):\n  1. Calculate the specific date based on today.\n  2. If today is Wednesday Feb 18, \"Thursday\" MUST be Feb 19.\n  3. Confirm the date (YYYY-MM-DD) before calling the `Create event` tool.\n\n## Additional info\n- Hairstylist's name is Alex Carter.\n- Salon address is 1533 Style Avenue\n\n# Examples:\n1.\nInput: Hi, I have an appointment soon but I won't make it. Can you move it to this afternoon, maybe at 4?\nOutput: Hi Sarah, no problem! The 4:00 PM slot is already taken, but I can offer you 4:30 PM or 5:00 PM instead. Which works better for you?\n\n2.\nOutput when an appointment has been changed: Done! Your appointment has been moved to today at 5:00 PM. See you soon!\n\n3.\nInput: Hi, I need an appointment\nOutput: Hi, I'm Emma from Cuts & Styles \u2014 lovely to meet you! I don't see you in our system yet, but I can get you set up in seconds. Could you share your full name and tell me a bit about your hair (length, type, any preferences)?\nInput: Sure! I'm Jessica Brown and I have long curly hair\nOutput: Perfect, you're all set! I have a few slots available today: 11:30 AM, 12:00 PM, or 12:30 PM. Which works for you? And what service are you looking for today?\n\n4.\nInput: Hi, I need an appointment\nOutput: Hi [user's name], great to hear from you! Here are a few available slots: [Available slots]. Which works for you? And what are we doing for you today \u2014 cut, color, something else?\n\n5.\nOutput: Done! Your blowout appointment is confirmed for today at 10:30 AM with Alex. See you soon! \ud83d\udc87 Let me know if you need anything else.\nInput: Thanks!\nOutput: You're welcome \u2014 see you soon!\n\n6.\nInput: Hi, I have an appointment today at 3 PM that I'd like to reschedule\nOutput: Hi [user's name], I found your appointment \u2014 it's actually booked at 12:30 PM, not 3:00 PM. Would you still like to reschedule it? If so, what day works best for you?"
        },
        "promptType": "define"
      },
      "retryOnFail": true,
      "typeVersion": 3.1
    },
    {
      "id": "84fa4817-92d9-4968-a4cb-9047c1f79bfa",
      "name": "WhatsApp Trigger",
      "type": "n8n-nodes-base.whatsAppTrigger",
      "position": [
        -2176,
        992
      ],
      "parameters": {
        "options": {},
        "updates": [
          "messages"
        ]
      },
      "credentials": {
        "whatsAppTriggerApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "1983a56e-4ab6-4091-b324-25a22398ef16",
      "name": "Download Audio",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1504,
        992
      ],
      "parameters": {
        "url": "={{ $json.url }}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "typeVersion": 4.2
    },
    {
      "id": "07b0371d-57d9-4753-ad9f-8ec1b94d2a28",
      "name": "Transcribe Audio",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        -1280,
        992
      ],
      "parameters": {
        "options": {},
        "resource": "audio",
        "operation": "transcribe"
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "d4d264be-f8a3-4938-b1e9-30e2531b174a",
      "name": "Fix mimeType for Audio",
      "type": "n8n-nodes-base.code",
      "position": [
        2912,
        64
      ],
      "parameters": {
        "jsCode": "for (const item of $input.all()) {\n  if (item.binary) {\n    const binaryPropertyNames = Object.keys(item.binary);\n    for (const propName of binaryPropertyNames) {\n      if (item.binary[propName].mimeType === 'audio/mp3') {\n        item.binary[propName].mimeType = 'audio/mpeg';\n      }\n    }\n  }\n}\n\nreturn $input.all();"
      },
      "typeVersion": 2
    },
    {
      "id": "9bfbcdf4-1ef4-487e-b289-5a74f790e45e",
      "name": "Send message",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        2688,
        256
      ],
      "parameters": {
        "textBody": "={{ $json.output }}",
        "operation": "send",
        "phoneNumberId": "470271332838881",
        "additionalFields": {},
        "recipientPhoneNumber": "={{ $('WhatsApp Trigger').item.json.messages[0].from }}"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "2bedd51f-60a0-4a8e-96d7-e2a4c298b7b4",
      "name": "Send audio",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        3136,
        64
      ],
      "parameters": {
        "mediaPath": "useMedian8n",
        "operation": "send",
        "messageType": "audio",
        "phoneNumberId": "470271332838881",
        "additionalFields": {},
        "recipientPhoneNumber": "={{ $('Input type').item.json.contacts[0].wa_id }}"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "1c7ea9ac-90f7-4095-be8c-b0a3d9904925",
      "name": "Audio",
      "type": "n8n-nodes-base.set",
      "position": [
        -1056,
        992
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "219577d5-b028-48fc-90be-980f4171ab68",
              "name": "message",
              "type": "string",
              "value": "={{ $json.text }}"
            },
            {
              "id": "3eb09778-f47e-4881-bd16-48d9dbfe3132",
              "name": "sessionId",
              "type": "string",
              "value": "={{ $('WhatsApp Trigger').item.json.messages[0].from }}"
            },
            {
              "id": "94082eb6-8031-4101-9795-2423d3dc6560",
              "name": "type",
              "type": "string",
              "value": "whatsapp"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "a4e3d012-43fe-4a0b-8b4a-c10f7f184304",
      "name": "Not supported",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        -1728,
        1184
      ],
      "parameters": {
        "textBody": "=You can only send text messages, images, audio files and PDF documents.",
        "operation": "send",
        "phoneNumberId": "470271332838881",
        "additionalFields": {},
        "recipientPhoneNumber": "={{ $('WhatsApp Trigger').item.json.messages[0].from }}"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "424fe518-bc50-4b86-9c76-2715826e9248",
      "name": "Get Audio Url",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        -1728,
        992
      ],
      "parameters": {
        "resource": "media",
        "operation": "mediaUrlGet",
        "mediaGetId": "={{ $('WhatsApp Trigger').item.json.messages[0].audio.id }}"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "f5347cd5-9c13-4f76-a857-3875d956b886",
      "name": "Generate Audio Response",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        2688,
        64
      ],
      "parameters": {
        "input": "={{ $('AI Agent1').item.json.output }}",
        "voice": "onyx",
        "options": {},
        "resource": "audio"
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "61b74351-ea96-48f1-90f8-8529dd220667",
      "name": "From audio to audio?",
      "type": "n8n-nodes-base.if",
      "position": [
        2288,
        176
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "b9d1d759-f585-4791-a743-b9d72951e77c",
              "operator": {
                "type": "object",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $('WhatsApp Trigger').item.json.messages[0].audio }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "7b2865c2-a532-4f99-ad5a-a1eae456f0ba",
      "name": "Input type",
      "type": "n8n-nodes-base.switch",
      "position": [
        -1952,
        976
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Text",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "08fd0c80-307e-4f45-b1de-35192ee4ec5e",
                    "operator": {
                      "type": "string",
                      "operation": "exists",
                      "singleValue": true
                    },
                    "leftValue": "={{ $json.messages[0].text.body }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Voice",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "b7b64446-f1ea-4622-990c-22f3999a8269",
                    "operator": {
                      "type": "object",
                      "operation": "exists",
                      "singleValue": true
                    },
                    "leftValue": "={{ $json.messages[0].audio }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra"
        }
      },
      "typeVersion": 3.2
    },
    {
      "id": "d78be069-d111-4cc3-abda-8f69faebe232",
      "name": "Calculator",
      "type": "@n8n/n8n-nodes-langchain.toolCalculator",
      "position": [
        912,
        896
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "98bf7e36-9fd9-4e35-bdaa-90749ccfc7ff",
      "name": "Send Email",
      "type": "n8n-nodes-base.gmailTool",
      "position": [
        1296,
        1152
      ],
      "parameters": {
        "sendTo": "xxx@xxx.com",
        "message": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Message', ``, 'string') }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "Escaletion ",
        "descriptionType": "manual",
        "toolDescription": "Use this tool to send a message and notify the team when a user asks for something different than booking, rescheduling or canceling an appointment."
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "cf918a88-9029-488a-9a3d-fd28ebc45c0a",
      "name": "Escalation",
      "type": "n8n-nodes-base.whatsAppHitlTool",
      "position": [
        1360,
        912
      ],
      "parameters": {
        "message": "=The agent wants to call {{ $tool.name }}\n\n{{ \n  $tool.parameters.keys()\n    .map(\n      param => \n       `${param}: ${$tool.parameters[param]}   \\n`\n    )\n    .join('')\n}}",
        "options": {},
        "approvalOptions": {
          "values": {
            "approvalType": "double"
          }
        },
        "recipientPhoneNumber": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Recipient_s_Phone_Number', ``, 'string') }}"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "4bfdb216-a855-4ea0-a472-d3051525d199",
      "name": "New client",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        448,
        848
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1DM24yhANppmCu92D3St6GpMh2He7L2feizajN0aLDrU/edit#gid=0",
          "cachedResultName": "Foglio1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1DM24yhANppmCu92D3St6GpMh2He7L2feizajN0aLDrU",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1DM24yhANppmCu92D3St6GpMh2He7L2feizajN0aLDrU/edit?usp=drivesdk",
          "cachedResultName": "Appointments"
        },
        "descriptionType": "manual",
        "toolDescription": "Get row(s) in sheet in Google Sheets"
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "18e664b8-032c-48ba-89a6-e57f7404b5bb",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        192,
        704
      ],
      "parameters": {
        "options": {},
        "modelName": "models/gemini-3.1-flash-lite-preview"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ba2c4744-1188-4843-9998-9daf945380fd",
      "name": "Guardrails",
      "type": "@n8n/n8n-nodes-langchain.guardrails",
      "position": [
        -464,
        768
      ],
      "parameters": {
        "text": "={{ $json.message }}",
        "guardrails": {
          "jailbreak": {
            "value": {
              "threshold": 0.7,
              "customizePrompt": true
            }
          }
        },
        "customizeSystemMessage": true
      },
      "typeVersion": 2
    },
    {
      "id": "73b085f8-a9cf-4303-b57b-cc8c330db2e5",
      "name": "Google Gemini Chat Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -528,
        1024
      ],
      "parameters": {
        "options": {},
        "modelName": "models/gemini-2.5-flash-lite"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "96c7f79b-d17b-44b7-b626-e3d917514b38",
      "name": "Send message1",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        -176,
        1120
      ],
      "parameters": {
        "textBody": "=What was communicated does not reflect our policies or is not relevant.",
        "operation": "send",
        "phoneNumberId": "470271332838881",
        "additionalFields": {},
        "recipientPhoneNumber": "={{ $('WhatsApp Trigger').item.json.messages[0].from }}"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "24bc71fb-c49a-468a-8665-707aff2a08b2",
      "name": "When chat message received",
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "position": [
        -2160,
        480
      ],
      "parameters": {
        "options": {
          "responseMode": "responseNodes"
        }
      },
      "typeVersion": 1.4
    },
    {
      "id": "a7f4878a-e5be-4b64-81f0-d0f9d432cca0",
      "name": "Postgres Chat Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryPostgresChat",
      "position": [
        320,
        704
      ],
      "parameters": {
        "sessionKey": "={{ $('Normalize').item.json.sessionId }}",
        "sessionIdType": "customKey"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "e91c8d86-3fc8-406a-9c2b-fd0d2656b8bd",
      "name": "Normalize",
      "type": "n8n-nodes-base.set",
      "position": [
        -736,
        768
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "125c9831-791b-4b6e-979b-ceda1d77ad7a",
              "name": "message",
              "type": "string",
              "value": "={{ $json.message }}"
            },
            {
              "id": "a802bf83-f88a-4eb1-b4bd-5b253a15e2cb",
              "name": "sessionId",
              "type": "string",
              "value": "={{ $json.sessionId }}"
            },
            {
              "id": "3014000a-962e-449c-93a9-99fe74a1a21e",
              "name": "type",
              "type": "string",
              "value": "chat"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "24f137f3-973b-4be8-a811-ab889f516c55",
      "name": "Switch",
      "type": "n8n-nodes-base.switch",
      "position": [
        1872,
        432
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "WhatsApp",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "05f0da8d-ac5d-4bea-b691-9f2468efa664",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Normalize').item.json.type }}",
                    "rightValue": "whatsapp"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Chat",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "93f83728-df15-4304-9b95-3ca321cb1abb",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Normalize').item.json.type }}",
                    "rightValue": "chat"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "839acb59-e122-4f64-aa6b-d24bcbb9d3d1",
      "name": "Escalation1",
      "type": "@n8n/n8n-nodes-langchain.chatHitlTool",
      "position": [
        1088,
        912
      ],
      "parameters": {
        "message": "=The agent wants to call {{ $tool.name }}\n\n{{ \n  $tool.parameters.keys()\n    .map(\n      param => \n       `${param}: ${$tool.parameters[param]}   \\n`\n    )\n    .join('')\n}}",
        "options": {
          "limitWaitTime": {
            "values": {
              "resumeUnit": "minutes",
              "resumeAmount": 45
            }
          }
        },
        "blockUserInput": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Block_User_Input', ``, 'boolean') }}",
        "approvalOptions": {
          "values": {
            "approvalType": "double"
          }
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "c474e2dd-44c7-477d-8b66-24d196723a4d",
      "name": "Chat",
      "type": "@n8n/n8n-nodes-langchain.chat",
      "position": [
        2272,
        640
      ],
      "parameters": {
        "message": "={{$json.output}}",
        "options": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "77313555-74a2-452e-86a5-8580627314dd",
      "name": "Chat1",
      "type": "@n8n/n8n-nodes-langchain.chat",
      "position": [
        -176,
        944
      ],
      "parameters": {
        "message": "=What was communicated does not reflect our policies or is not relevant.",
        "options": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "ba8be3a0-deb5-4452-9c04-5cabbdcaacc2",
      "name": "Message",
      "type": "n8n-nodes-base.set",
      "position": [
        -1056,
        800
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "c53cd9f9-77c1-4331-98ff-bfc9bdf95a3c",
              "name": "message",
              "type": "string",
              "value": "={{ $('WhatsApp Trigger').item.json.messages[0].text.body }}"
            },
            {
              "id": "87dbdb41-66ba-4c02-a441-a6b44777dcbd",
              "name": "sessionId",
              "type": "string",
              "value": "={{ $('WhatsApp Trigger').item.json.messages[0].from }}"
            },
            {
              "id": "6e097caf-b3d4-4a17-8f63-57aa735e12ac",
              "name": "type",
              "type": "string",
              "value": "whatsapp"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "47783407-0d84-4172-9e19-589f3b1e9a61",
      "name": "From Chat",
      "type": "n8n-nodes-base.set",
      "position": [
        -1056,
        480
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "c53cd9f9-77c1-4331-98ff-bfc9bdf95a3c",
              "name": "message",
              "type": "string",
              "value": "={{ $json.chatInput }}"
            },
            {
              "id": "e355708a-2353-46c9-9423-9e15ea731955",
              "name": "sessionId",
              "type": "string",
              "value": "={{ $json.sessionId }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "f7e49b4c-5e99-424c-8429-7904e1c0520f",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2256,
        320
      ],
      "parameters": {
        "color": 7,
        "width": 1392,
        "height": 368,
        "content": "## STEP 1 - Chat Input\nMessage from chatbot"
      },
      "typeVersion": 1
    },
    {
      "id": "ec999d01-a788-43f5-92c1-58414b37697b",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2256,
        720
      ],
      "parameters": {
        "color": 7,
        "width": 1392,
        "height": 656,
        "content": "## STEP 1 bis - WhatsApp Input\nMessage (text or audio with speech to text) from WhatsApp"
      },
      "typeVersion": 1
    },
    {
      "id": "44f25d5d-3373-4007-b6e3-78c8c00f565c",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -832,
        320
      ],
      "parameters": {
        "color": 7,
        "width": 880,
        "height": 1056,
        "content": "## STEP 2  - Guardrails\n"
      },
      "typeVersion": 1
    },
    {
      "id": "0c46b4c3-a6b6-4127-8163-3b8fec3d6bc1",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        96,
        320
      ],
      "parameters": {
        "color": 7,
        "width": 944,
        "height": 1056,
        "content": "## STEP 3  - AI Agent\n[Clone this sheet](https://docs.google.com/spreadsheets/d/1DM24yhANppmCu92D3St6GpMh2He7L2feizajN0aLDrU/edit?usp=sharing) for example of simple database"
      },
      "typeVersion": 1
    },
    {
      "id": "d59bae7b-3ead-4218-808b-c20ae85cb857",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1072,
        784
      ],
      "parameters": {
        "color": 7,
        "width": 592,
        "height": 592,
        "content": "## STEP 3 bis - Escalation\nEscalation with Chatbot or WhatsApp (select one)"
      },
      "typeVersion": 1
    },
    {
      "id": "a4e9a4c5-197c-4162-a3c4-af42e5fbadac",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2144,
        -16
      ],
      "parameters": {
        "color": 7,
        "width": 1264,
        "height": 496,
        "content": "## STEP 4 - WhatsApp Response"
      },
      "typeVersion": 1
    },
    {
      "id": "5ec9105b-012c-4dfa-a04c-7296691f97ec",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2144,
        512
      ],
      "parameters": {
        "color": 7,
        "width": 1264,
        "height": 336,
        "content": "## STEP 4 bis - Chatbot Response\n"
      },
      "typeVersion": 1
    },
    {
      "id": "d096f221-ab1c-41bb-b287-4d192eb77d7b",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2256,
        -656
      ],
      "parameters": {
        "width": 1392,
        "height": 560,
        "content": "## AI-Powered WhatsApp Chatbot\nThis workflow implements an **AI-powered WhatsApp booking assistant** for a hair salon. The system allows customers to **book, reschedule, or cancel appointments automatically via text or voice messages on WhatsApp**.\n\nThe workflow supports **both text and voice messages**. \n\n### How it works\n\nThis workflow acts as an **AI-powered WhatsApp booking assistant** that processes both **text and voice messages**. Incoming messages from WhatsApp or a chat interface are captured, and voice notes are automatically downloaded and transcribed using **OpenAI Whisper**. The message then passes through a **Guardrails layer (Google Gemini)** that filters unsafe or policy-violating inputs before reaching the AI agent.\n\nThe **AI Agent (\u201cEmma\u201d)** interprets the user\u2019s intent and decides which tools to call. It checks or creates clients in **Google Sheets**, manages appointments in **Google Calendar** (check availability, create, update, cancel), and escalates unsupported requests via **Gmail or human-in-the-loop channels**. Conversation history is stored in **PostgreSQL** for context. Responses are sent back through WhatsApp as **text or synthesized voice (TTS)** depending on the original message type.\n\n### Setup steps\n\nTo deploy the workflow, configure integrations and update key resources used by the automation. First, connect required **credentials** including Google Calendar, Google Sheets, Gmail, WhatsApp Business API, OpenAI/OpenRouter for transcription and TTS, Google Gemini for AI processing and guardrails, and a **Postgres database** for conversation memory.\n\nNext, prepare **Google Sheets** as a lightweight CRM by creating a sheet with columns such as *Phone Number*, *Client*, and *Service*, then insert its **Document ID** in the relevant nodes. Verify the correct **Google Calendar ID** for appointment management. Configure the **WhatsApp Trigger and messaging nodes** with your Phone Number ID, update the escalation **email recipient**, and review the **AI prompts** to customize salon details, assistant behavior, and guardrail thresholds before activating the workflow.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "d8b42d4f-0e37-4b7d-98e1-87af9d4a4be1",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2256,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 1392,
        "height": 336,
        "content": "## STEP 5 - Error"
      },
      "typeVersion": 1
    },
    {
      "id": "fabd4439-a9b1-4d7c-9242-30486c2c1363",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -816,
        -656
      ],
      "parameters": {
        "color": 7,
        "width": 736,
        "height": 736,
        "content": "## MY NEW YOUTUBE CHANNEL\n\ud83d\udc49 [Subscribe to my new **YouTube channel**](https://youtube.com/@n3witalia). Here I\u2019ll share videos and Shorts with practical tutorials and **FREE templates for n8n**.\n\n[![image](https://n3wstorage.b-cdn.net/n3witalia/youtube-n8n-cover.jpg)](https://youtube.com/@n3witalia)"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "30e3114d-dee1-43cb-bf17-5c20f8d09a5a",
  "connections": {
    "Audio": {
      "main": [
        [
          {
            "node": "Normalize",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch": {
      "main": [
        [
          {
            "node": "From audio to audio?",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Chat",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Message": {
      "main": [
        [
          {
            "node": "Normalize",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "From Chat": {
      "main": [
        [
          {
            "node": "Normalize",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize": {
      "main": [
        [
          {
            "node": "Guardrails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add client": {
      "ai_tool": [
        [
          {
            "node": "Virtual Assistant",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Calculator": {
      "ai_tool": [
        [
          {
            "node": "Virtual Assistant",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Escalation": {
      "ai_tool": [
        []
      ]
    },
    "Get events": {
      "ai_tool": [
        [
          {
            "node": "Virtual Assistant",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Guardrails": {
      "main": [
        [
          {
            "node": "Virtual Assistant",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Chat1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Input type": {
      "main": [
        [
          {
            "node": "Message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Get Audio Url",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Not supported",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "New client": {
      "ai_tool": [
        [
          {
            "node": "Virtual Assistant",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Send Email": {
      "ai_tool": [
        [
          {
            "node": "Escalation",
            "type": "ai_tool",
            "index": 0
          },
          {
            "node": "Escalation1",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Escalation1": {
      "ai_tool": [
        [
          {
            "node": "Virtual Assistant",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Create event": {
      "ai_tool": [
        [
          {
            "node": "Virtual Assistant",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Delete event": {
      "ai_tool": [
        [
          {
            "node": "Virtual Assistant",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Update event": {
      "ai_tool": [
        [
          {
            "node": "Virtual Assistant",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Get Audio Url": {
      "main": [
        [
          {
            "node": "Download Audio",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Audio": {
      "main": [
        [
          {
            "node": "Transcribe Audio",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Transcribe Audio": {
      "main": [
        [
          {
            "node": "Audio",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "WhatsApp Trigger": {
      "main": [
        [
          {
            "node": "Input type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Virtual Assistant": {
      "main": [
        [
          {
            "node": "Switch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "From audio to audio?": {
      "main": [
        [
          {
            "node": "Generate Audio Response",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Postgres Chat Memory": {
      "ai_memory": [
        [
          {
            "node": "Virtual Assistant",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Fix mimeType for Audio": {
      "main": [
        [
          {
            "node": "Send audio",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Workflow Error Handler": {
      "main": [
        [
          {
            "node": "Notify: Workflow Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Audio Response": {
      "main": [
        [
          {
            "node": "Fix mimeType for Audio",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Virtual Assistant",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "Guardrails",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "When chat message received": {
      "main": [
        [
          {
            "node": "From Chat",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow implements an AI-powered WhatsApp booking assistant for a hair salon. The system allows customers to book, reschedule, or cancel appointments automatically via text or voice messages on WhatsApp.

Source: https://n8n.io/workflows/13984/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Transform your salon/service business with this streamlined WhatsApp automation system featuring Claude integration, zero-setup database management, and intelligent conversation handling. Claude MCP I

WhatsApp Trigger, WhatsApp, Redis +11
AI & RAG

This workflow creates a complete AI-powered restaurant ordering system through WhatsApp. It receives customer messages, processes multimedia content (text, voice, images, PDFs, location), uses GPT-4 t

OpenAI Chat, Memory Postgres Chat, HTTP Request +6
AI & RAG

This template is designed for anyone who wants to use WhatsApp as a personal AI assistant hub. If you often juggle tasks, emails, calendars, and expenses across multiple tools, this workflow consolida

OpenAI Chat, Memory Buffer Window, Mcp Client Tool +12
AI & RAG

⚠️ EDUCATIONAL DEMONSTRATION ONLY - NOT FOR PRODUCTION MEDICAL USE ⚠️

Agent, Memory Postgres Chat, OpenAI Chat +7
AI & RAG

📺 Full walkthrough video: https://youtu.be/-kpt0BwjKls

HTTP Request, OpenAI, WhatsApp +11