This workflow corresponds to n8n.io template #16533 — we link there as the canonical source.
This workflow follows the Agent → Gmail 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": "WUtcuC4ub15paKJk",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "No-Show & Missed-Viewing Recovery Agent",
"tags": [],
"nodes": [
{
"id": "02372adc-966e-4eaa-863c-3041eeb80fdb",
"name": "Detect Missed Viewing",
"type": "n8n-nodes-base.googleCalendarTrigger",
"position": [
-336,
0
],
"parameters": {
"options": {},
"pollTimes": {
"item": [
{
"mode": "everyX",
"unit": "minutes",
"value": 15
}
]
},
"triggerOn": "eventUpdated",
"calendarId": {
"__rl": true,
"mode": "list",
"value": "user@example.com",
"cachedResultName": "QM-Viewings"
}
},
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "807e9ecf-7195-4fd8-8964-642658c574b2",
"name": "Filter: No-Show Only",
"type": "n8n-nodes-base.if",
"position": [
-80,
0
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "e9f72873-eedb-4f59-ba2a-903dad12a16c",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.summary.toLowerCase().includes(\"no-show\") }}",
"rightValue": true
}
]
}
},
"typeVersion": 2.3
},
{
"id": "fcb5db8a-c6c2-4fd1-883e-b7197189a9ea",
"name": "Extract Lead Details",
"type": "n8n-nodes-base.set",
"position": [
176,
-16
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "e2383e25-734a-457e-93a9-509ee0f8a719",
"name": "leadName",
"type": "string",
"value": "={{ $json.description.match(/Name:\\s*(.*?)\\s*\\|/)?.[1] ?? ''}}"
},
{
"id": "54a89f32-aee3-475c-aad7-04f1925cf4f6",
"name": "leadEmail",
"type": "string",
"value": "={{ $json.description.match(/Email:\\s*(.*?)\\s*\\|/)?.[1] ?? ''}}"
},
{
"id": "a0bb54d0-1d20-46cd-aa96-6050f55b28cb",
"name": "leadPhone",
"type": "string",
"value": "={{ $json.description.match(/Phone:\\s*(.*?)\\s*\\|/)?.[1] ?? ''}}"
},
{
"id": "f4d91109-3c25-420a-b703-8006c843295d",
"name": "propertyAddress",
"type": "string",
"value": "={{ $json.location }}"
},
{
"id": "c274c80b-fa1b-4c63-b3b3-09f603e6cd68",
"name": "viewingTime",
"type": "string",
"value": "={{ $json.start.dateTime }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "31a594de-77c0-48e5-abc9-628d2268f849",
"name": "Draft Re-Engagement Messages",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
384,
-80
],
"parameters": {
"text": "=Lead name: {{ $json.leadName }}\nProperty address: {{ $json.propertyAddress }}\nOriginally scheduled viewing time: {{ $json.viewingTime }}",
"options": {
"systemMessage": "You are a friendly, professional real estate assistant. Write a short, warm \nre-engagement message to a buyer lead who missed a scheduled property viewing. \nThe tone should be low-pressure \u2014 never guilt-trip the lead. Offer to reschedule.\n\nFormat the \"email_message\" value as two parts separated by a literal \"\\n\\n\":\n1. A short greeting line only, e.g. \"Hi {name},\"\n2. The message body as a single paragraph.\n\nThe \"whatsapp_message\" should remain one short casual paragraph, no greeting line needed.\n\nOutput ONLY valid JSON with two fields: \"email_message\" and \"whatsapp_message\"."
},
"promptType": "define"
},
"typeVersion": 3.1
},
{
"id": "80fbe295-baaa-4ecc-b47c-19b070dd18ac",
"name": "Google Gemini Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
384,
96
],
"parameters": {
"options": {}
},
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.1
},
{
"id": "20ed36d5-4cf2-4f51-b82f-4b8e288cdaea",
"name": "Has WhatsApp Number?",
"type": "n8n-nodes-base.if",
"position": [
960,
-192
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "61b54542-a5b4-46ef-a68a-c58f00894ff3",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $('Extract Lead Details').item.json.leadPhone }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.3
},
{
"id": "6369044d-1b78-47bb-b4c5-7034f8364ddf",
"name": "Send Re-Engagement WhatsApp",
"type": "n8n-nodes-base.whatsApp",
"position": [
1200,
-208
],
"parameters": {
"textBody": "={{ $json.whatsapp_message }}",
"operation": "send",
"additionalFields": {},
"recipientPhoneNumber": "={{ $json.leadPhone }}"
},
"typeVersion": 1.1,
"alwaysOutputData": false
},
{
"id": "08dde01f-7180-4dd1-8b5f-59b8f938d683",
"name": "Send Re-Engagement Email",
"type": "n8n-nodes-base.gmail",
"position": [
960,
48
],
"parameters": {
"sendTo": "={{ $('Extract Lead Details').item.json.leadEmail }}",
"message": "=<p>{{ $json.email_message }}</p>",
"options": {
"appendAttribution": false
},
"subject": "=Following up on your viewing at {{ $('Extract Lead Details').item.json.propertyAddress }}"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "a873603a-335f-42cb-a3a5-cedb1f4ce0ec",
"name": "Parse AI Message Output",
"type": "n8n-nodes-base.code",
"position": [
720,
-80
],
"parameters": {
"jsCode": "function escapeHtml(text) {\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>');\n}\n\nfunction formatEmailHtml(text) {\n // Split on the intentional double-newline (greeting vs. body)\n const paragraphs = text\n .split(/\\n\\s*\\n/)\n .map(p => p.replace(/\\s+/g, ' ').trim()) // clean up accidental wraps within each paragraph\n .filter(Boolean);\n return paragraphs.map(p => `<p>${escapeHtml(p)}</p>`).join('');\n}\n\nconst raw = $input.first().json.output;\nlet parsed;\ntry {\n parsed = JSON.parse(raw);\n} catch (e) {\n parsed = {\n email_message: `Hi {{name}},\\n\\nWe missed you at the viewing today \u2014 would you like to reschedule? Just reply to this email.`,\n whatsapp_message: `Hi! Sorry we missed each other today \u2014 want to reschedule?`\n };\n}\n\nconst leadData = $('Extract Lead Details').first().json;\nconst emailHtml = formatEmailHtml(\n parsed.email_message.includes('\\n\\n')\n ? parsed.email_message\n : parsed.email_message.replace(leadData.leadName, `${leadData.leadName},\\n\\n`) // fallback if AI forgets the break\n);\nconst cleanWhatsapp = parsed.whatsapp_message.replace(/\\s+/g, ' ').trim();\n\nreturn [{\n json: {\n ...leadData,\n email_message: emailHtml,\n whatsapp_message: cleanWhatsapp,\n recordId: `${leadData.leadEmail}-${Date.now()}`\n }\n}];"
},
"typeVersion": 2
},
{
"id": "14739c9d-820e-4933-b724-6a3f5b0ca2ab",
"name": "Log Follow-Up Outcome",
"type": "n8n-nodes-base.googleSheets",
"position": [
1264,
48
],
"parameters": {
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "",
"cachedResultUrl": "",
"cachedResultName": ""
},
"documentId": {
"__rl": true,
"mode": "url",
"value": ""
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "99012bb5-6ad9-4704-9773-323300f29f92",
"name": "Update WhatsApp sent status",
"type": "n8n-nodes-base.googleSheets",
"position": [
1440,
-208
],
"parameters": {
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "",
"cachedResultUrl": "",
"cachedResultName": ""
},
"documentId": {
"__rl": true,
"mode": "url",
"value": ""
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "0b0e1d78-e7fd-4b6e-8b76-b87470a63715",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-960,
-320
],
"parameters": {
"width": 496,
"height": 592,
"content": "## No-Show & Missed-Viewing Recovery Agent\n\nWhen a buyer misses a scheduled property viewing, this workflow automatically re-engages them \u2014 by **email** right away, and by **WhatsApp** too if a phone number is on file. No more lost leads because nobody had time to follow up.\n\n### How it works\nA Google Calendar event tagged as a no-show triggers the workflow. Lead details are pulled from the event, AI drafts a short, warm re-engagement message, email is sent immediately, and WhatsApp sends as a bonus touch if a phone number exists. Every outcome is logged to Google Sheets.\n\n### Setup\n1. Connect **Google Calendar**, **Google Sheets**, and **Gmail** credentials.\n2. Add your **Gemini API key**.\n3. Copy the *Viewing Recovery Log* sheet template and link it to the Append/Update nodes.\n4. **WhatsApp is optional** \u2014 no credentials yet? Disable that branch; email still sends normally.\n5. Point the Calendar node to your viewings calendar.\n\n### Customization\nEdit the AI prompt to change tone or language. Add more channels (e.g. SMS) by copying the WhatsApp branch pattern."
},
"typeVersion": 1
},
{
"id": "8c873847-de00-43dc-96b9-4ead1658da32",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-416,
-240
],
"parameters": {
"color": "#FFFFFF",
"height": 416,
"content": "## Detect Missed Viewing\nWatches your calendar and only continues if the event is tagged \"no-show.\""
},
"typeVersion": 1
},
{
"id": "4a072e0e-00a6-466e-b102-25fa79aef0bc",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
96,
-240
],
"parameters": {
"color": "#FFFFFF",
"width": 224,
"height": 416,
"content": "## Extract Lead Details\nPulls name, email, phone, property, and viewing time from the event."
},
"typeVersion": 1
},
{
"id": "dc90b59f-cb5f-4fb0-a322-3e9b91f6427f",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
336,
-304
],
"parameters": {
"color": "#FFFFFF",
"width": 560,
"height": 560,
"content": "## Draft & Clean AI Messages\nAI writes the email and WhatsApp messages, then formatting is cleaned up."
},
"typeVersion": 1
},
{
"id": "045bf2c4-50d6-4145-8fa3-b9448fadc5de",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
912,
-304
],
"parameters": {
"color": "#FFFFFF",
"width": 688,
"height": 560,
"content": "## Email, WhatsApp & Log\nSends email always, WhatsApp if possible, and logs every outcome to Sheets."
},
"typeVersion": 1
},
{
"id": "a00d4394-563c-4cfa-b918-eaeb94ed72f7",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-160,
-240
],
"parameters": {
"color": 3,
"height": 416,
"content": "## Requires Tagging\nThis step only works if your calendar events are reliably tagged as\n\"no-show\" (e.g. in the event title). "
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "ba1c9cbe-2d14-429e-a73b-1f2aded5685e",
"connections": {
"Extract Lead Details": {
"main": [
[
{
"node": "Draft Re-Engagement Messages",
"type": "main",
"index": 0
}
]
]
},
"Filter: No-Show Only": {
"main": [
[
{
"node": "Extract Lead Details",
"type": "main",
"index": 0
}
]
]
},
"Has WhatsApp Number?": {
"main": [
[
{
"node": "Send Re-Engagement WhatsApp",
"type": "main",
"index": 0
}
],
[]
]
},
"Detect Missed Viewing": {
"main": [
[
{
"node": "Filter: No-Show Only",
"type": "main",
"index": 0
}
]
]
},
"Parse AI Message Output": {
"main": [
[
{
"node": "Send Re-Engagement Email",
"type": "main",
"index": 0
},
{
"node": "Has WhatsApp Number?",
"type": "main",
"index": 0
}
]
]
},
"Google Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "Draft Re-Engagement Messages",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Send Re-Engagement Email": {
"main": [
[
{
"node": "Log Follow-Up Outcome",
"type": "main",
"index": 0
}
]
]
},
"Send Re-Engagement WhatsApp": {
"main": [
[
{
"node": "Update WhatsApp sent status",
"type": "main",
"index": 0
}
]
]
},
"Draft Re-Engagement Messages": {
"main": [
[
{
"node": "Parse AI Message Output",
"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.
gmailOAuth2googleCalendarOAuth2ApigooglePalmApigoogleSheetsOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
When a buyer misses a scheduled viewing, this workflow automatically sends an AI-personalized follow-up by email and WhatsApp using Google Gemini, then logs the outcome to Google Sheets, recovering leads that would otherwise go cold. Triggers every 15 minutes when a Google…
Source: https://n8n.io/workflows/16533/ — 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.
Arvifund - Supabase (Fixed v5). Uses httpRequest, telegram, googleSheets, telegramTrigger. Event-driven trigger; 91 nodes.
Arvifund - Supabase. Uses httpRequest, telegram, googleSheets, telegramTrigger. Event-driven trigger; 90 nodes.
Arvifund - Supabase (Fixed v2). Uses httpRequest, telegram, googleSheets, telegramTrigger. Event-driven trigger; 90 nodes.
Arvifund - Supabase (Fixed v4). Uses httpRequest, telegram, googleSheets, telegramTrigger. Event-driven trigger; 90 nodes.
Arvifund - Supabase (Fixed v3). Uses httpRequest, telegram, googleSheets, telegramTrigger. Event-driven trigger; 90 nodes.