This workflow corresponds to n8n.io template #17500 — we link there as the canonical source.
This workflow follows the Chainllm → Google Sheets 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 →
{
"id": "ZEonfoiotFTgbtYZ",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Check vendor availability and send quotes from WhatsApp",
"tags": [],
"nodes": [
{
"id": "609ba31a-cec1-4ad0-a4aa-b20e1e772098",
"name": "Template Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
-512,
720
],
"parameters": {
"color": 4,
"width": 700,
"height": 1088,
"content": "## \ud83d\udcc5 Check Vendor Availability and Send Quotes from WhatsApp\n\n## Who's it for\nEvent and wedding vendors (photographers, caterers, decorators, venues) who get date inquiries over WhatsApp and want the calendar checked and a price quoted automatically, instead of manually checking their calendar and pricing sheet for every single message.\n\n## How it works\nWhen a customer asks about a date, AI extracts the event date, type, guest count, and their name. In parallel, the workflow checks Google Calendar for existing bookings on that date and looks up your pricing sheet for a matching rate. Both results are combined, and if the date is free, the customer gets an instant quote with your deposit terms. If it's already booked, they get a message letting them know and asking for an alternate date instead of being left waiting. Every inquiry is logged either way.\n\n## How to set up\n1. Connect your WhatsApp Business Cloud credentials to the trigger and both send nodes.\n2. Connect your Groq or Gemini API credentials to the Chat Model node.\n3. Connect your Google Calendar credentials and note your calendar ID.\n4. Connect your Google Sheets credentials and create a Pricing sheet with the columns described in the sticky note near the Lookup Pricing node.\n5. Connect your Supabase credentials and create the table described near the Log Inquiry nodes.\n6. Open the Set Configuration node and fill in your calendar ID, Sheet ID, WhatsApp phone number ID, vendor name, and deposit percentage.\n7. Activate the workflow.\n\n## Requirements\n- WhatsApp Business Cloud API access\n- Groq or Gemini API key\n- Google Calendar account\n- Google Sheets account\n- Supabase project\n\n## How to customize\nEdit the pricing sheet's tiers to match how you actually price by guest count or event type. Edit the quote and unavailable-date message templates to match your brand's tone."
},
"typeVersion": 1
},
{
"id": "2b4f610e-b4e2-43fa-89c9-5de767deaf72",
"name": "Section - Trigger and Setup",
"type": "n8n-nodes-base.stickyNote",
"position": [
208,
1152
],
"parameters": {
"color": 7,
"width": 428,
"height": 300,
"content": "## 1\ufe0f\u20e3 2\ufe0f\u20e3 Trigger + Configuration\nFires on every incoming WhatsApp message."
},
"typeVersion": 1
},
{
"id": "fc7f10ed-65a7-439d-88f2-7b62984deb5c",
"name": "Section - AI Extraction",
"type": "n8n-nodes-base.stickyNote",
"position": [
672,
1120
],
"parameters": {
"color": 7,
"width": 428,
"height": 524,
"content": "## 3\ufe0f\u20e3 AI Extraction\nPulls the event date, type, and guest count from the message. The event_date must come out as an ISO date (YYYY-MM-DD) for the calendar check to work."
},
"typeVersion": 1
},
{
"id": "2405207c-51e6-4a14-959d-34efaa4e2aa2",
"name": "Section - Parallel Lookups",
"type": "n8n-nodes-base.stickyNote",
"position": [
1168,
1072
],
"parameters": {
"color": 7,
"width": 916,
"height": 828,
"content": "## 4\ufe0f\u20e3 Two Independent Lookups, Then Merge\nCalendar availability and pricing are checked in parallel, then combined into one item with the Merge node before deciding anything."
},
"typeVersion": 1
},
{
"id": "d1bf3796-0d0b-4b27-8584-d5fdbb327129",
"name": "Section - Decide and Respond",
"type": "n8n-nodes-base.stickyNote",
"position": [
2112,
1088
],
"parameters": {
"color": 7,
"width": 900,
"height": 600,
"content": "## 5\ufe0f\u20e3 6\ufe0f\u20e3 Decide + Respond\nIf the date has zero calendar events, it's free. Matches pricing by event type and guest count. Branches into a quote or an unavailable message, either way the inquiry gets logged."
},
"typeVersion": 1
},
{
"id": "eb0821b4-f0de-4440-bcc8-72abff71fcb7",
"name": "Pricing Sheet Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
1184,
1696
],
"parameters": {
"color": 3,
"width": 300,
"content": "\u26a0\ufe0f Pricing sheet schema\nPricing: event_type, max_guests, price. The Code node picks the first row where event_type matches and guest_count fits under max_guests, add rows for every tier you offer."
},
"typeVersion": 1
},
{
"id": "12923c5c-1d75-46f0-8507-37be2f418c4c",
"name": "New Vendor Inquiry",
"type": "n8n-nodes-base.whatsAppTrigger",
"position": [
240,
1296
],
"parameters": {
"options": {},
"updates": [
"messages"
]
},
"credentials": {
"whatsAppTriggerApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "115606e0-a6b1-4b40-9c12-ea7378434fac",
"name": "Set Configuration",
"type": "n8n-nodes-base.set",
"position": [
432,
1296
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "cfg-1",
"name": "vendor_name",
"type": "string",
"value": "REPLACE_WITH_VENDOR_NAME"
},
{
"id": "cfg-2",
"name": "whatsapp_phone_number_id",
"type": "string",
"value": "REPLACE_WITH_WHATSAPP_PHONE_NUMBER_ID"
},
{
"id": "cfg-3",
"name": "calendar_id",
"type": "string",
"value": "REPLACE_WITH_GOOGLE_CALENDAR_ID"
},
{
"id": "cfg-4",
"name": "sheet_id",
"type": "string",
"value": "REPLACE_WITH_GOOGLE_SHEET_ID"
},
{
"id": "cfg-5",
"name": "pricing_sheet_name",
"type": "string",
"value": "Pricing"
},
{
"id": "cfg-6",
"name": "supabase_table_inquiries",
"type": "string",
"value": "vendor_inquiries"
},
{
"id": "cfg-7",
"name": "deposit_percent",
"type": "number",
"value": 30
},
{
"id": "cfg-8",
"name": "customer_message",
"type": "string",
"value": "={{ $json.messages[0].text.body }}"
},
{
"id": "cfg-9",
"name": "customer_number",
"type": "string",
"value": "={{ $json.messages[0].from }}"
},
{
"id": "cfg-10",
"name": "customer_name",
"type": "string",
"value": "={{ $json.contacts[0].profile.name }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "05d1a1f4-ad35-456a-b5dd-c8c5ee1c0eb0",
"name": "Extract Inquiry Details",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
736,
1296
],
"parameters": {
"text": "=Extract event inquiry details from this WhatsApp message. Today's date is {{ $now.toFormat('yyyy-MM-dd') }}.\n\nMessage: {{ $('Set Configuration').item.json.customer_message }}\n\nExtract: event_date (as YYYY-MM-DD, resolve relative dates like \"next Saturday\" using today's date), event_type (wedding, birthday, corporate, or other), and guest_count (a number, estimate if a range is given).",
"batching": {},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 1.8
},
{
"id": "116be620-79c2-433f-bf33-e9539b8f1e0c",
"name": "Extraction Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGroq",
"position": [
736,
1488
],
"parameters": {
"options": {}
},
"credentials": {
"groqApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "024d27fc-1677-41ce-b670-d50cc69acd4b",
"name": "Extraction Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
880,
1504
],
"parameters": {
"schemaType": "manual",
"inputSchema": "{\n \"type\": \"object\",\n \"properties\": {\n \"event_date\": {\n \"type\": \"string\"\n },\n \"event_type\": {\n \"type\": \"string\"\n },\n \"guest_count\": {\n \"type\": \"number\"\n }\n },\n \"required\": [\n \"event_date\",\n \"event_type\",\n \"guest_count\"\n ]\n}"
},
"typeVersion": 1.2
},
{
"id": "92ac14c2-051c-44d1-ac6d-05aba3eb56e0",
"name": "Check Calendar Availability",
"type": "n8n-nodes-base.googleCalendar",
"position": [
1216,
1232
],
"parameters": {
"options": {},
"timeMax": "={{ $('Extract Inquiry Details').item.json.output.event_date }}T23:59:59",
"timeMin": "={{ $('Extract Inquiry Details').item.json.output.event_date }}T00:00:00",
"calendar": {
"__rl": true,
"mode": "id",
"value": "={{ $('Set Configuration').item.json.calendar_id }}"
},
"operation": "getAll"
},
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "5d997731-642e-46e2-aa77-49e029041017",
"name": "Aggregate Calendar Events",
"type": "n8n-nodes-base.aggregate",
"position": [
1456,
1232
],
"parameters": {
"options": {},
"aggregate": "aggregateAllItemData",
"destinationFieldName": "calendar_events"
},
"typeVersion": 1
},
{
"id": "27d2f576-5621-42dc-a848-f87dd838b1b1",
"name": "Lookup Pricing",
"type": "n8n-nodes-base.googleSheets",
"position": [
1216,
1520
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $('Set Configuration').item.json.pricing_sheet_name }}"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Set Configuration').item.json.sheet_id }}"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.5
},
{
"id": "d1262ef0-ffa2-41ff-981c-180a8a64f163",
"name": "Aggregate Pricing Rows",
"type": "n8n-nodes-base.aggregate",
"position": [
1456,
1520
],
"parameters": {
"options": {},
"aggregate": "aggregateAllItemData",
"destinationFieldName": "pricing_rows"
},
"typeVersion": 1
},
{
"id": "318aed00-3976-4e38-9eb2-1d959b4bd325",
"name": "Merge Availability and Pricing",
"type": "n8n-nodes-base.merge",
"position": [
1808,
1360
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineByPosition"
},
"typeVersion": 3.1
},
{
"id": "68f26ee4-3c06-4e0b-bff7-df1f6ed6ec52",
"name": "Determine Availability and Price",
"type": "n8n-nodes-base.code",
"position": [
2144,
1360
],
"parameters": {
"jsCode": "const merged = $json;\nconst inquiry = $('Extract Inquiry Details').first().json.output;\n\nconst isAvailable = (merged.calendar_events || []).length === 0;\n\nconst pricingRows = merged.pricing_rows || [];\nconst match = pricingRows.find(r =>\n String(r.event_type).toLowerCase().trim() === String(inquiry.event_type).toLowerCase().trim() &&\n inquiry.guest_count <= Number(r.max_guests)\n);\n\nconst price = match ? Number(match.price) : null;\nconst depositPercent = $('Set Configuration').first().json.deposit_percent;\nconst depositAmount = price ? Math.round(price * depositPercent / 100) : null;\n\nreturn [{\n json: {\n is_available: isAvailable,\n event_date: inquiry.event_date,\n event_type: inquiry.event_type,\n guest_count: inquiry.guest_count,\n matched_price: price,\n deposit_amount: depositAmount\n }\n}];"
},
"typeVersion": 2
},
{
"id": "acd6d09e-90f2-49fc-bc88-4d2dc5d6b115",
"name": "Is Available?",
"type": "n8n-nodes-base.if",
"position": [
2304,
1360
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.is_available }}",
"rightValue": true
}
]
}
},
"typeVersion": 2.2
},
{
"id": "34435f1a-cf29-44d1-961b-144e29751c34",
"name": "Send Quote",
"type": "n8n-nodes-base.whatsApp",
"position": [
2512,
1248
],
"parameters": {
"textBody": "=Good news, {{ $('Set Configuration').item.json.customer_name }}! {{ $json.event_date }} is available for your {{ $json.event_type }} ({{ $json.guest_count }} guests). Our quote is \u20b9{{ $json.matched_price }}, with a \u20b9{{ $json.deposit_amount }} deposit to confirm the booking.",
"operation": "send",
"phoneNumberId": "={{ $('Set Configuration').item.json.whatsapp_phone_number_id }}",
"additionalFields": {},
"recipientPhoneNumber": "={{ $('Set Configuration').item.json.customer_number }}"
},
"credentials": {
"whatsAppApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "f56d63f2-1824-455e-8de8-256ec583e79e",
"name": "Log Inquiry - Quoted",
"type": "n8n-nodes-base.supabase",
"position": [
2736,
1248
],
"parameters": {
"operation": "insert"
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "e87f6516-49d5-4d5c-bd0c-f4d6c33ca0f2",
"name": "Send Unavailable Message",
"type": "n8n-nodes-base.whatsApp",
"position": [
2496,
1520
],
"parameters": {
"textBody": "=Sorry {{ $('Set Configuration').item.json.customer_name }}, {{ $json.event_date }} is already booked. Do you have an alternate date in mind? Happy to check availability for that instead.",
"operation": "send",
"phoneNumberId": "={{ $('Set Configuration').item.json.whatsapp_phone_number_id }}",
"additionalFields": {},
"recipientPhoneNumber": "={{ $('Set Configuration').item.json.customer_number }}"
},
"credentials": {
"whatsAppApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "ed43a0aa-e022-4a8a-b905-51900e08979f",
"name": "Log Inquiry - Date Conflict",
"type": "n8n-nodes-base.supabase",
"position": [
2736,
1520
],
"parameters": {
"operation": "insert"
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "503d08fe-ff2a-4dfb-a4e1-c8a592d45f51",
"nodeGroups": [],
"connections": {
"Send Quote": {
"main": [
[
{
"node": "Log Inquiry - Quoted",
"type": "main",
"index": 0
}
]
]
},
"Is Available?": {
"main": [
[
{
"node": "Send Quote",
"type": "main",
"index": 0
}
],
[
{
"node": "Send Unavailable Message",
"type": "main",
"index": 0
}
]
]
},
"Lookup Pricing": {
"main": [
[
{
"node": "Aggregate Pricing Rows",
"type": "main",
"index": 0
}
]
]
},
"Extraction Parser": {
"ai_outputParser": [
[
{
"node": "Extract Inquiry Details",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"Set Configuration": {
"main": [
[
{
"node": "Extract Inquiry Details",
"type": "main",
"index": 0
}
]
]
},
"New Vendor Inquiry": {
"main": [
[
{
"node": "Set Configuration",
"type": "main",
"index": 0
}
]
]
},
"Extraction Chat Model": {
"ai_languageModel": [
[
{
"node": "Extract Inquiry Details",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Aggregate Pricing Rows": {
"main": [
[
{
"node": "Merge Availability and Pricing",
"type": "main",
"index": 1
}
]
]
},
"Extract Inquiry Details": {
"main": [
[
{
"node": "Check Calendar Availability",
"type": "main",
"index": 0
},
{
"node": "Lookup Pricing",
"type": "main",
"index": 0
}
]
]
},
"Send Unavailable Message": {
"main": [
[
{
"node": "Log Inquiry - Date Conflict",
"type": "main",
"index": 0
}
]
]
},
"Aggregate Calendar Events": {
"main": [
[
{
"node": "Merge Availability and Pricing",
"type": "main",
"index": 0
}
]
]
},
"Check Calendar Availability": {
"main": [
[
{
"node": "Aggregate Calendar Events",
"type": "main",
"index": 0
}
]
]
},
"Merge Availability and Pricing": {
"main": [
[
{
"node": "Determine Availability and Price",
"type": "main",
"index": 0
}
]
]
},
"Determine Availability and Price": {
"main": [
[
{
"node": "Is Available?",
"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.
googleCalendarOAuth2ApigoogleSheetsOAuth2ApigroqApisupabaseApiwhatsAppApiwhatsAppTriggerApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow responds to incoming WhatsApp inquiries by extracting event details with Groq, checking availability in Google Calendar, looking up rates in Google Sheets, and then sending either a quote or a date-conflict reply, while logging each inquiry to Supabase. Triggers…
Source: https://n8n.io/workflows/17500/ — 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.
• Multi-Layer Message Analysis - Every customer interaction passes through three specialized AI classifiers: privacy detection (identifies sensitive data and security requirements), intent recognition
This workflow routes incoming WhatsApp home-service requests by using Groq to classify urgency and extract key details, logs every request to Supabase, and either alerts an on-call technician for emer
This n8n workflow automates the transformation of spreadsheet data into professional charts and graphs using AI-driven analysis. Triggered via Slack, it processes uploaded files (Excel, CSV, Google Sh
Handles GDPR Article 15 (access) and Article 17 (erasure) requests end-to-end — from inbound email to legally-compliant response — with zero manual intervention and a full audit trail. 📬 Monitors Gmai
This workflow automatically detects completed orders in PostgreSQL and prepares them for AI-based post-purchase communication. It enriches each order with customer, product, and payment data, then gen