By Sagar Shankaran, Founder of CallSphere
Build an AI tenant support agent that handles maintenance ticket creation, rent balance lookups, lease question answering, and smart escalation to property management staff.
Key takeaways
Property managers spend 60-70% of their time answering repetitive tenant questions: "When is my rent due?", "What is the status of my maintenance request?", "Can I have a pet?" An AI tenant support agent handles these inquiries instantly while creating proper tickets for issues that need human attention.
This guide walks through building a tenant support agent with maintenance ticket creation, rent inquiry handling, lease lookups, and intelligent escalation.
We start with the data layer that the agent needs to access.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent for real estate in your browser — 60 seconds, no signup.
flowchart LR
CALLER(["Buyer or Seller Lead"])
subgraph TEL["Telephony"]
SIP["Twilio SIP and PSTN"]
end
subgraph BRAIN["Real Estate AI Agent"]
STT["Streaming STT<br/>Deepgram or Whisper"]
NLU{"Intent and<br/>Entity Extraction"}
TOOLS["Tool Calls"]
TTS["Streaming TTS<br/>ElevenLabs or Rime"]
end
subgraph DATA["Live Data Plane"]
CRM[("CRM and Notes")]
CAL[("Calendar and<br/>Schedule")]
KB[("Knowledge Base<br/>and Policies")]
end
subgraph OUT["Outcomes"]
O1(["Showing scheduled"])
O2(["Lead routed to agent"])
O3(["Pre-qual handed to broker"])
end
CALLER --> SIP --> STT --> NLU
NLU -->|Lookup| TOOLS
TOOLS <--> CRM
TOOLS <--> CAL
TOOLS <--> KB
NLU --> TTS --> SIP --> CALLER
NLU -->|Resolved| O1
NLU -->|Schedule| O2
NLU -->|Escalate| O3
style CALLER fill:#f1f5f9,stroke:#64748b,color:#0f172a
style NLU fill:#4f46e5,stroke:#4338ca,color:#fff
style O1 fill:#059669,stroke:#047857,color:#fff
style O2 fill:#0ea5e9,stroke:#0369a1,color:#fff
style O3 fill:#f59e0b,stroke:#d97706,color:#1f2937
from dataclasses import dataclass, field
from datetime import datetime, date
from enum import Enum
from typing import Optional
class TicketPriority(Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
EMERGENCY = "emergency"
class TicketStatus(Enum):
OPEN = "open"
IN_PROGRESS = "in_progress"
SCHEDULED = "scheduled"
COMPLETED = "completed"
@dataclass
class MaintenanceTicket:
ticket_id: str
tenant_id: str
unit: str
category: str # plumbing, electrical, hvac, appliance, etc.
description: str
priority: TicketPriority
status: TicketStatus
created_at: datetime
scheduled_date: Optional[date] = None
@dataclass
class TenantAccount:
tenant_id: str
name: str
unit: str
lease_start: date
lease_end: date
monthly_rent: float
balance_due: float
pet_policy: str
parking_spot: Optional[str] = None
The ticket creation tool needs to classify urgency automatically. A burst pipe is an emergency; a squeaky door is low priority.
from agents import function_tool
import uuid
EMERGENCY_KEYWORDS = ["flood", "fire", "gas leak", "no heat", "burst pipe", "sewage", "electrical fire"]
HIGH_PRIORITY_KEYWORDS = ["no hot water", "ac broken", "heater broken", "leak", "mold"]
def classify_priority(description: str) -> TicketPriority:
desc_lower = description.lower()
for kw in EMERGENCY_KEYWORDS:
if kw in desc_lower:
return TicketPriority.EMERGENCY
for kw in HIGH_PRIORITY_KEYWORDS:
if kw in desc_lower:
return TicketPriority.HIGH
return TicketPriority.MEDIUM
@function_tool
async def create_maintenance_ticket(
tenant_id: str,
category: str,
description: str,
) -> str:
"""Create a maintenance request ticket for a tenant."""
priority = classify_priority(description)
ticket_id = str(uuid.uuid4())[:8]
# In production, this writes to a database
ticket = MaintenanceTicket(
ticket_id=ticket_id,
tenant_id=tenant_id,
unit="auto-resolved", # looked up from tenant_id
category=category,
description=description,
priority=priority,
status=TicketStatus.OPEN,
created_at=datetime.now(),
)
response = f"Ticket {ticket_id} created (Priority: {priority.value})."
if priority == TicketPriority.EMERGENCY:
response += " EMERGENCY: Maintenance team has been paged immediately."
return response
@function_tool
async def check_ticket_status(ticket_id: str) -> str:
"""Look up the status of an existing maintenance ticket."""
# In production, this queries the database
return (
f"Ticket {ticket_id}: Status is IN_PROGRESS. "
f"Scheduled for Tuesday between 9 AM and 12 PM. "
f"Technician: Mike R."
)
The priority classification is intentionally keyword-based rather than LLM-based. For safety-critical routing like emergency maintenance, deterministic rules are more reliable than probabilistic model outputs.
@function_tool
async def get_rent_info(tenant_id: str) -> str:
"""Get rent balance, due date, and payment history for a tenant."""
# In production, this queries the accounting system
return (
"Monthly rent: $1,850.00\n"
"Current balance: $0.00 (paid through March 2026)\n"
"Next due date: April 1, 2026\n"
"Payment method: Auto-pay (Chase checking ending 4521)"
)
@function_tool
async def lookup_lease_terms(tenant_id: str, question: str) -> str:
"""Answer a question about a tenant's lease terms."""
# In production, this searches a parsed lease document
lease_data = {
"pet_policy": "Cats and small dogs (under 35 lbs) allowed with $500 deposit.",
"guest_policy": "Guests may stay up to 14 consecutive days without notification.",
"subletting": "Subletting is not permitted without written landlord approval.",
"early_termination": "60-day notice required. Early termination fee: 2 months rent.",
"parking": "One assigned spot included. Additional spots $75/month if available.",
}
q_lower = question.lower()
for key, answer in lease_data.items():
if any(word in q_lower for word in key.split("_")):
return answer
return "I could not find that specific clause. Let me connect you with property management."
Not every issue should stay with the AI. We build an explicit escalation tool.
@function_tool
async def escalate_to_manager(
tenant_id: str,
reason: str,
urgency: str = "normal",
) -> str:
"""Escalate an issue to the property manager when AI cannot resolve it."""
return (
f"Your request has been escalated to the property manager. "
f"Reason: {reason}. "
f"Expected response time: {'1 hour' if urgency == 'urgent' else '24 hours'}."
)
from agents import Agent
tenant_agent = Agent(
name="TenantSupportAgent",
instructions="""You are a tenant support assistant for Oakwood Apartments.
Identify the tenant by their unit number or tenant ID first.
Handle maintenance requests by creating tickets.
Answer rent and lease questions from the system.
Escalate to a manager for: complaints about neighbors, legal disputes,
lease negotiations, or anything you are unsure about.
Be empathetic and professional.""",
tools=[
create_maintenance_ticket,
check_ticket_status,
get_rent_info,
lookup_lease_terms,
escalate_to_manager,
],
)
The priority classifier detects emergency keywords and automatically pages the on-call maintenance team. The agent confirms to the tenant that emergency staff have been notified and provides safety instructions when applicable (e.g., "shut off the water main valve").
Still reading? Stop comparing — try CallSphere live.
See the real estate AI agent handle a real call — complete, industry-specific, and live in your browser. No signup.
No. The agent should only provide balance information and payment status. Actual payment processing should happen through a secure payment portal. The agent can share a link to that portal but should never collect payment card details directly.
Authentication happens before the agent conversation begins. The tenant's ID is injected into the session context, and all tool calls are scoped to that ID. The agent never accepts a tenant ID from the conversation — it uses only the authenticated session identity.
#TenantSupport #PropertyManagement #AgenticAI #Python #MaintenanceAutomation #LearnAI #AIEngineering

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
How estate agents and property managers in Luxembourg City and across the Grand Duchy use CallSphere to capture multilingual viewing and enquiry calls 24/7, GDPR compliant.
American real estate agents and property managers lose leads and tenants to unanswered calls. See how CallSphere AI voice and chat agents capture every inquiry 24/7.
Guyana's property market is white-hot as the oil economy drives demand. Georgetown real-estate agents and property managers use CallSphere AI voice and chat agents to answer every enquiry, book every viewing, and never lose a lead.
Property managers on the Dalmatian coast lose bookings and burn out answering guest messages around the clock. See how a CallSphere AI voice and chat agent handles every enquiry in every language, 24/7.
High-rise sales in Punta Pacífica and Costa del Este and the retiree rental market in Boquete run on international leads that arrive at every hour. Here is how Panama real estate and property firms use a bilingual AI voice and chat agent to catch them 24/7.
Market-data view of lead response in Kuwait real estate and property management across Kuwait City, Hawalli and Salmiya, and how CallSphere AI voice and chat agents qualify buyers and tenants 24/7 in Arabic, English and expat languages.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.