Back to BlogIndustry Guides

A Maintenance Request System That Routes Jobs to Contractors

Rupak Amin

Founder & Lead Engineer, RAITHub

11 min read

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.

PartWhat it doesIn PropDesk
Request intakeTenant describes the problem, picks a priority, adds photosTenant portal: submission by priority
Triage and assignmentLandlord confirms the priority and chooses who does the workLandlord portal: maintenance assignment and contractor management
Contractor work orderContractor is notified, sees what they need, updates statusContractor portal: assignment notifications, job status, property and unit access details, completion confirmation
Tenant statusTenant sees what is happening without phoningNot itemised in the case study; show it on the tenant's request list
HousekeepingStale requests do not pile up and hide urgent onesDaily 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.

PriorityExample issuesExample target (landlord setting)What the system does
EmergencyGas smell, major leak, no heat in winter, a door that will not lockAcknowledged within 1 hourNotify the landlord at once on every channel; tell the tenant what to do right now, including when to call emergency services
UrgentNo hot water, a broken appliance, a toilet that will not flush in a one-bathroom unitContractor assigned within 24 hoursNotify the landlord; escalate if unassigned at the deadline
RoutineA dripping tap, a sticking drawer, a loose handleScheduled within 7 daysQueue 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:

StateOwnerMoves to
SubmittedLandlordAssigned, or Closed with a reason
AssignedContractorIn progress, or back to Submitted if declined
In progressContractorCompleted
CompletedTenant or landlordClosed when confirmed, or In progress if reopened
ClosedNobodyReopened 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 caseWhat must hold
Tenant submits an Emergency requestLandlord notified at once; tenant sees emergency guidance
Urgent request passes its deadline unassignedEscalated exactly once, even with two workers running
Auto-closure job runsCloses only stale Routine requests; never Emergency or Urgent
Contractor declines a jobBack to the landlord at once, with the history kept
Contractor requests a lease, rent record or another landlord's property404
Access details requested after the job closesNot returned
Upload of a script renamed to .jpg, or a 200 MB fileRejected
Tenant reopens a closed requestA 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.

Maintenance request systemMaintenance trackingWork ordersContractor managementProperty management softwareLandlord softwarePropTech

Ready to discuss your project?

Book a free 15-minute technical audit with our engineering team.