Workflow · Property Managers

n8n Workflow for Tenant Maintenance Request Automation 2026

Complete n8n workflow that automatically creates tickets, assigns contractors, and tracks maintenance requests from tenant submissions to completion.

This n8n workflow automatically processes tenant maintenance requests from a Google Form submission through ticket creation, contractor assignment, and completion tracking. It connects Google Forms, Sheets, Gmail, and Slack to eliminate 45 minutes of manual coordination per request.

Why this automation matters

Without automation, each maintenance request requires manual data entry, contractor lookup, email drafting, and status tracking across multiple systems. Property managers lose requests in email threads, assign wrong contractors to jobs outside their expertise, and tenants wait days for acknowledgment. This workflow ensures every request gets processed within 10 minutes and assigned to the right contractor based on issue type.

What you need before starting

How to build it: step by step

1. Google Sheets Trigger — Monitor new maintenance requests

Node type: Google Sheets Trigger Sheet: Your maintenance request response sheet Trigger On: Row Added Poll Time: Every 2 minutes Output: New form submission data as individual items containing tenant info, issue details, and timestamp. Why this matters: The 2-minute polling ensures rapid response times while avoiding API rate limits during busy periods.

2. Set Node — Standardize contractor assignment logic

Node type: Set Fields to Set:

3. Google Sheets — Log processed request

Node type: Google Sheets Operation: Append Row Sheet: Maintenance Tracking > Active Requests Values to Append: Ticket ID, Tenant Name, Address, Issue Type, Contractor Email, Date Created, Status (set to “Assigned”) Output: Confirmation of logged entry with row number for future updates. Why this matters: Creates a centralized tracking record that survives email deletions and provides status history.

4. Gmail — Notify assigned contractor

Node type: Gmail Operation: Send Email To: {{ $json.contractor_email }} Subject: {{ 'NEW ' + $json.priority_level + ' PRIORITY: ' + $json.ticket_id + ' - ' + $json.issue_type }} Message: Details the issue, tenant contact info, property address, and requested completion timeframe Output: Email delivery confirmation with message ID for tracking. Why this matters: Contractors receive consistent formatting with all necessary details to schedule and price the job immediately.

5. Gmail — Send acknowledgment to tenant

Node type: Gmail Operation: Send Email To: {{ $json.tenant_email }} Subject: {{ 'Maintenance Request Received - ' + $json.ticket_id }} Message: Confirms receipt, provides ticket number, contractor contact info, and expected response timeframe Output: Tenant notification delivery confirmation. Why this matters: Immediate acknowledgment prevents follow-up calls and gives tenants a reference number for status inquiries.

6. Slack — Alert property management team

Node type: Slack Operation: Send Message Channel: #maintenance-alerts Message: {{ 'New ' + $json.priority_level + ' priority request: ' + $json.ticket_id + ' assigned to ' + $json.contractor_email + ' for ' + $json.issue_type + ' at ' + $json.property_address }} Output: Posted message with timestamp and channel confirmation. Why this matters: Team visibility enables intervention for emergency requests and provides backup communication if contractors don’t respond.

Full workflow JSON

{
  "name": "Tenant Maintenance Request Automation",
  "nodes": [
    {
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute",
              "minute": 2
            }
          ]
        },
        "documentId": {
          "__rl": true,
          "value": "your-google-sheet-id",
          "mode": "list",
          "cachedResultName": "Maintenance Requests"
        },
        "sheetName": {
          "__rl": true,
          "value": "Form Responses 1",
          "mode": "list",
          "cachedResultName": "Form Responses 1"
        },
        "event": "rowAdded"
      },
      "id": "a1b2c3d4-e5f6-7g8h-9i0j-k1l2m3n4o5p6",
      "name": "New Maintenance Request",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "typeVersion": 4,
      "position": [460, 240],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "google-sheets-credential-id",
          "name": "Google Sheets Maintenance"
        }
      }
    },
    {
      "parameters": {
        "values": {
          "string": [
            {
              "name": "contractor_email",
              "value": "={{ $json['Issue Type'] === 'Plumbing' ? 'plumber@contractors.com' : $json['Issue Type'] === 'Electrical' ? 'electrician@contractors.com' : $json['Issue Type'] === 'HVAC' ? 'hvac@contractors.com' : 'general@contractors.com' }}"
            },
            {
              "name": "priority_level",
              "value": "={{ $json['Urgency Level'] === 'Emergency' ? 'HIGH' : 'NORMAL' }}"
            },
            {
              "name": "ticket_id",
              "value": "={{ 'MNT-' + new Date().getFullYear() + '-' + String(Date.now()).slice(-6) }}"
            },
            {
              "name": "tenant_name",
              "value": "={{ $json['Tenant Name'] }}"
            },
            {
              "name": "tenant_email",
              "value": "={{ $json['Email Address'] }}"
            },
            {
              "name": "property_address",
              "value": "={{ $json['Property Address'] }}"
            },
            {
              "name": "issue_type",
              "value": "={{ $json['Issue Type'] }}"
            },
            {
              "name": "issue_description",
              "value": "={{ $json['Description of Issue'] }}"
            },
            {
              "name": "urgency",
              "value": "={{ $json['Urgency Level'] }}"
            }
          ]
        },
        "options": {}
      },
      "id": "b2c3d4e5-f6g7-8h9i-0j1k-l2m3n4o5p6q7",
      "name": "Process Request Data",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3,
      "position": [680, 240]
    },
    {
      "parameters": {
        "operation": "appendOrUpdate",
        "documentId": {
          "__rl": true,
          "value": "your-tracking-sheet-id",
          "mode": "list",
          "cachedResultName": "Maintenance Tracking"
        },
        "sheetName": {
          "__rl": true,
          "value": "Active Requests",
          "mode": "list",
          "cachedResultName": "Active Requests"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Ticket ID": "={{ $json.ticket_id }}",
            "Tenant Name": "={{ $json.tenant_name }}",
            "Property Address": "={{ $json.property_address }}",
            "Issue Type": "={{ $json.issue_type }}",
            "Contractor Email": "={{ $json.contractor_email }}",
            "Date Created": "={{ new Date().toISOString().split('T')[0] }}",
            "Status": "Assigned",
            "Priority": "={{ $json.priority_level }}"
          },
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "id": "c3d4e5f6-g7h8-9i0j-1k2l-m3n4o5p6q7r8",
      "name": "Log to Tracking Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [900, 180],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "google-sheets-credential-id",
          "name": "Google Sheets Maintenance"
        }
      }
    },
    {
      "parameters": {
        "sendTo": "={{ $json.contractor_email }}",
        "subject": "={{ 'NEW ' + $json.priority_level + ' PRIORITY: ' + $json.ticket_id + ' - ' + $json.issue_type }}",
        "emailType": "text",
        "message": "=New maintenance request assigned:\n\nTicket: {{ $json.ticket_id }}\nProperty: {{ $json.property_address }}\nTenant: {{ $json.tenant_name }}\nContact: {{ $json.tenant_email }}\n\nIssue Type: {{ $json.issue_type }}\nPriority: {{ $json.priority_level }}\nDescription: {{ $json.issue_description }}\n\nPlease respond within {{ $json.priority_level === 'HIGH' ? '2 hours' : '24 hours' }} with availability and estimated cost.\n\nQuestions? Reply to this email or call the office.",
        "options": {}
      },
      "id": "d4e5f6g7-h8i9-0j1k-2l3m-n4o5p6q7r8s9",
      "name": "Email Contractor",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [900, 300],
      "credentials": {
        "gmailOAuth2": {
          "id": "gmail-credential-id",
          "name": "Property Manager Gmail"
        }
      }
    },
    {
      "parameters": {
        "sendTo": "={{ $json.tenant_email }}",
        "subject": "={{ 'Maintenance Request Received - ' + $json.ticket_id }}",
        "emailType": "text",
        "message": "=Dear {{ $json.tenant_name }},\n\nWe've received your maintenance request and assigned ticket number {{ $json.ticket_id }}.\n\nRequest Details:\nProperty: {{ $json.property_address }}\nIssue: {{ $json.issue_type }}\nPriority: {{ $json.priority_level }}\n\nYour assigned contractor will contact you within {{ $json.priority_level ===