Founder & Lead Engineer, RAITHub
A maintenance request system for landlords needs five parts: a request with a priority, one owner at every step, a way to send the job to the right contractor, a status the tenant can see, and a timer that escalates stalled jobs. In PropDesk, tenants submit requests as Emergency, Urgent or Routine, landlords assign a contractor, and the contractor updates the job and confirms completion.
This guide is for founders and teams building maintenance tracking into a property management product. It draws on PropDesk, the property management platform RAITHub built for landlords with 1 to 50 units, with 4 roles (landlord, tenant, contractor and admin), a contractor portal, 5 daily automation jobs and 1,024 automated tests. In PropDesk the landlord chooses the contractor; the rule-based suggestions and escalation timers below are engineering guidance for taking that further, and are marked as such.
What does a maintenance request system need?
A record that moves between three people without getting lost. Most landlord maintenance problems are not repair problems; they are handover problems: the text that was never answered, the contractor who was never told, the tenant who never heard back.
| Part | What it does | In PropDesk |
|---|---|---|
| Request intake | Tenant describes the problem, picks a priority, adds photos | Tenant portal: submission by priority |
| Triage and assignment | Landlord confirms the priority and chooses who does the work | Landlord portal: maintenance assignment and contractor management |
| Contractor work order | Contractor is notified, sees what they need, updates status | Contractor portal: assignment notifications, job status, property and unit access details, completion confirmation |
| Tenant status | Tenant sees what is happening without phoning | Not itemised in the case study; show it on the tenant's request list |
| Housekeeping | Stale requests do not pile up and hide urgent ones | Daily job that closes unresolved low-priority requests after a configurable period |
How should maintenance priorities work?
Three levels are enough, and each one should carry a target response time the landlord sets. PropDesk uses Emergency, Urgent and Routine. The examples and targets below are illustrations, not rules; the right values depend on the property, the lease and local law.
| Priority | Example issues | Example target (landlord setting) | What the system does |
|---|---|---|---|
| Emergency | Gas smell, major leak, no heat in winter, a door that will not lock | Acknowledged within 1 hour | Notify the landlord at once on every channel; tell the tenant what to do right now, including when to call emergency services |
| Urgent | No hot water, a broken appliance, a toilet that will not flush in a one-bathroom unit | Contractor assigned within 24 hours | Notify the landlord; escalate if unassigned at the deadline |
| Routine | A dripping tap, a sticking drawer, a loose handle | Scheduled within 7 days | Queue for the landlord; eligible for auto-closure if nobody acts |
Let the tenant choose, and let the landlord change it with a reason recorded. Tenants will sometimes mark a dripping tap as an emergency; they will also sometimes under-rate a real one, which is why an emergency option must always be one tap away.
Deadlines have legal weight in some places. In California, for example, a tenant who repairs and deducts after the 30th day following notice "is presumed to have acted after a reasonable time", and that remedy is capped at one month's rent and at twice in any 12-month period (California Civil Code § 1942). That is general information, not legal advice; confirm the rules for each property with your adviser. For the software, the point is that the timestamp of notice matters, so record exactly when each request arrived and when each person saw it.
How do you route maintenance jobs to the right contractor?
Suggest, then let the landlord confirm. Match the job's trade to contractors who do that trade, who already work at that property, and who are not overloaded. In PropDesk the landlord assigns the contractor directly; a ranked suggestion list is the natural next step, because small landlords usually have a short list of trusted people and want to stay in control of who enters a tenant's home.
type Trade = 'plumbing' | 'electrical' | 'heating' | 'appliance' | 'general'
type Priority = 'emergency' | 'urgent' | 'routine'
interface Request { id: string; propertyId: string; trade: Trade; priority: Priority }
interface Contractor {
id: string
active: boolean
trades: Trade[]
propertyIds: string[] // properties this landlord has approved them for
openJobs: number
acceptsEmergencies: boolean
}
// Returns candidates best-first. The landlord confirms; nothing is auto-assigned.
export function suggestContractors(req: Request, contractors: Contractor[]): Contractor[] {
return contractors
.filter((c) => c.active && c.trades.includes(req.trade) && c.propertyIds.includes(req.propertyId))
.filter((c) => req.priority !== 'emergency' || c.acceptsEmergencies)
.sort((a, b) => a.openJobs - b.openJobs)
}
Three rules around that function:
- Scope contractors to a landlord. A contractor approved by one landlord must never appear in another landlord's list, or see their properties. That is tenant isolation, the same problem as in any multi-customer SaaS; the database-level version is in the Postgres row-level security guide.
- Record who assigned whom, and when. If a job goes wrong, the history is the first thing anyone asks for.
- Let a contractor decline. A declined job goes back to the landlord at once, not after the deadline passes.
What states should a maintenance request move through?
Few, with one owner each. PropDesk's flow is submitted by the tenant, assigned by the landlord, worked by the contractor and confirmed complete. Written as a state table:
| State | Owner | Moves to |
|---|---|---|
| Submitted | Landlord | Assigned, or Closed with a reason |
| Assigned | Contractor | In progress, or back to Submitted if declined |
| In progress | Contractor | Completed |
| Completed | Tenant or landlord | Closed when confirmed, or In progress if reopened |
| Closed | Nobody | Reopened as a new linked request if the problem returns |
"Owner" means the person the system waits on. If you cannot say who owns a request in a given state, that state will fill up with requests nobody touches. Enforce the transitions in one place on the server, so a contractor cannot close a job the tenant has not confirmed and a tenant cannot mark their own request assigned. The permission design behind that is in SaaS authorization and RBAC design.
How do you escalate a maintenance request that nobody acts on?
Store a deadline on every request from its priority, and run a job that finds requests past their deadline and escalates each one exactly once. This is engineering guidance; PropDesk's own scheduled housekeeping is the auto-closure of low-priority requests.
alter table maintenance_request
add column respond_by timestamptz, -- set from priority on insert
add column escalated_at timestamptz;
-- Run by a worker every few minutes. SKIP LOCKED lets two workers run
-- at once without escalating the same request twice.
with due as (
select id
from maintenance_request
where status in ('submitted', 'assigned')
and respond_by < now()
and escalated_at is null
order by respond_by
limit 50
for update skip locked
)
update maintenance_request m
set escalated_at = now()
from due
where m.id = due.id
returning m.id, m.priority;
PostgreSQL's documentation says rows that cannot be locked are skipped, which "can be used to avoid lock contention with multiple consumers accessing a queue-like table" (PostgreSQL: SELECT, the locking clause). The worker sends the escalation for each returned row; because escalated_at is set in the same statement, a second run finds nothing to do.
Auto-closure is the opposite case and needs the opposite care. PropDesk closes unresolved low-priority requests after a configurable period, which keeps the queue honest. Never let the same job touch Emergency or Urgent requests, and tell the tenant when a request is closed, with a one-tap way to reopen it.
What should a contractor see, and what should they not?
Exactly what they need to do the job, and nothing about money. PropDesk's contractor portal shows work order assignment notifications, job status tracking and updates, property and unit access details, and a completion confirmation workflow. It does not show rent, leases or tenant payments.
- Access details expire. Show entry instructions and door codes for assigned, open jobs only, and hide them when the job closes.
- Tenant contact is job-scoped. The contractor needs a way to arrange access, not the tenant's full profile.
- Completion needs evidence. A note and photos on completion give the landlord something to check and the tenant something to confirm.
How do you handle photos on maintenance requests safely?
Treat every upload as untrusted. A photo of a leak is the most useful thing a tenant can send, and a file upload is also one of the easiest ways into a web application. OWASP's guidance includes: "List allowed extensions", "Validate the file type, don't trust the Content-Type header as it can be spoofed", "Change the filename to something generated by the application" and "Set a file size limit" (OWASP File Upload Cheat Sheet). Store the files in cloud storage rather than on the app server, as PropDesk does, and serve them only to people who can see the request.
How do you test a maintenance request system?
Test the handovers and the clocks. PropDesk's 1,024 automated tests are 932 unit and 92 end to end, run in CI. For maintenance, these cases come first:
| Test case | What must hold |
|---|---|
| Tenant submits an Emergency request | Landlord notified at once; tenant sees emergency guidance |
| Urgent request passes its deadline unassigned | Escalated exactly once, even with two workers running |
| Auto-closure job runs | Closes only stale Routine requests; never Emergency or Urgent |
| Contractor declines a job | Back to the landlord at once, with the history kept |
| Contractor requests a lease, rent record or another landlord's property | 404 |
| Access details requested after the job closes | Not returned |
| Upload of a script renamed to .jpg, or a 200 MB file | Rejected |
| Tenant reopens a closed request | A new linked request, with the old history visible |
Why RAITHub for this, and when you don't need us
RAITHub built PropDesk's maintenance tracking across three portals: tenants submit by priority, landlords assign contractors, contractors update and confirm the work, and a daily job clears stale low-priority requests, inside a platform with 4 roles and 1,024 tests. Scheduling, state machines and background jobs of this kind are built under API and backend development as fixed-scope work, quoted in writing after a free 15-minute technical audit. You own the code.
You don't need RAITHub if:
- You are a landlord looking for maintenance tracking for your own units. Property management tools already include it; see property management software for small landlords.
- You need a contractor marketplace with vetting, insurance checks and payouts. That is a different product, with payment and verification work PropDesk does not prove.
- You need legal advice on repair deadlines. RAITHub builds the timers; the rules come from you and your adviser.
- You want developers placed in your team. RAITHub does not offer staff augmentation, and is not SOC 2 or ISO 27001 certified.
If maintenance tracking is part of the product you are building, book the free 15-minute technical audit with your roles, priorities and how contractors work with your landlords. The rest of the product is in property management software development, rent is in building online rent collection, and the PropTech page sets out what a build includes.
PostgreSQL, OWASP and California Civil Code sources checked on 29 September 2026.
Frequently asked questions
What should a maintenance request system for landlords include?
Tenant intake with a priority and photos, landlord triage and assignment, a contractor work order with status updates, a status the tenant can see, and escalation when a job stalls. PropDesk covers intake, assignment, contractor updates and completion confirmation.
What priority levels should maintenance requests have?
Three is enough. PropDesk uses Emergency, Urgent and Routine. Give each a target response time the landlord can configure, and record when the request arrived and who saw it.
How do you assign maintenance jobs to contractors automatically?
Suggest rather than assign: filter contractors by trade, by approval for that property and by emergency availability, rank by open jobs, and let the landlord confirm. In PropDesk the landlord assigns the contractor.
Should maintenance requests close automatically?
Only low-priority ones. PropDesk closes unresolved low-priority requests after a configurable period. Never auto-close Emergency or Urgent requests, and let the tenant reopen a closed request in one step.
What should a contractor portal show?
Assigned work orders, job status, property and unit access details, and a way to confirm completion. PropDesk's contractor portal shows these and nothing about rent, leases or tenant payments.
How do you stop the same overdue request being escalated twice?
Store an escalated-at timestamp and set it in the same statement that selects the overdue rows, using FOR UPDATE SKIP LOCKED so concurrent workers skip rows another worker holds.
Related posts
Ready to discuss your project?
Book a free 15-minute technical audit with our engineering team.