This workflow corresponds to n8n.io template #7329 — we link there as the canonical source.
This workflow follows the Form Trigger → 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": "EdYQiWMiaUiLHaY5",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Real Estate Price",
"tags": [],
"nodes": [
{
"id": "cb598cd5-1145-4de8-adb7-50f88c9e2c13",
"name": "Property Search Form",
"type": "n8n-nodes-base.formTrigger",
"position": [
120,
240
],
"parameters": {
"path": "property-search-form",
"options": {},
"formTitle": "\ud83c\udfe0 Find Your Perfect Property",
"formFields": {
"values": [
{
"fieldType": "email",
"fieldLabel": "Your Email Address",
"requiredField": true
},
{
"fieldType": "select",
"fieldLabel": "Property Status"
},
{
"fieldType": "number",
"fieldLabel": "Minimum Price ($)"
},
{
"fieldType": "number",
"fieldLabel": "Maximum Price ($)"
},
{
"fieldType": "number",
"fieldLabel": "Minimum Bedrooms"
},
{
"fieldType": "number",
"fieldLabel": "Maximum Bedrooms"
},
{
"fieldType": "number",
"fieldLabel": "Minimum Bathrooms"
},
{
"fieldType": "number",
"fieldLabel": "Maximum Bathrooms"
},
{
"fieldType": "number",
"fieldLabel": "Minimum House Size (sqft)"
},
{
"fieldType": "number",
"fieldLabel": "Maximum House Size (sqft)"
},
{
"fieldType": "number",
"fieldLabel": "Minimum Lot Size (acres)"
},
{
"fieldLabel": "State"
},
{
"fieldLabel": "City"
},
{
"fieldLabel": "ZIP Code"
}
]
},
"formDescription": "Search through 1000+ real estate properties in our database"
},
"typeVersion": 2
},
{
"id": "2498ced6-ab95-4967-a902-2ebf66361a54",
"name": "Build SQL Query",
"type": "n8n-nodes-base.code",
"position": [
340,
240
],
"parameters": {
"jsCode": "// N8N JavaScript Code Tool for Property Search - Query Builder\n// This code builds the SQL query based on form criteria\n\n// Get form data from the previous node\nconst formData = $input.all()[0].json;\n\nconsole.log('Received form data:', JSON.stringify(formData, null, 2));\n\n// Initialize query components\nlet selectClause = `SELECT TOP (1000) [brokered_by]\n ,[status]\n ,[price]\n ,[bed]\n ,[bath]\n ,[acre_lot]\n ,[street]\n ,[city]\n ,[state]\n ,[zip_code]\n ,[house_size]\n ,[prev_sold_date]`;\n\nlet fromClause = 'FROM [REALTOR].[dbo].[realtor_usa_price]';\nlet whereConditions = [];\n\n// Helper function to add numeric range conditions\nfunction addNumericRangeCondition(fieldName, minValue, maxValue, paramPrefix) {\n const conditions = [];\n \n if (minValue !== undefined && minValue !== null && minValue !== '' && minValue !== 0) {\n conditions.push(`[${fieldName}] >= ${minValue}`);\n }\n \n if (maxValue !== undefined && maxValue !== null && maxValue !== '' && maxValue !== 0) {\n conditions.push(`[${fieldName}] <= ${maxValue}`);\n }\n \n return conditions;\n}\n\n// Helper function to add string conditions\nfunction addStringCondition(fieldName, value) {\n if (value && value.trim() !== '') {\n whereConditions.push(`[${fieldName}] = '${value.trim()}'`);\n }\n}\n\n// Process form parameters\ntry {\n // Price range\n const priceConditions = addNumericRangeCondition(\n 'price', \n formData['Minimum Price ($)'], \n formData['Maximum Price ($)'], \n 'price'\n );\n whereConditions.push(...priceConditions);\n\n // Bedroom range\n const bedConditions = addNumericRangeCondition(\n 'bed', \n formData['Minimum Bedrooms'], \n formData['Maximum Bedrooms'], \n 'bed'\n );\n whereConditions.push(...bedConditions);\n\n // Bathroom range\n const bathConditions = addNumericRangeCondition(\n 'bath', \n formData['Minimum Bathrooms'], \n formData['Maximum Bathrooms'], \n 'bath'\n );\n whereConditions.push(...bathConditions);\n\n // House size range (if provided)\n const houseSizeConditions = addNumericRangeCondition(\n 'house_size', \n formData['Minimum House Size (sqft)'], \n formData['Maximum House Size (sqft)'], \n 'house_size'\n );\n whereConditions.push(...houseSizeConditions);\n\n // Lot size range (if provided)\n const lotSizeConditions = addNumericRangeCondition(\n 'acre_lot', \n formData['Minimum Lot Size (acres)'], \n formData['Maximum Lot Size (acres)'], \n 'lot_size'\n );\n whereConditions.push(...lotSizeConditions);\n\n // String filters\n addStringCondition('state', formData['State']);\n addStringCondition('city', formData['City']);\n \n // ZIP Code (if provided)\n if (formData['ZIP Code'] && formData['ZIP Code'] !== '') {\n whereConditions.push(`[zip_code] = ${parseInt(formData['ZIP Code'])}`);\n }\n\n // Property Status (if provided and not empty)\n addStringCondition('status', formData['Property Status']);\n\n} catch (error) {\n console.error('Error processing form data:', error);\n return [{\n json: {\n error: 'Failed to process form parameters',\n details: error.message,\n receivedData: formData\n }\n }];\n}\n\n// Build the complete query\nlet finalQuery = selectClause + '\\n' + fromClause;\n\nif (whereConditions.length > 0) {\n finalQuery += '\\nWHERE ' + whereConditions.join('\\n AND ');\n}\n\n// Add ordering\nfinalQuery += '\\nORDER BY [price] ASC';\n\nconsole.log('Generated SQL Query:', finalQuery);\n\n// Return the query for the SQL Server node to execute\nreturn [{\n json: {\n query: finalQuery,\n searchCriteria: {\n priceRange: {\n min: formData['Minimum Price ($)'] || null,\n max: formData['Maximum Price ($)'] || null\n },\n bedrooms: {\n min: formData['Minimum Bedrooms'] || null,\n max: formData['Maximum Bedrooms'] || null\n },\n bathrooms: {\n min: formData['Minimum Bathrooms'] || null,\n max: formData['Maximum Bathrooms'] || null\n },\n houseSizeRange: {\n min: formData['Minimum House Size (sqft)'] || null,\n max: formData['Maximum House Size (sqft)'] || null\n },\n lotSizeRange: {\n min: formData['Minimum Lot Size (acres)'] || null,\n max: formData['Maximum Lot Size (acres)'] || null\n },\n location: {\n state: formData['State'] || null,\n city: formData['City'] || null,\n zipCode: formData['ZIP Code'] || null\n },\n status: formData['Property Status'] || null\n },\n formData: formData,\n totalConditions: whereConditions.length,\n userEmail: formData['Your Email Address'],\n searchTimestamp: new Date().toISOString()\n }\n}];"
},
"typeVersion": 2
},
{
"id": "b846fe8e-e5bc-4b2b-8c38-3fe17102318b",
"name": "Microsoft SQL",
"type": "n8n-nodes-base.microsoftSql",
"position": [
520,
240
],
"parameters": {
"query": "{{ $json.query }}",
"operation": "executeQuery"
},
"typeVersion": 1.1
},
{
"id": "9544305e-8b88-4c81-b471-7bb6bcab74b6",
"name": "Convert to File",
"type": "n8n-nodes-base.convertToFile",
"position": [
740,
240
],
"parameters": {
"options": {}
},
"typeVersion": 1.1
},
{
"id": "254786a5-8d35-4978-918e-5bcaf2727b7a",
"name": "Send a message",
"type": "n8n-nodes-base.gmail",
"position": [
980,
240
],
"parameters": {
"sendTo": "={{ $('Property Search Form').item.json['Your Email Address'] }}",
"message": "=Hi there! \ud83d\udc4b\n\nGreat news! We found {{ $('Microsoft SQL').all().length }} properties that match your search criteria.\n\n\ud83c\udfe0 TOP MATCHES:\n{{ $('Microsoft SQL').all().slice(0, 3).map(property => \n`\ud83d\udcb0 $${property.json.price.toLocaleString()} - ${property.json.bed}bed/${property.json.bath}bath in ${property.json.city}, ${property.json.state}`\n).join('\\n') }}\n\n\ud83d\udcce COMPLETE RESULTS ATTACHED\nAll {{ $('Microsoft SQL').all().length }} properties are in the attached CSV file - perfect for Excel!\n\n\ud83c\udfaf YOUR SEARCH:\n- Price Range: ${{ $('Build SQL Query').first().json.searchCriteria.priceRange.min?.toLocaleString() || 'Any' }} - ${{ $('Build SQL Query').first().json.searchCriteria.priceRange.max?.toLocaleString() || 'Any' }}\n- Bedrooms: {{ $('Build SQL Query').first().json.searchCriteria.bedrooms.min || 'Any' }}+\n- Location: {{ $('Build SQL Query').first().json.searchCriteria.location.city || $('Build SQL Query').first().json.searchCriteria.location.state || 'Anywhere' }}\n\nHappy house hunting! \ud83c\udfe1\u2728\n\nYour Property Search Team",
"options": {
"attachmentsUi": {
"attachmentsBinary": [
{}
]
}
},
"subject": "=\ud83c\udfe0 Your Dream Home Search Results - {{ $('Microsoft SQL').all().length }} Properties Found!",
"emailType": "text"
},
"typeVersion": 2.1
},
{
"id": "37c6710a-8a1f-42f0-a286-a90d784b7824",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-40,
60
],
"parameters": {
"width": 280,
"height": 140,
"content": "## \ud83c\udfaf 1. START HERE!\nUser fills out their dream home criteria - price range, bedrooms, location, etc. This triggers the entire search process!"
},
"typeVersion": 1
},
{
"id": "7c8fda0b-383c-453a-a2a7-0bf2be41e99d",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
200,
400
],
"parameters": {
"color": 2,
"width": 340,
"content": "## \ud83e\udde0 2. SMART QUERY BUILDER\nTakes user input and creates a dynamic SQL query. Only adds conditions for fields the user actually filled out - no empty searches!"
},
"typeVersion": 1
},
{
"id": "4a132039-0d82-4d4f-a870-8f0d2a0143ab",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
420,
80
],
"parameters": {
"color": 4,
"width": 360,
"height": 140,
"content": "## \ud83d\udd0d 3. DATABASE DETECTIVE\nSearches through 1000+ real estate properties in SQL Server and finds matches based on user criteria. Returns real property data!"
},
"typeVersion": 1
},
{
"id": "7f09df22-f034-4b8e-8405-1591ded23d6f",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
640,
400
],
"parameters": {
"color": 5,
"width": 320,
"height": 140,
"content": "## \ud83d\udcca 4. EXCEL MAGIC\nTransforms search results into a beautiful CSV file that users can open in Excel or Google Sheets for easy viewing and sorting!"
},
"typeVersion": 1
},
{
"id": "55d26769-fc6e-4777-b556-6c2e3d668d21",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
900,
80
],
"parameters": {
"color": 6,
"width": 320,
"height": 140,
"content": "## \ud83d\udce7 5. DELIVERY SERVICE\nSends personalized email with search results attached. User gets their dream properties delivered straight to their inbox!"
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "c31c11a2-7b73-4c44-ac15-9ede8725fd7e",
"connections": {
"Microsoft SQL": {
"main": [
[
{
"node": "Convert to File",
"type": "main",
"index": 0
}
]
]
},
"Build SQL Query": {
"main": [
[
{
"node": "Microsoft SQL",
"type": "main",
"index": 0
}
]
]
},
"Convert to File": {
"main": [
[
{
"node": "Send a message",
"type": "main",
"index": 0
}
]
]
},
"Property Search Form": {
"main": [
[
{
"node": "Build SQL Query",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Transform property searches into personalized experiences! This powerful automation delivers dream home matches straight to clients' inboxes with professional CSV reports - all from a simple web form.
Source: https://n8n.io/workflows/7329/ — 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.
Loan eligibility workflow. Uses formTrigger, googleSheets, gmail. Event-driven trigger; 53 nodes.
Echo Brand Voice Analysis (Processor) - TASK-074 Dec 10 Fix. Uses formTrigger, httpRequest, executeWorkflowTrigger, moveBinaryData. Event-driven trigger; 40 nodes.
Streamline your content pipeline by bridging Notion and Instagram with a professional "review-before-publish" safeguard. This workflow allows team members to submit content via a simple form, generate
🎥 Analyze YouTube Video for Summaries, Transcripts & Content + Google Gemini AI. Uses stickyNote, httpRequest, googleDrive, gmail. Event-driven trigger; 33 nodes.
This workflow automates event registrations and attendee communication from initial signup to event day. It captures form submissions, prevents duplicate entries, and stores registrations in Google Sh