{
  "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, '&amp;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;');\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
          }
        ]
      ]
    }
  }
}