This workflow corresponds to n8n.io template #9383 — we link there as the canonical source.
This workflow follows the Agent → Google Calendar Tool 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 →
{
"meta": {
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "0fa7eed1-0e6d-4370-bb11-2500e93c2ef0",
"name": "JotForm Trigger",
"type": "n8n-nodes-base.jotFormTrigger",
"position": [
-1024,
256
],
"parameters": {
"form": "252801824783057"
},
"credentials": {
"jotFormApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "0c107533-b8d0-4dc8-a073-7138a7288146",
"name": "Append or update row in sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
-800,
256
],
"parameters": {
"columns": {
"value": {
"Full Name": "={{ $json['Full Name'].first }} {{ $json['Full Name'].last }}",
"client type": "={{ $json['I am a...'] }}",
"Phone Number": "={{ $json['Phone Number'].full }}",
"Brief Message": "={{ $json['Brief Message'] }}",
"Email Address": "={{ $json['Email Address'] }}",
"Legal Service of Interest": "={{ $json['Legal Service of Interest'] }}",
"How Did You Hear About Us?": "={{ $json['How Did You Hear About Us?'] }}"
},
"schema": [
{
"id": "Full Name",
"type": "string",
"display": true,
"required": false,
"displayName": "Full Name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Email Address",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Email Address",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Phone Number",
"type": "string",
"display": true,
"required": false,
"displayName": "Phone Number",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "client type",
"type": "string",
"display": true,
"required": false,
"displayName": "client type",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Legal Service of Interest",
"type": "string",
"display": true,
"required": false,
"displayName": "Legal Service of Interest",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Brief Message",
"type": "string",
"display": true,
"required": false,
"displayName": "Brief Message",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "How Did You Hear About Us?",
"type": "string",
"display": true,
"required": false,
"displayName": "How Did You Hear About Us?",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"Email Address"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "appendOrUpdate",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1invngp2z_3ZMe_Qcs5XioDkAs50DSXT-Pl4ibPLXyA0/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1invngp2z_3ZMe_Qcs5XioDkAs50DSXT-Pl4ibPLXyA0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1invngp2z_3ZMe_Qcs5XioDkAs50DSXT-Pl4ibPLXyA0/edit?usp=drivesdk",
"cachedResultName": "Law Client Enquiries"
},
"authentication": "serviceAccount"
},
"credentials": {
"googleApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "69795336-f1ce-4ce8-b7fc-4379418cb83e",
"name": "AI Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-528,
256
],
"parameters": {
"text": "=<prompt>\n <persona>\n <name>Alex</name>\n <role>Expert, friendly, and efficient legal intake assistant</role>\n <company>VTR Law Firm</company>\n </persona>\n\n <client_info>\n <name>{{ $json[\"Full Name\"] }}</name>\n <service_interest>{{ $json[\"Legal Service of Interest\"] }}</service_interest>\n <message>{{ $json[\"Brief Message\"] }}</message>\n </client_info>\n\n <task>\n Draft a concise, professional, and welcoming WhatsApp message to the potential client using their submitted information.\n </task>\n\n <instructions>\n <message_structure>\n <item>Greet the client by their first name.</item>\n <item>Thank them for contacting VTR Law Firm regarding their interest in the specified service.</item>\n <item>Briefly acknowledge their message to show you've read it.</item>\n <item>Inform them that the next step is a *complimentary consultation* to discuss their matter in more detail.</item>\n <item>Provide a clear call to action to schedule the meeting using this link: https://calendly.com/vtr-law-firm/consultation</item>\n <item>Sign off professionally with your name and the firm's name.</item>\n </message_structure>\n </instructions>\n\n <output_constraints>\n <rule>The output must be formatted as a single, average-sized WhatsApp message.</rule>\n <rule>Use asterisks (*) for bolding key phrases.</rule>\n <rule>Do not include the client's phone number or email address in your response.</rule>\n <rule>Generate only the WhatsApp message text itself.</rule>\n </output_constraints>\n\n <example>\n <description>Based on the instructions and sample data, the generated message should look like this.</description>\n <output_message>\nHi Abhi,\n\nThank you for contacting VTR Law Firm about your query related to Business Law. I've reviewed the message you sent regarding the situation with your partner.\n\nThe best next step is to schedule a *complimentary, no-obligation consultation* with one of our legal experts to discuss this in more detail.\n\nYou can schedule a call with our legal executive by replying your available times\n\nWe look forward to speaking with you.\n\nBest regards,\nAlex | VTR Law Firm\n </output_message>\n </example>\n</prompt>",
"options": {},
"promptType": "define"
},
"typeVersion": 2.2
},
{
"id": "95a55ce6-3967-4a6b-ba11-57ba9c9d0998",
"name": "Google Gemini Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
-528,
528
],
"parameters": {
"options": {}
},
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "b06b2984-c342-4db7-a46b-06679b0080db",
"name": "WhatsApp Trigger",
"type": "n8n-nodes-base.whatsAppTrigger",
"position": [
240,
320
],
"parameters": {
"options": {
"messageStatusUpdates": [
"delivered"
]
},
"updates": [
"messages"
]
},
"credentials": {
"whatsAppTriggerApi": {
"name": "<your credential>"
}
},
"executeOnce": true,
"typeVersion": 1
},
{
"id": "a715deb0-e11f-46df-b4ae-1cb4c214c183",
"name": "If",
"type": "n8n-nodes-base.if",
"position": [
496,
320
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c2380501-f59a-4075-b335-8ae3b7f64788",
"operator": {
"type": "string",
"operation": "empty",
"singleValue": true
},
"leftValue": "={{ $json.messages[0].text.body }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "e157f8f8-f9da-4f03-baa3-114b199d36e0",
"name": "GET MANY EVENTS OF DAY THE USER ASKED",
"type": "n8n-nodes-base.googleCalendarTool",
"position": [
976,
544
],
"parameters": {
"options": {},
"timeMax": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Before', ``, 'string') }}",
"timeMin": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('After', ``, 'string') }}",
"calendar": {
"__rl": true,
"mode": "list",
"value": "user@example.com",
"cachedResultName": "user@example.com"
},
"operation": "getAll",
"returnAll": true
},
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "a4b80f3b-caf8-40c7-b787-c090b5010755",
"name": "Create an event",
"type": "n8n-nodes-base.googleCalendarTool",
"position": [
1136,
544
],
"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": "user@example.com",
"cachedResultName": "user@example.com"
},
"additionalFields": {
"summary": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Summary', ``, 'string') }}",
"attendees": [
"={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('attendees0_Attendees', ``, 'string') }}"
],
"description": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Description', ``, 'string') }}",
"sendUpdates": "all",
"guestsCanInviteOthers": true
}
},
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "937a57e5-4e9e-4302-99a7-e38ff770e453",
"name": "Google Gemini Chat Model1",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
656,
544
],
"parameters": {
"options": {}
},
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "ed687b20-55af-41a3-b815-0044bf238692",
"name": "AI Agent1",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
912,
336
],
"parameters": {
"text": "=<prompt>\n <persona>\n <name>Alex</name>\n <role>Professional and friendly legal scheduling assistant</role>\n <company>VTR Law Associates</company>\n </persona>\n\n <context>\n <timestamp>\n The current date and time is {{ $now.toFormat(\"DDDD, HH:mm:ss ZZZZ\") }}. You must use this information only to correctly interpret relative time requests from the user (like \"tomorrow,\" \"next Tuesday,\" or \"in two hours\").\n </timestamp>\n </context>\n\n <instructions>\n <task>\n When a user wants to schedule a legal consultation, follow these steps precisely. Use the provided tools and context to answer the user's question.\n </task>\n \n <procedure>\n <step n=\"1\">\n <description>Find User Details: First, use the 'Know about the user enquiry' tool to find the user's requirement details, such as their email address and the reason for their legal enquiry.</description>\n <tool>Know about the user enquiry</tool>\n </step>\n \n <step n=\"2\">\n <description>Gather Missing Details: Ask the user for their preferred date and time for the consultation. If you could not find the user's email address in the previous step, politely ask for it now for the calendar invitation.</description>\n </step>\n \n <step n=\"3\">\n <description>Check Availability: Use the 'GET MANY EVENTS OF DAY THE USER ASKED' tool to check for existing events on the user's requested date.</description>\n <tool>GET MANY EVENTS OF DAY THE USER ASKED</tool>\n </step>\n \n <step n=\"4\">\n <description>Handle the Outcome based on availability:</description>\n <condition case=\"time available\">\n <action>Use the 'Create an event' tool. Set the event title to \"Legal Consultation - VTR Law Associates\" and add the user's email as an attendee.</action>\n <action>Confirm with the user that the consultation is successfully booked.</action>\n <tool>Create an event</tool>\n </condition>\n <condition case=\"time unavailable\">\n <action>Do not create an event.</action>\n <action>Inform the user that the requested time is booked and suggest specific alternative times based on the availability you found.</action>\n </condition>\n </step>\n </procedure>\n \n <general_instruction>Handle greetings naturally without needing the context.</general_instruction>\n </instructions>\n\n <user_input>\n <message>{{ $json.messages[0].text.body }}</message>\n </user_input>\n\n <output_format>\n <style>WhatsApp</style>\n <rules>\n <rule>Use italics for emphasis.</rule>\n <rule>Use bold for key points.</rule>\n <rule>Use bullet lists (\u2022) for lists.</rule>\n <rule>Keep responses short, clear, and conversational.</rule>\n <rule>Avoid markdown headers or code blocks.</rule>\n </rules>\n </output_format>\n\n <constraints>\n <fallback>\n If the answer cannot be found in the context, reply: \"I'm sorry, my primary role is to schedule consultations. I don't have the information to answer that question.\"\n </fallback>\n </constraints>\n</prompt>",
"options": {},
"promptType": "define"
},
"typeVersion": 2.2
},
{
"id": "6cda0580-9e50-4406-a794-2d9315498f0e",
"name": "Postgres Chat Memory",
"type": "@n8n/n8n-nodes-langchain.memoryPostgresChat",
"position": [
816,
544
],
"parameters": {
"sessionKey": "={{ $('If').item.json.contacts[0].wa_id }}",
"sessionIdType": "customKey"
},
"credentials": {
"postgres": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "4891245b-9402-44b6-8632-d6f1053a8f54",
"name": "Know about the user enquiry",
"type": "n8n-nodes-base.googleSheetsTool",
"position": [
1296,
544
],
"parameters": {
"options": {},
"filtersUI": {
"values": [
{
"lookupColumn": "Phone Number"
}
]
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1invngp2z_3ZMe_Qcs5XioDkAs50DSXT-Pl4ibPLXyA0/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1invngp2z_3ZMe_Qcs5XioDkAs50DSXT-Pl4ibPLXyA0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1invngp2z_3ZMe_Qcs5XioDkAs50DSXT-Pl4ibPLXyA0/edit?usp=drivesdk",
"cachedResultName": "Law Client Enquiries"
},
"authentication": "serviceAccount"
},
"credentials": {
"googleApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "3021dcef-b9ef-4a87-abe6-992081b72600",
"name": "Send message",
"type": "n8n-nodes-base.whatsApp",
"position": [
-144,
256
],
"parameters": {
"textBody": "={{ $json.output }}",
"operation": "send",
"phoneNumberId": "838262432698376",
"additionalFields": {},
"recipientPhoneNumber": "={{ $('Append or update row in sheet').item.json['Phone Number'] }}"
},
"credentials": {
"whatsAppApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "6aad2ba5-1451-4150-b28a-f760061e8341",
"name": "Send message1",
"type": "n8n-nodes-base.whatsApp",
"position": [
1424,
336
],
"parameters": {
"textBody": "={{ $json.output }}",
"operation": "send",
"phoneNumberId": "838262432698376",
"additionalFields": {},
"recipientPhoneNumber": "={{ $('WhatsApp Trigger').item.json.contacts[0].wa_id }}"
},
"credentials": {
"whatsAppApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "b90257f8-e8c0-4c63-9713-6052a9785d8f",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1072,
-352
],
"parameters": {
"width": 1136,
"height": 1056,
"content": "## Part A: New Lead Intake & Welcome Message\n\nThis flow triggers when a potential client fills out your JotForm. It saves their data and sends an immediate welcome message.\n\n* **1. JotForm Trigger** \ud83d\udce5\n * **Function**: This node is the starting point. It actively listens for new submissions on your specific JotForm. When someone hits 'submit', this node wakes up and grabs all the information they entered.\n\n* **2. Append or update row in sheet** \ud83d\udcdd\n * **Function**: This node acts as your record-keeper. It takes the data from the JotForm and adds it as a new row in your \"Law Client Enquiries\" Google Sheet. If an entry with the same email already exists, it simply updates that row with the new information.\n\n* **3. AI Agent** \ud83e\udd16\n * **Function**: This is the message writer. It takes the client's name and service of interest from the previous step and uses a pre-written prompt to craft a personalized, professional welcome message.\n\n* **4. Google Gemini Chat Model** \ud83e\udde0\n * **Function**: This is the \"brain\" that powers the AI Agent. The agent sends its prompt and the client's info to this model, and Gemini generates the actual text for the WhatsApp message.\n\n* **5. Send message (WhatsApp)** \ud83d\udcf2\n * **Function**: This node sends the final message. It takes the text generated by the AI and delivers it as a WhatsApp message to the phone number the client provided in the form.\n\n***\n\n"
},
"typeVersion": 1
},
{
"id": "b7acce1b-992f-42bc-b9f7-4ee7670b412e",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
192,
-336
],
"parameters": {
"width": 1456,
"height": 1056,
"content": "\n## Part B: AI-Powered Appointment Scheduling\n\nThis flow activates when the client replies to the welcome message. An AI agent then chats with them to book a consultation.\n\n* **1. WhatsApp Trigger** \ud83d\udcac\n * **Function**: This node listens for incoming WhatsApp messages. When a client replies to your business number, it kicks off the scheduling conversation.\n\n* **2. If** \ud83e\udd14\n * **Function**: A simple checkpoint. It checks if the incoming message has text. If it's an empty message or just a delivery receipt, the workflow stops. If there is text, it proceeds.\n\n* **3. AI Agent1** \ud83d\udc69\u200d\ud83d\udcbc\n * **Function**: This is your AI scheduling assistant, \"Alex\". It manages the back-and-forth conversation with the client to find a suitable meeting time. It uses a set of \"tools\" to perform actions like checking your calendar.\n\n* **4. Postgres Chat Memory** \ud83d\udcbe\n * **Function**: This node gives the AI a memory. It saves the conversation history for each client, so if the chat is long, the AI can remember what was discussed previously.\n\n* **5. The AI's Tools (What it can do)** \ud83d\udee0\ufe0f\n * **Know about the user enquiry (Sheets Tool)**: Allows the AI to look up the client's original submission details from the Google Sheet using their phone number.\n * **GET MANY EVENTS... (Calendar Tool)**: Allows the AI to check your Google Calendar for existing appointments on a date the client requests.\n * **Create an event (Calendar Tool)**: Allows the AI to book the appointment directly on your Google Calendar once a time is confirmed.\n\n* **6. Send message1 (WhatsApp)** \ud83d\udde3\ufe0f\n * **Function**: This node sends the AI's conversational replies back to the client. This could be a confirmation (\"You're all booked!\"), a question (\"Is 3 PM tomorrow okay?\"), or a suggestion for an alternative time."
},
"typeVersion": 1
}
],
"connections": {
"If": {
"main": [
[],
[
{
"node": "AI Agent1",
"type": "main",
"index": 0
}
]
]
},
"AI Agent": {
"main": [
[
{
"node": "Send message",
"type": "main",
"index": 0
}
]
]
},
"AI Agent1": {
"main": [
[
{
"node": "Send message1",
"type": "main",
"index": 0
}
]
]
},
"Send message": {
"main": [
[]
]
},
"Create an event": {
"ai_tool": [
[
{
"node": "AI Agent1",
"type": "ai_tool",
"index": 0
}
]
]
},
"JotForm Trigger": {
"main": [
[
{
"node": "Append or update row in sheet",
"type": "main",
"index": 0
}
]
]
},
"WhatsApp Trigger": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
},
"Postgres Chat Memory": {
"ai_memory": [
[
{
"node": "AI Agent1",
"type": "ai_memory",
"index": 0
}
]
]
},
"Google Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Google Gemini Chat Model1": {
"ai_languageModel": [
[
{
"node": "AI Agent1",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Know about the user enquiry": {
"ai_tool": [
[
{
"node": "AI Agent1",
"type": "ai_tool",
"index": 0
}
]
]
},
"Append or update row in sheet": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"GET MANY EVENTS OF DAY THE USER ASKED": {
"ai_tool": [
[
{
"node": "AI Agent1",
"type": "ai_tool",
"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.
googleApigoogleCalendarOAuth2ApigooglePalmApijotFormApipostgreswhatsAppApiwhatsAppTriggerApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Youtube Explanation: https://youtu.be/KgmNiV7SwkU
Source: https://n8n.io/workflows/9383/ — 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.
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
This template is designed for businesses that provide customer support and appointment-based services over WhatsApp. It’s ideal for service providers (e.g., clinics, salons, repair shops, consultants)
Turn your WhatsApp chats into an AI-powered meeting scheduler with Google Gemini, Google Calendar, and Google Sheets. This workflow understands natural language like “Book a meeting with Ali at 3 PM t
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