AutomationFlowsAI & RAG › Automate Real Estate Lead Matching with Jotform, & Gemini AI to Zoho CRM

Automate Real Estate Lead Matching with Jotform, & Gemini AI to Zoho CRM

ByAbdullah Alshiekh @abdullah01 on n8n.io

In real estate, inquiries come from many sources and often require immediate, personalized attention. Brokers waste significant time manually:

Event trigger★★★★☆ complexityAI-powered15 nodesJot Form TriggerAgentGoogle Gemini ChatGoogle Sheets ToolOutput Parser StructuredGmailZoho Crm
AI & RAG Trigger: Event Nodes: 15 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #9494 — 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 →

Download .json
{
  "id": "DPZycgH7MnQo6dpN",
  "name": "JotForm to Zoho Lead Automation (Submitted)",
  "tags": [],
  "nodes": [
    {
      "id": "95504a3e-0464-4260-b058-011b59b035d9",
      "name": "Set: Normalize Lead",
      "type": "n8n-nodes-base.set",
      "position": [
        -80,
        624
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "lead_fullName",
              "name": "lead.fullName",
              "type": "string",
              "value": "={{ $json.name || $json['Name'] || '' }}"
            },
            {
              "id": "lead_email",
              "name": "lead.email",
              "type": "string",
              "value": "={{ $json.email || $json['Email'] || '' }}"
            },
            {
              "id": "lead_phone",
              "name": "lead.phone",
              "type": "string",
              "value": "={{ $json.phone || $json['Cell Phone Number'] || '' }}"
            },
            {
              "id": "lead_moveInDate",
              "name": "lead.moveInDate",
              "type": "string",
              "value": "={{ $json.move_in_date || $json['Desired move in date'] || '' }}"
            },
            {
              "id": "lead_mustHaves",
              "name": "lead.mustHaves",
              "type": "string",
              "value": "={{ ($json.must_haves || $json['Any \\'must haves\\' for your apartment?'] || '').toString().trim() }}"
            },
            {
              "id": "lead_minBedrooms",
              "name": "lead.minBedrooms",
              "type": "string",
              "value": "={{ $json['Minimum # of bedrooms desired'] }}"
            },
            {
              "id": "lead_minBathrooms",
              "name": "lead.minBathrooms",
              "type": "string",
              "value": "={{ $json['Minimum # of bathrooms desired'] }}"
            },
            {
              "id": "lead_needsAssignedParking",
              "name": "lead.needsAssignedParking",
              "type": "boolean",
              "value": "={{ ($json.assigned_parking || $json['Do you need assigned parking?'] || '').toString().toLowerCase() === 'yes' }}"
            },
            {
              "id": "lead_hasPets",
              "name": "lead.hasPets",
              "type": "boolean",
              "value": "={{ ($json.has_pets || $json['Do you have pets?'] || '').toString().toLowerCase().includes('dont') }}"
            },
            {
              "id": "lead_preferredNeighborhoods",
              "name": "lead.preferredNeighborhoods",
              "type": "array",
              "value": "={{ ($json.preferred_neighborhoods || $json['List your preferred neighborhoods'] || '').toString().split(/,|\\n/).map(s => s.trim()).filter(s => s.length > 0) }}"
            },
            {
              "id": "lead_maxMonthlyRent",
              "name": "lead.maxMonthlyRent",
              "type": "number",
              "value": "={{ (() => { const val = ($json.price_range || $json['What is the price range you are considering?'] || '').toString().replace(/[^0-9.]/g, ''); return val ? Number(val) : null; })() }}"
            },
            {
              "id": "lead_submittedAt",
              "name": "lead.submittedAt",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "e4430f1c-e7b6-4360-9826-4717bdceec21",
      "name": "JotForm Trigger",
      "type": "n8n-nodes-base.jotFormTrigger",
      "position": [
        -384,
        624
      ],
      "parameters": {
        "form": "YOUR_JOTFORM_FORM_ID"
      },
      "typeVersion": 1
    },
    {
      "id": "3eebca56-8295-4c30-91af-a97f35a8b7b3",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        176,
        624
      ],
      "parameters": {
        "text": "={\n  \"lead\": {{ JSON.stringify($json.lead) }},\n  \"listings\": googleSheets tool node \n}\n",
        "options": {
          "systemMessage": "=You are a real-estate assistant that receives a customer\u2019s apartment search criteria and finds the best matching listings from a Google Sheet database.\n\nYou have access to the tool \"googleSheets\" which contains all available apartment listings. Each row includes fields such as:\nlisting_id, supplier_name, supplier_type, bedrooms, bathrooms, assigned_parking, rent, pets_allowed, furnished, neighborhoods, available_from, address, contact_email, contact_phone, crm_listing_id, images, sqft, floor, elevator, balcony, supplier_rating.\n\nYour goal:\n1. Search the sheet for apartments that match the user\u2019s criteria.\n2. Return the top 3 matches ranked by overall suitability.\n3. Return your reasoning and summarize why each match fits.\n4. Always output in valid JSON.\n\n### Matching rules\n- Bedrooms \u2265 requested minimum.\n- Bathrooms \u2265 requested minimum.\n- Assigned parking must be \"Yes\" if the user needs it.\n- Rent \u2264 maximum price range (if provided).\n- If user has a pet \u2192 only listings where `pets_allowed` = \"Yes\".\n- Preferred neighborhoods \u2192 give higher score to exact matches or close alternatives.\n- Consider soonest available dates \u2265 requested move-in date.\n- Higher `supplier_rating` is better.\n- Ignore listings that violate hard constraints (e.g. rent too high or parking required but missing).\n\n### Required Output Format\nUse this exact structure (valid JSON only):\n\n{\n  \"recommendedListingId\": \"string\",\n  \"topMatches\": [\n    {\n      \"listingId\": \"string\",\n      \"score\": number,\n      \"reason\": \"string\",\n      \"rent\": number,\n      \"bedrooms\": number,\n      \"bathrooms\": number,\n      \"neighborhood\": \"string\",\n      \"availableFrom\": \"string\"\n    }\n  ],\n  \"notes\": \"string\"\n}\n\nDo not include explanations or prose outside this JSON.\n"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "1b2e69b8-bde7-45d2-bfb0-2886ac2f3997",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        16,
        912
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "5e06fdc6-e204-41e1-a3e5-dd0fce8cffd2",
      "name": "googleSheets",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        272,
        928
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_SHEET_GID_OR_NAME",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_DOCUMENT_ID/edit#gid=YOUR_SHEET_GID_OR_NAME",
          "cachedResultName": "Sheet Name (Template)"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_DOCUMENT_ID",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_DOCUMENT_ID/edit?usp=drivesdk",
          "cachedResultName": "Sample Database (Template)"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "dfdfbe18-115f-4d97-a75f-915dff0b3e11",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        512,
        896
      ],
      "parameters": {
        "jsonSchemaExample": "{\n  \"recommendedListingId\": \"string\",\n  \"topMatches\": [\n    {\n      \"listingId\": \"string\",\n      \"score\": \"\",\n      \"reason\": \"string\",\n      \"rent\": \"\",\n      \"bedrooms\": \"\",\n      \"bathrooms\": \"\",\n      \"neighborhood\": \"string\",\n      \"availableFrom\": \"string\"\n    }\n  ],\n  \"notes\": \"string\"\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "29e27f69-2060-4058-87dd-1c118acfd4be",
      "name": "Send a message",
      "type": "n8n-nodes-base.gmail",
      "position": [
        672,
        624
      ],
      "parameters": {
        "sendTo": "={{ $('JotForm Trigger').item.json.Email }}",
        "message": "=Hi {{ $('JotForm Trigger').item.json.Name }},\n\nGreat news! \ud83c\udf89 Based on your preferences, we\u2019ve found several apartments that closely match what you\u2019re looking for. \n\nWe\u2019d love to discuss these options with you and help you pick the perfect one.\n\nPlease use the link below to book a quick meeting with our property consultant: \ud83d\udc49 YOUR_CALENDLY_LINK\n\nDuring the call, we\u2019ll review the listings, confirm availability, and guide you through the next steps.\n\nThe Real Estate Team \u2014 If you\u2019ve already scheduled a call, please disregard this message.\n\nLooking forward to speaking with you soon!\nBest regards,\n",
        "options": {},
        "subject": "Your apartment search results are ready",
        "emailType": "text"
      },
      "typeVersion": 2.1,
      "alwaysOutputData": false
    },
    {
      "id": "e0793c85-9b49-4a34-9b1a-03862379c05f",
      "name": "Create a lead",
      "type": "n8n-nodes-base.zohoCrm",
      "position": [
        928,
        624
      ],
      "parameters": {
        "Company": "TEMPLATED_COMPANY_NAME",
        "lastName": "={{ $('JotForm Trigger').item.json.Name }}",
        "resource": "lead",
        "additionalFields": {
          "Email": "={{ $('JotForm Trigger').item.json.Email }}",
          "Mobile": "={{ $('JotForm Trigger').item.json['Cell Phone Number'] }}",
          "Full_Name": "={{ $('JotForm Trigger').item.json.Name }}",
          "Description": "=\ud83e\udded Apartment Match Summary\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nLead Name: {{ $('JotForm Trigger').item.json.Name }}\nEmail: {{ $('JotForm Trigger').item.json.Email }}\nPhone: {{ $('JotForm Trigger').item.json['Cell Phone Number'] }}\nMove-in Date: {{ $('Set: Normalize Lead').item.json.lead.moveInDate }}\nPreferred Neighborhoods: {{ $('Set: Normalize Lead').item.json.lead.preferredNeighborhoods }}\nMax Rent: {{ $('Set: Normalize Lead').item.json.lead.maxMonthlyRent }} EGP\nNeeds Parking: {{ $('Set: Normalize Lead').item.json.lead.needsAssignedParking }}\nHas Pets: {{ $('Set: Normalize Lead').item.json.lead.hasPets }}\n\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ud83c\udfe0 AI Recommended Listing\nRecommended Listing ID: {{ $json.output.recommendedListingId }}\nTop Match Neighborhood: {{ $json.output.topMatches[0].neighborhood }}\nRent: {{ $json.output.topMatches[0].rent }} EGP\nBedrooms/Bathrooms: {{ $json.output.topMatches[0].bedrooms }}/{{ $json.output.topMatches[0].bathrooms }}\nAvailable From: {{ $json.output.topMatches[0].availableFrom }}\n\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ud83c\udfd8\ufe0f Other Good Matches\n2\ufe0f\u20e3 {{ $json.output.topMatches[1].listingId }} \u2014 {{ $json.output.topMatches[1].neighborhood }}\n   Rent: {{ $json.output.topMatches[1].rent }} EGP\n\n3\ufe0f\u20e3 {{ $json.output.topMatches[2].listingId }} \u2014 {{ $json.output.topMatches[2].neighborhood }}\n   Rent:  {{ $json.output.topMatches[2].rent }} EGP\n\n\n",
          "Lead_Source": "JotForm form"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4ad8beca-d9ad-414f-a0ad-bc56011f0993",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -448,
        544
      ],
      "parameters": {
        "color": 3,
        "content": "Starts the workflow when a user submits the JotForm, capturing the raw lead data."
      },
      "typeVersion": 1
    },
    {
      "id": "df77592b-4994-41a5-b47c-59ce7923bb44",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -144,
        544
      ],
      "parameters": {
        "color": 4,
        "content": "Maps the raw form fields to a standardized lead object for consistent data use.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "80c58506-ad79-481e-9d5a-24985dbb3cd5",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        112,
        528
      ],
      "parameters": {
        "color": 6,
        "width": 336,
        "content": "The core logic. Uses lead criteria and the googleSheets tool to find and rank the top three matching listings.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "4c92a0b0-2f32-4b94-81cf-0b0dd4d842ef",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        176,
        976
      ],
      "parameters": {
        "color": 5,
        "width": 288,
        "content": ".\n\n\n\n\nThe tool that allows the AI to search and retrieve all apartment listings from the Google Sheet database.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "9a308dff-e55f-47fc-b5b6-1319b6e78e52",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        592,
        528
      ],
      "parameters": {
        "color": 4,
        "width": 256,
        "content": "Automatically emails the lead to notify them their results are ready and prompts them to book a follow-up consultation.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "cbe5ea3e-4edc-4a3d-8bbf-ddaaefc9baee",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        864,
        528
      ],
      "parameters": {
        "color": 5,
        "width": 288,
        "content": "Creates a new, detailed lead record in Zoho CRM, including the customer's needs and the AI's top listing recommendations.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "3873d283-f68d-4bc3-90ae-4ebf61767759",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -448,
        -224
      ],
      "parameters": {
        "width": 1024,
        "height": 736,
        "content": "# JotForm Setup Guide\n\n\n## \u26a1 Step 1: Link with the Webhook\n\n\n* **1. Log in to your JotForm account, select your Cashback Form, and navigate to Settings.**\n\n* **2. Go to Integrations and search for Webhooks.**\n\n* **3. Paste the JotForm Trigger Webhook URL provided by your n8n workflow into the field.**\n\n* **4. Save and activate! Your form is now live!**\n\n## \ud83d\udd11 Step 2: Unlock File Access (API Key)\n\n* **1.Generate a JotForm API Key in your JotForm account settings.**\n\n* **2.CRITICAL: Set the key permission level to \"Full Access\" (required for file downloads).**\n\n* **3.Go to your n8n workflow and insert this key into:**\n    * **I.The JotForm Trigger node's credential.**\n    * **II.The Fetch All Receipts node's query parameters.**\n\n## \u2699\ufe0f Step 3: Configure Your n8n Nodes\n\n* **In the JotForm Trigger node replace the placeholder with your actual JotForm ID.**"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "27b5e246-a6bf-40c4-8ddf-52a41aeb2040",
  "connections": {
    "AI Agent": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "googleSheets": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Send a message": {
      "main": [
        [
          {
            "node": "Create a lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "JotForm Trigger": {
      "main": [
        [
          {
            "node": "Set: Normalize Lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set: Normalize Lead": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "AI Agent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

In real estate, inquiries come from many sources and often require immediate, personalized attention. Brokers waste significant time manually:

Source: https://n8n.io/workflows/9494/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Workflow explaination video: https://youtu.be/z1grVuNOXMk

Jot Form Trigger, Agent, Google Gemini Chat +4
AI & RAG

Agente_Creador_de_Freebies. Uses formTrigger, agent, lmChatGoogleGemini, lmChatOpenAi. Event-driven trigger; 26 nodes.

Form Trigger, Agent, Google Gemini Chat +6
AI & RAG

This blueprint details a highly efficient, AI-powered workflow designed to automate customer reward fulfillment. Leveraging the accessible interface of Jotform, this system delivers superior reliabili

Jot Form Trigger, Google Gemini Chat, HTTP Request +4
AI & RAG

Turn raw feedback into actionable product insights. This workflow collects feedback from both customers and staff via a single Jotform, uses Gemini AI to analyze and categorize it, then intelligently

Jot Form Trigger, Agent, Output Parser Structured +5
AI & RAG

Stop drowning in job applications. This workflow transforms your hiring process from a manual, time-consuming data-entry task into an automated, intelligent screening system.

Jot Form Trigger, Google Gemini Chat, Output Parser Structured +5