PropTech
Facility management
Outsourcing development
Turn free-text resident requests into structured, work-order-ready tickets without moving any decision away from the staff accountable for it.
category groups
priority levels
emergency types
Residents report maintenance issues the way people describe problems: vaguely, at the wrong level of detail, and sometimes three at once. A resident payments and communications platform used by US property management companies needed those messages to become structured, work-order-ready tickets.
The messages came through the same channel residents used to reach their property manager. Until then, most of the work happened in conversation, while the platform received little more than a stub.
We built an intake layer that classifies requests against a closed list of 12 category groups and 4 priority levels. It also applies per-company rules with documented fallbacks and guides residents through a conversation that produces a ticket draft for confirmation.
The boundary that made this safe to ship was simple: the model provides signals, not decisions. Every threshold that turns a signal into an action stays in the client’s application layer.
A value that is wrong can be corrected. An action that is wrong may have already sent a truck.
Most maintenance work happened in conversation, and almost none of it reached the system.
Property managers resolve maintenance the way they resolve many operational issues: by talking to people. A call, a message, a conversation in the hallway. The problem gets diagnosed, a technician gets sent, and the resident gets an answer.
What lands in the platform is a stub: a line of text, a status, a date.
Three problems follow, and they compound.
The record is thin. Every recurrence starts from zero. The next time the same unit reports the same symptom, nothing in the system says what was found last time or what fixed it. The knowledge stays with the manager who took the call.
Managers spend time eliciting details. What exactly is leaking? Since when? Has anyone looked at it? When can someone enter the unit?
Each question creates another round trip, even when the resident has already provided the answer in conversation.
The operation is hard to measure. Without a structured record of what was reported, decided, and done, neither the platform nor the property owner can see what maintenance actually costs or where the process slows down.
Written requests created another layer of manual work. Someone had to read each message and turn it into a category, a priority, and a judgment about whether a technician was needed.
The same problem could land in different queues on different days. Queue order stopped carrying useful information.
Urgency was often decided by tone. The loudest message moved first.
Emotional urgency and technical severity are different things. Confusing them means a politely worded gas smell can wait behind an angry complaint about a paint scuff.
Tickets also arrived incomplete. Location, duration, prior attempts, and contact preferences were often missing. The first action was another message to the resident, which could add another day before dispatch.
Non-maintenance requests consumed the same queue. Billing questions, lease renewals, and reference letters came through the same door and had to be pulled back out manually.
Finally, each property management company worked differently, but the platform had no practical way to encode those differences.
The rules that made each company’s process its own lived in people’s heads and in documents that were never written to be machine-readable. Treating every company the same way put a ceiling on how many the platform could serve well.
We designed the intake layer around one constraint: make the process more structured without moving operational decisions into the model. The intake layer combines structured classification, conversational intake, and client-controlled rules.

An intake assistant reads the subject and description of a request and returns structured output.
That includes suggested categories from a fixed list of 12 category groups, a suggested priority from 4 levels, a factual summary for internal review, a maintenance/non-maintenance verdict, a drafted acknowledgment, a sentiment signal, and an explicit confidence level.
Two rules did most of the work.
The category list is closed. The model cannot invent a category. When nothing fits, it returns General Maintenance: Other rather than a plausible-sounding new label. A new label would be invisible to downstream rules built around the existing taxonomy.
When several issues appear in one message, the highest priority prevails. Priorities are evaluated per issue and never averaged. A leaking faucet described alongside a gas smell therefore does not get averaged down to Medium.
A chat service gathers what a work order needs through a conversation: issue, location, duration, impact, prior attempts, and contact preference.
It skips anything already known from session context instead of asking twice. The result is a draft the resident confirms before the ticket enters the queue.
The point is not a nicer form. The conversation itself becomes the record.
Questions a manager used to ask on the phone are asked once, in a channel that captures the answers as fields rather than leaving them as recollection. The elicitation still happens. It simply stops being work that leaves no trace.

Three behaviors matter most.
Six emergency types are escalated immediately: flood, fire, gas leak, no heat in winter, security breach, and electrical hazard. Each has its own safety instructions, and no further intake questions are asked.
Requests that are not maintenance are classified as such and redirected to the appropriate channel. They never enter the maintenance flow.
Ambiguity is surfaced rather than resolved by guesswork. “My apartment is too loud” could describe a neighbor or a failing HVAC unit. The assistant asks before classifying and is prohibited from guessing.
The client needed to operate the system without calling engineering for every change. Knowledge-base management is therefore part of the delivery.
The API supports uploads of policy, FAQ, template, and troubleshooting documents through presigned URLs. It also supports per-company metadata tagging and a sync operation with observable status.
Acceptance was reviewed through a non-production demo interface with six scenarios. Reviewers could click through the system rather than read a specification.
The model returns signals. It never makes decisions.
It does not create tickets or work orders, execute workflows, or act on its own confidence. Confidence and sentiment are emitted as values. Thresholds that determine what happens at each value belong to the client’s application layer.
Ticket creation in the system of record, resident authentication, the portal experience, and the operational emergency response all stayed on the client’s side.
This is why sentiment is deliberately kept out of the operational path.
In our specification, it is informational only. It cannot influence category, priority, escalation, or acknowledgment wording.
Emergency classification runs on factual content, never emotional tone. A calmly described gas smell is an emergency. An angry message about a scratched cabinet is not.
The reason is accountability. Once a model’s interpretation of someone’s mood can move a queue, the operator loses the ability to explain why one job was dispatched before another. That matters to residents, owners, and regulators.
Keeping sentiment out of the decision path makes the rest of the output safer to automate against.

Two structural choices followed from the same principle.
Conversation state lives in the agent’s own session, while persistent storage holds only structured drafts. There is no second copy of conversation history to secure, reconcile, or expire.
The draft state is read from storage rather than parsed from the model’s text. This keeps the API contract stable when prompts change.
The service runs on AWS Bedrock with a Python service layer around it, inside the client’s own AWS account.
Resident conversations never traverse our infrastructure. Messages to property managers can contain apartment numbers, access arrangements, and occasionally much more.
They stay in the account the client already governs, under the controls it already operates. We do not create another place where that data lives.
The model can also be replaced without changing the product or workflow. It sits behind an interface that returns values.
Swapping it for another model, provider, or tuned version changes what arrives in those fields, not what the platform does with them. On technology that changes every few months, that is the difference between an upgrade and a migration.
See how a property management platform was strengthened for reliable growth, including the engineering choices behind its performance and maintainability.
The per-company layer assumed two things the client turned out not to have in reusable form: an agreed set of contextual property fields and each company’s operating policies in a machine-readable format.
Discovery established that neither existed in sufficient structure.
Two usual responses were available. We could stall until the client produced them or build against assumptions and discover the gap at acceptance.
We did neither.
The scope changed through a formal change request. We built the layer that can consume those materials when they become available.
That includes retrieval with per-company metadata filtering, standard rules applied alongside company-specific exceptions where available, documented fallback to standard handling where they are absent, and output that states which rules and inputs were actually used.
The platform can now start with lightweight company-specific inputs and deepen them from real operating practice. It does not have to wait for a complete playbook that, in many cases, is never written.
You do not need your operating rules fully documented before starting.
Most platforms discover halfway through that the rules live in people’s heads. The project then stalls while everyone waits for a document nobody has time to produce.
This layer runs on standard handling from day one. It can absorb company-specific material later, one company at a time. The output also states which rules it used.
The re-scope was possible because the work was shipped in stages. Each stage stood on its own, so the change affected one stage rather than the whole engagement.
The result was a more consistent intake process without changing who owns the operational decisions. The main changes were visible in the quality of the record, the routing of requests, and the boundary between AI output and client action.
What changed
What it means operationally
What used to be elicited by phone is captured once, in a channel that writes it down. The unit’s history no longer lives in one person’s memory.
Closed category list, four priority levels, explicit confidence, and a stated maintenance/non-maintenance verdict.
Emergencies are identified from what the resident describes, not how upset they sound.
Requests are redirected instead of being pulled back out of the queue later.
Company materials shape follow-up questions and wording where they exist, with documented fallback where they do not.
Company-specific or standard, auditable without instrumenting the model.
The service runs in the client’s own AWS account. Conversations do not pass through our infrastructure.
Thresholds, ticket creation, and operational response remain on the client’s side.
Three principles from this project apply to any team putting a language model in front of an operational queue.
Draw the line at decisions, not capability.
The useful question is not how much the model can do. It is which outputs are values and which are actions.
Values can be wrong and corrected. Actions leave the system, and correcting them can cost a dispatched truck.
Keep the taxonomy closed.
A model that may invent a category will invent one. Every rule written against the old list can then silently stop matching.
A closed list with an explicit Other is more useful than a richer taxonomy the model can extend.
Do not act on sentiment.
It is the most tempting signal and the least defensible one.
The moment a mood score can reorder a queue, nobody can clearly explain the order. In maintenance, that is exactly what someone will eventually be asked to justify.
Book a short scoping call. We’ll explain how to prepare an anonymized sample of maintenance requests for an initial taxonomy review.
We will run them against a closed taxonomy and come back with three things: where the categories land cleanly, where they collide, and which requests are not maintenance at all.
That takes a few days and costs you nothing. You will see what your own intake looks like as structured data before deciding whether to build anything.
And if the answer is that you do not need this yet, that is a useful answer too.
Two related perspectives on building and connecting property-management platforms.