Building a Move-In/Move-Out Agent: Coordinating Transitions with AI
Build an AI agent that automates the move-in and move-out process, including checklist management, utility coordination, key tracking, and security deposit processing.
The Move-In/Move-Out Coordination Problem
A single unit turnover involves 15-20 discrete tasks: collecting keys, inspecting the unit, processing deposits, coordinating cleaning, transferring utilities, and communicating with both the departing and arriving tenant. Property managers juggle multiple turnovers simultaneously, and missed steps lead to delays, disputes, and lost revenue. An AI agent orchestrates this entire workflow.
Modeling the Transition Process
We define the turnover as a state machine with clear phases and dependencies.
from dataclasses import dataclass, field
from datetime import date, timedelta
from enum import Enum
from typing import Optional
class TransitionPhase(Enum):
NOTICE_RECEIVED = "notice_received"
PRE_MOVEOUT = "pre_moveout"
MOVEOUT_DAY = "moveout_day"
UNIT_TURNOVER = "unit_turnover"
PRE_MOVEIN = "pre_movein"
MOVEIN_DAY = "movein_day"
COMPLETED = "completed"
@dataclass
class TransitionTask:
task_id: str
name: str
phase: TransitionPhase
assigned_to: str # tenant, manager, vendor
due_date: date
completed: bool = False
depends_on: list[str] = field(default_factory=list)
notes: str = ""
@dataclass
class UnitTransition:
transition_id: str
unit: str
departing_tenant: Optional[str]
arriving_tenant: Optional[str]
moveout_date: date
movein_date: date
current_phase: TransitionPhase
tasks: list[TransitionTask] = field(default_factory=list)
Generating the Task Checklist
Each transition gets a customized checklist based on the situation.
def generate_transition_tasks(
transition: UnitTransition,
) -> list[TransitionTask]:
"""Generate all tasks for a unit transition."""
tasks = []
mo = transition.moveout_date
mi = transition.movein_date
# Pre move-out tasks (assigned to departing tenant)
if transition.departing_tenant:
tasks.extend([
TransitionTask(
task_id="mo_01", name="Submit forwarding address",
phase=TransitionPhase.PRE_MOVEOUT,
assigned_to="tenant", due_date=mo - timedelta(days=14),
),
TransitionTask(
task_id="mo_02", name="Schedule utility disconnection",
phase=TransitionPhase.PRE_MOVEOUT,
assigned_to="tenant", due_date=mo - timedelta(days=7),
),
TransitionTask(
task_id="mo_03", name="Return all keys and access devices",
phase=TransitionPhase.MOVEOUT_DAY,
assigned_to="tenant", due_date=mo,
),
TransitionTask(
task_id="mo_04", name="Move-out inspection",
phase=TransitionPhase.MOVEOUT_DAY,
assigned_to="manager", due_date=mo,
depends_on=["mo_03"],
),
TransitionTask(
task_id="mo_05", name="Process security deposit",
phase=TransitionPhase.MOVEOUT_DAY,
assigned_to="manager", due_date=mo + timedelta(days=21),
depends_on=["mo_04"],
),
])
# Unit turnover tasks
tasks.extend([
TransitionTask(
task_id="to_01", name="Professional cleaning",
phase=TransitionPhase.UNIT_TURNOVER,
assigned_to="vendor", due_date=mo + timedelta(days=2),
depends_on=["mo_04"] if transition.departing_tenant else [],
),
TransitionTask(
task_id="to_02", name="Maintenance repairs",
phase=TransitionPhase.UNIT_TURNOVER,
assigned_to="vendor", due_date=mo + timedelta(days=5),
depends_on=["mo_04"] if transition.departing_tenant else [],
),
TransitionTask(
task_id="to_03", name="Paint touch-up",
phase=TransitionPhase.UNIT_TURNOVER,
assigned_to="vendor", due_date=mi - timedelta(days=5),
depends_on=["to_01"],
),
])
# Pre move-in tasks
if transition.arriving_tenant:
tasks.extend([
TransitionTask(
task_id="mi_01", name="Move-in inspection",
phase=TransitionPhase.PRE_MOVEIN,
assigned_to="manager", due_date=mi - timedelta(days=1),
depends_on=["to_03"],
),
TransitionTask(
task_id="mi_02", name="Prepare key packets",
phase=TransitionPhase.PRE_MOVEIN,
assigned_to="manager", due_date=mi - timedelta(days=1),
),
TransitionTask(
task_id="mi_03", name="Key handoff and welcome",
phase=TransitionPhase.MOVEIN_DAY,
assigned_to="manager", due_date=mi,
depends_on=["mi_01", "mi_02"],
),
])
return tasks
Security Deposit Processing
The deposit tool compares inspection reports and calculates deductions.
See AI Voice Agents Handle Real Calls
Book a free demo or calculate how much you can save with AI voice automation.
@dataclass
class DepositDeduction:
item: str
amount: float
reason: str
def process_security_deposit(
deposit_amount: float,
inspection_damages: list[dict],
normal_wear_items: list[str],
) -> dict:
"""Calculate security deposit return after deductions."""
deductions = []
for damage in inspection_damages:
if damage["area"] not in normal_wear_items:
deductions.append(DepositDeduction(
item=damage["area"],
amount=damage["repair_cost"],
reason=damage["description"],
))
total_deductions = sum(d.amount for d in deductions)
refund = max(0, deposit_amount - total_deductions)
return {
"original_deposit": deposit_amount,
"deductions": [
{"item": d.item, "amount": d.amount, "reason": d.reason}
for d in deductions
],
"total_deductions": total_deductions,
"refund_amount": refund,
}
The Transition Agent
from agents import Agent, function_tool
@function_tool
async def get_transition_status(unit: str) -> str:
"""Get the current status of a unit transition."""
return (
f"Unit {unit} transition status: UNIT_TURNOVER phase\n"
f"Completed: 5/12 tasks\n"
f"Next due: Professional cleaning (tomorrow, assigned to CleanCo)\n"
f"Blockers: None"
)
@function_tool
async def complete_task(transition_id: str, task_id: str) -> str:
"""Mark a transition task as completed."""
return f"Task {task_id} marked complete. Dependent tasks are now unblocked."
@function_tool
async def send_tenant_reminder(
tenant_id: str,
message_type: str,
) -> str:
"""Send a reminder to a tenant about upcoming transition tasks."""
templates = {
"key_return": "Reminder: Please return all keys to the office by your move-out date.",
"utility_transfer": "Reminder: Schedule your utility disconnection at least 7 days before move-out.",
"forwarding_address": "Please submit your forwarding address for deposit return.",
}
msg = templates.get(message_type, "Please contact the office for details.")
return f"Reminder sent to tenant: {msg}"
@function_tool
async def calculate_deposit_return(
unit: str,
deposit_amount: float,
) -> str:
"""Calculate and generate the security deposit return statement."""
result = process_security_deposit(
deposit_amount=deposit_amount,
inspection_damages=[
{"area": "Kitchen faucet", "repair_cost": 150, "description": "Handle broken"},
],
normal_wear_items=["carpet wear", "paint fading"],
)
return (
f"Deposit: ${result['original_deposit']:,.2f}\n"
f"Deductions: ${result['total_deductions']:,.2f}\n"
f"Refund: ${result['refund_amount']:,.2f}"
)
transition_agent = Agent(
name="MoveInMoveOutAgent",
instructions="""You are a unit transition coordinator.
Track move-in/move-out tasks, send reminders, coordinate
vendors, and process security deposits. Always ensure
deposit returns comply with state timelines.""",
tools=[
get_transition_status, complete_task,
send_tenant_reminder, calculate_deposit_return,
],
)
FAQ
How does the agent handle overlapping move-out and move-in dates?
The task dependency system prevents move-in tasks from starting before move-out tasks complete. If the timeline is too tight (e.g., same-day turnover), the agent flags it and recommends extending the gap or scheduling express cleaning services.
What happens if a vendor misses their scheduled task?
The agent monitors task completion deadlines. When a vendor task passes its due date without being marked complete, it sends an alert to the property manager and suggests rebooking with a backup vendor. Dependent tasks are automatically rescheduled.
How are security deposit disputes handled?
The agent generates an itemized deduction statement with photo evidence from inspections. If a tenant disputes a charge, the agent pulls the move-in and move-out inspection photos for that specific item, providing objective comparison. Final dispute resolution still involves human judgment.
#MoveInMoveOut #PropertyManagement #WorkflowAutomation #Python #AgenticAI #LearnAI #AIEngineering
CallSphere Team
Expert insights on AI voice agents and customer communication automation.
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.