This workflow corresponds to n8n.io template #15777 — we link there as the canonical source.
This workflow follows the Agent → Google Gemini Chat 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 →
{
"nodes": [
{
"id": "10d87045-73fb-4bcd-8360-d78bba09e6a7",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
0,
0
],
"parameters": {
"width": 480,
"height": 720,
"content": "## Automated Gohighlevel Appoinement booking system on Whatsapp with Gemini\n\n### How it works\n\n1. The workflow listens for incoming WhatsApp messages.\n2. It validates the sender to ensure authorized interaction.\n3. It retrieves relevant contact information from HighLevel.\n4. An AI agent processes the message using chat history and CRM tools.\n5. The final response is sent back to the customer via WhatsApp.\n\n### Setup steps\n\n- - [ ] Configure the WhatsApp Trigger with your credentials and webhook.\n- - [ ] Connect your GoHighLevel account to the HighLevel nodes.\n- - [ ] Set up the Google Gemini API credentials.\n- - [ ] Ensure Redis is configured for persistent chat memory.\n\n### Customization\n\nYou can swap the AI model in the 'Gemini Chat Model' sub-node or add additional tools to the AI Agent to expand its capabilities."
},
"typeVersion": 1
},
{
"id": "030e7724-1334-428f-8b00-0bc593162687",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
560,
48
],
"parameters": {
"color": 7,
"width": 416,
"height": 304,
"content": "## Trigger and validate message sender\n\nInitial trigger and validation of incoming WhatsApp messages."
},
"typeVersion": 1
},
{
"id": "d66a47ca-760a-42dc-8ef0-d54ba8a1d612",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1008,
48
],
"parameters": {
"color": 7,
"width": 1088,
"height": 512,
"content": "## Fetch contacts and run AI agent\n\nFetches customer data from HighLevel and processes the conversation."
},
"typeVersion": 1
},
{
"id": "8dc440be-1b95-407d-b3ff-313ff7f695f2",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
2128,
0
],
"parameters": {
"color": 7,
"height": 320,
"content": "## Send reply to customer\n\nSends the final generated response back to the user via WhatsApp."
},
"typeVersion": 1
},
{
"id": "526fc41c-a922-4e4a-a87e-a5cbdf77822c",
"name": "Customer Service Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
1360,
160
],
"parameters": {
"text": "=<context>\n <timestamp>\n Current date and time: {{ $now.toFormat(\"DDDD, HH:mm:ss ZZZZ\") }}\n Use this to correctly interpret relative time requests from the user (e.g., \"tomorrow morning,\" \"this Friday,\" \"in two hours\").\n </timestamp>\n \n <user_details>\n {{ $json.id ? \"Contact ID: \" + $json.id + \"\\nFirst Name: \" + $json.firstName + \"\\nLast Name: \" + $json.lastName + \"\\nEmail: \" + $json.email + \"\\nPhone Number: \" + $('When WhatsApp Message Received').item.json.messages[0].from : \"Contact ID: EMPTY\\nPhone Number: \" + $('When WhatsApp Message Received').item.json.messages[0].from + \"\\nMissing Info: Contact does not exist. You MUST ask the user for their Name and Email to create a contact profile.\" }}\n </user_details>\n</context>\n\n<persona>\n <name>Alex</name>\n <role>Professional and friendly HVAC service coordinator</role>\n <company>Blankarray HVAC Solutions</company>\n <tone>Warm, professional, and empathetic</tone>\n</persona>\n\n<instructions>\n <task>\n Handle HVAC service inquiries, collect customer information when needed, save their issues, and book appointments efficiently.\n </task>\n \n <workflow>\n <step n=\"1\">\n <name>Greet and Understand the Issue</name>\n <action>\n - Greet the user warmly\n - Ask about their HVAC issue or concern\n - Listen and acknowledge their problem with empathy\n - When they describe their issue, IMMEDIATELY use the \"Save user issue in notes\" tool to document it\n </action>\n </step>\n \n <step n=\"2\">\n <name>Check Contact Status</name>\n <condition case=\"Contact ID is EMPTY\">\n <priority>HIGH - Must create contact first</priority>\n <action>\n Inform the user you'll need some basic information to schedule their service.\n \n Collect the following (phone number already available, DO NOT ask for it):\n - Full Name (ask for their complete name)\n - Email address\n \n Once collected, use the \"Create or update a contact in HighLevel\" tool with:\n - Email: user's email\n - Phone: {{ $('When WhatsApp Message Received').item.json.messages[0].from }}\n - Address: User's address\n After contact creation, proceed to booking.\n </action>\n <tool>Create or update a contact in HighLevel</tool>\n </condition>\n \n <condition case=\"Contact ID exists\">\n <action>\n Address the user by their first name warmly.\n \n Since we already have their information on file (Name: First Name Last Name, Email: Email), skip information collection and proceed directly to scheduling.\n \n DO NOT ask for phone number - we already have it.\n </action>\n </condition>\n </step>\n \n <step n=\"3\">\n <name>Fetch Available Appointment Slots</name>\n <action>\n Ask when they'd prefer to schedule the service appointment.\n \n CRITICAL - ALWAYS fetch availability first using the \"Fetch Available Calendar Slots\" tool before suggesting times.\n \n To fetch slots:\n 1. Convert the user's requested date to Unix timestamps in milliseconds\n 2. Set Start_Date to the beginning of the day (00:00:00) in milliseconds\n 3. Set End_Date to the end of the day (23:59:59) in milliseconds\n \n Example for January 15, 2025:\n - Start_Date: 1736899200000 (Jan 15, 2025 00:00:00)\n - End_Date: 1736985599000 (Jan 15, 2025 23:59:59)\n \n After receiving available slots, present them in a friendly, easy-to-read format with specific times using bullet points.\n </action>\n <tool>Fetch Available Calendar Slots</tool>\n </step>\n \n <step n=\"4\">\n <name>Book the Appointment</name>\n <action>\n Once the user confirms a specific time slot, use the \"Book Calendar Appointment\" tool.\n \n CRITICAL - Format Start_Time parameter correctly:\n - Use ISO 8601 format with timezone offset\n - Format: \"YYYY-MM-DDTHH:mm:ss+05:30\" (for India timezone)\n - Example: \"2025-01-15T10:00:00+05:30\" for 10:00 AM on Jan 15, 2025\n - The system automatically adds 30 minutes for the end time\n \n After successful booking, confirm the appointment details clearly:\n - Date\n - Time\n - Service type (based on their issue)\n \n Thank them and let them know a technician will arrive at the scheduled time.\n </action>\n <tool>Book Calendar Appointment</tool>\n </step>\n </workflow>\n \n <general_guidelines>\n - Handle greetings naturally and warmly\n - Be empathetic toward users dealing with HVAC issues (heating, cooling, comfort problems)\n - Always remain professional and helpful\n - NEVER book an appointment without first fetching available slots\n - Keep responses conversational and friendly\n - Use WhatsApp formatting (bold, italics, bullets) for readability\n - Save user issues to notes as soon as they mention their problem\n - If Contact ID is EMPTY, creating the contact is your FIRST PRIORITY before booking\n - Never ask for phone number - it's already captured from WhatsApp\n </general_guidelines>\n</instructions>\n\n<user_input>\n <message>{{ $('When WhatsApp Message Received').item.json.messages[0].text.body }}</message>\n</user_input>\n\n<output_format>\n <style>WhatsApp</style>\n <rules>\n <rule>Use *bold* for key information like dates, times, and names</rule>\n <rule>Use _italics_ for emphasis on important points</rule>\n <rule>Use bullet lists (\u2022) for presenting multiple options</rule>\n <rule>Keep responses short, clear, and conversational</rule>\n <rule>Avoid markdown headers or code blocks</rule>\n <rule>Use line breaks for readability</rule>\n <rule>Use emojis sparingly (\u2705 for confirmations, \ud83d\udcc5 for dates, \ud83d\udd27 for service)</rule>\n </rules>\n</output_format>\n\n<constraints>\n <fallback>\n If asked about topics unrelated to HVAC services or appointment scheduling, politely redirect: \"I specialize in scheduling HVAC service appointments. For other inquiries, please contact our main office. How can I help you with your heating or cooling needs today?\"\n </fallback>\n</constraints>",
"options": {},
"promptType": "define"
},
"retryOnFail": true,
"typeVersion": 3.1
},
{
"id": "8c72cec0-cbf8-42ac-b979-fb9b0aeb0b61",
"name": "Gemini Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
1056,
400
],
"parameters": {
"options": {}
},
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.1
},
{
"id": "9cb94d49-0425-4a5a-a2fb-3b32ed61bd4f",
"name": "Redis Chat Memory",
"type": "@n8n/n8n-nodes-langchain.memoryRedisChat",
"position": [
1232,
400
],
"parameters": {
"sessionKey": "={{ $('When WhatsApp Message Received').first().json.messages[0].from }}",
"sessionTTL": 500000,
"sessionIdType": "customKey",
"contextWindowLength": 15
},
"credentials": {
"redis": {
"name": "<your credential>"
}
},
"typeVersion": 1.5
},
{
"id": "af2c4ba9-710b-4b54-a905-82d5feaa65b6",
"name": "Fetch Calendar Slots Tool",
"type": "n8n-nodes-base.highLevelTool",
"position": [
1392,
400
],
"parameters": {
"endDate": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('End_Date', ``, 'number') }}",
"resource": "calendar",
"operation": "getFreeSlots",
"startDate": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Start_Date', ``, 'number') }}",
"calendarId": "enter-your-gohighlevel-calenderid",
"requestOptions": {},
"descriptionType": "manual",
"toolDescription": "Name: Fetch Available Calendar Slots\nDescription: Retrieves available appointment slots for a specific date range. \n- Start_Date: Unix timestamp in milliseconds (e.g., 1735689600000 for Jan 1, 2025)\n- End_Date: Unix timestamp in milliseconds\nReturns: List of available 30-minute time slots",
"additionalFields": {}
},
"credentials": {
"highLevelOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2,
"rewireOutputLogTo": "ai_tool"
},
{
"id": "9806994f-9b1a-4d77-b2b4-30f84249ca28",
"name": "Book Appointment Tool",
"type": "n8n-nodes-base.highLevelTool",
"position": [
1584,
400
],
"parameters": {
"resource": "calendar",
"contactId": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Contact_ID', ``, 'string') }}",
"startTime": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Start_Time', ``, 'string') }}",
"calendarId": "enter-your-gohighlevel-calenderid",
"locationId": "enter-your-gohighlevel-locationid",
"requestOptions": {},
"descriptionType": "manual",
"toolDescription": "Name: Book Calendar Appointment\nDescription: Books an appointment at a specific time.\n\nStart_Time: ISO 8601 datetime string (e.g., \"2025-01-15T10:00:00-05:30\")\n\nContact_ID: The HighLevel Contact ID. You MUST provide this ID. If the contact already existed, pull it from your initial instructions context. If you just created the contact, use the new ID returned from the creation tool.",
"additionalFields": {}
},
"credentials": {
"highLevelOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "999f713f-b334-4b9d-89f0-fd6f621e5d5d",
"name": "Save User Issue Tool",
"type": "n8n-nodes-base.highLevelTool",
"position": [
1760,
416
],
"parameters": {
"email": "={{$json.email }}",
"phone": "=+{{ $('When WhatsApp Message Received').item.json.messages[0].from }}",
"requestOptions": {},
"descriptionType": "manual",
"toolDescription": "Name: Save user issue in notes\nDescription: Saves the user's HVAC issue description to their contact profile. Requires the 'Note' parameter containing a brief string summarizing the issue.",
"additionalFields": {
"notes": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Note', `you should add the issue over here in this field`, 'string') }}"
}
},
"credentials": {
"highLevelOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2,
"rewireOutputLogTo": "ai_tool"
},
{
"id": "9b9171d4-5348-4961-ab29-c1c7b94237f2",
"name": "Update Contact Tool",
"type": "n8n-nodes-base.highLevelTool",
"position": [
1968,
416
],
"parameters": {
"email": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Email', ``, 'string') }}",
"phone": "=+{{ $('When WhatsApp Message Received').item.json.messages[0].from }}",
"requestOptions": {},
"descriptionType": "manual",
"toolDescription": "Name: Create or update a contact in HighLevel\nDescription: Creates a new contact in the system. Use this when the user's Contact ID is empty. Requires the user's 'Email' address, user address & User name",
"additionalFields": {
"name": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Name', ``, 'string') }}",
"address1": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Address', ``, 'string') }}"
}
},
"credentials": {
"highLevelOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "669a28e1-d00c-4e0b-87df-cee4dd39ef9c",
"name": "Send WhatsApp Message",
"type": "n8n-nodes-base.whatsApp",
"position": [
2176,
160
],
"parameters": {
"textBody": "={{ $json.output }}",
"operation": "send",
"phoneNumberId": "1083481144853090",
"additionalFields": {},
"recipientPhoneNumber": "={{ $('When WhatsApp Message Received').first().json.messages[0].from }}"
},
"credentials": {
"whatsAppApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.1
},
{
"id": "b98f92d6-e827-453d-af46-4d84c167a5bd",
"name": "If Valid Sender Exists",
"type": "n8n-nodes-base.if",
"position": [
832,
176
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "11bbaeec-d3f0-4e49-a323-10fb6e13a513",
"operator": {
"type": "number",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json.messages[0].from }}",
"rightValue": ""
}
]
},
"looseTypeValidation": true
},
"typeVersion": 2.3
},
{
"id": "ead06c36-3256-4ac0-9c6f-fb0a04e469d8",
"name": "Fetch GHL Contacts",
"type": "n8n-nodes-base.highLevel",
"position": [
1104,
160
],
"parameters": {
"filters": {
"query": "=+{{ $json.messages[0].from }}"
},
"options": {},
"operation": "getAll",
"requestOptions": {}
},
"credentials": {
"highLevelOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2,
"alwaysOutputData": true
},
{
"id": "8bc9d747-4914-4fc2-9883-c7853cae5878",
"name": "When WhatsApp Message Received",
"type": "n8n-nodes-base.whatsAppTrigger",
"position": [
608,
176
],
"parameters": {
"options": {},
"updates": [
"messages"
]
},
"credentials": {
"whatsAppTriggerApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
}
],
"connections": {
"Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "Customer Service Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Redis Chat Memory": {
"ai_memory": [
[
{
"node": "Customer Service Agent",
"type": "ai_memory",
"index": 0
}
]
]
},
"Fetch GHL Contacts": {
"main": [
[
{
"node": "Customer Service Agent",
"type": "main",
"index": 0
}
]
]
},
"Update Contact Tool": {
"ai_tool": [
[
{
"node": "Customer Service Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Save User Issue Tool": {
"ai_tool": [
[
{
"node": "Customer Service Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Book Appointment Tool": {
"ai_tool": [
[
{
"node": "Customer Service Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Customer Service Agent": {
"main": [
[
{
"node": "Send WhatsApp Message",
"type": "main",
"index": 0
}
]
]
},
"If Valid Sender Exists": {
"main": [
[
{
"node": "Fetch GHL Contacts",
"type": "main",
"index": 0
}
]
]
},
"Fetch Calendar Slots Tool": {
"ai_tool": [
[
{
"node": "Customer Service Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"When WhatsApp Message Received": {
"main": [
[
{
"node": "If Valid Sender Exists",
"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.
googlePalmApihighLevelOAuth2ApirediswhatsAppApiwhatsAppTriggerApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Youtube Video For Workflow Explanation: https://youtu.be/3VTYQU7N6uU
Source: https://n8n.io/workflows/15777/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
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
This project is specifically built for an HVAC business to automatically upsell to old customers when their locality has a heatwave or snow wave forecast in the upcoming five days.
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 AI Assistant for Clinic Appointment Booking Automate your entire appointment lifecycle with an intelligent AI assistant that lives on WhatsApp. This workflow empowers any clinic or independen
Wait Splitout. Uses whatsAppTrigger, whatsApp, httpRequest, memoryBufferWindow. Event-driven trigger; 35 nodes.