Apartment hunting is a stateful workflow. At any given moment, a user might have 15 listings in various stages: some saved, some inquired about, some with scheduled viewings, some attended, some applied to, some rejected. Each has associated data: emails, notes, documents, times, decisions pending.
Modeling this well is a product design problem more than a technical one. But the technical decisions you make about state structure shape what the product can actually do.
This is what I've learned building the application tracker for HomeScout.
The state machine
The viewing/application workflow has a natural state machine structure:
Saved → Inquired → Viewing Scheduled → Attended → Applied → Outcome
↓ ↓
Not Interested Accepted / Rejected
With additional states like "Rescheduled" (a substate of "Viewing Scheduled") and "Waiting for references" (a substate of "Applied").
The question is how much of this to expose to the user. Early versions exposed the full pipeline with all substates. Users found it overwhelming. They didn't want to maintain an accurate state machine; they wanted a rough picture of where things stood.
Current version has seven states: Saved, Contacted, Viewing Scheduled, Viewed, Applied, Offer, Closed (rejected/accepted/withdrawn). That's enough structure to give an overview without requiring micro-updates to every state transition.
Data model decisions that matter
Each tracked listing needs:
- Reference to the canonical listing (price, location, photos, specs)
- Viewing datetime (nullable, set when scheduled)
- User notes (free text, the most-used field)
- Status enum
- Last-action timestamp (for sorting the list by recency of activity)
- Reminder datetime (nullable, for follow-up nudges)
The canonical listing reference is tricky. Listings on rental portals disappear when let. If a user is tracking an application and the listing goes dead, you need to have captured the listing data rather than holding a reference to a URL that will 404. (Most renters who are tracking 10+ properties simultaneously are also dealing with the speed problem — setting up real-time alerts is what keeps the pipeline from going stale before you can even schedule viewings.)
We snapshot the listing data at the point of save: title, price, address, description, photos (or at least photo URLs, with the understanding they may eventually 404). This creates storage overhead but is necessary for the tracker to remain useful after a listing closes.
The alternative (storing only a URL) fails silently in the worst way: the user's tracker looks fine but clicking through gives them nothing, exactly when they're trying to reference details before an interview or application.
The calendar view problem
Showing upcoming viewings in a calendar seems straightforward. The complications:
Timezone. Users searching from abroad (a meaningful chunk of our user base) may have their device in a different timezone than Dublin. We store viewing datetimes in UTC with Ireland timezone display. We show the timezone explicitly in the viewing confirmation so remote users know what "Thursday 2pm" means in their local time.
Conflict detection. Users sometimes accidentally schedule two viewings at the same time, or with insufficient travel time between them. We flag conflicts (exact time overlap) but don't try to flag "you've booked two viewings 15 minutes apart and they're across the city." That requires knowing travel time between the two viewing addresses, which we could calculate but haven't yet. Simple conflict detection catches the obvious case.
Duration assumption. We default to 30 minutes per viewing block in the calendar. Most viewings are shorter, some run longer. We let users edit duration but most don't. The 30-minute default gives enough visual separation in the calendar to make conflicts obvious.
Notes: the underrated feature
Post-viewing notes are the feature users engage with most, measured by time spent interacting. They enter them right after a viewing (sometimes before they've left the building) and come back to them when making a decision a week later.
The design decisions that made this work better:
No formatting. Early version had a rich text editor. Users wanted to type fast and didn't want to think about formatting. Plain text input with newlines is what they actually need.
Persistent draft. If a user starts typing and closes the app, the draft is saved. Property viewings happen in the real world; people are typing on their phone and getting interrupted. Losing notes because the session closed is the worst outcome for a feature whose value is capturing transient impressions.
Timestamps on notes. Each note shows when it was added. This matters when a user has multiple notes on a property across different visits or different points in the application process.
The reminder problem: where we're at
Follow-up timing is the biggest behavioral lever in rental searching. The window between "attended viewing" and "property offered to someone else" is often 24-48 hours. Users who follow up in that window have much better outcomes than users who follow up on day 3.
We know this. Our reminder system is still too manual. The right behavior is: status transitions to "Attended + Interested" trigger an automatic 24-hour reminder. We have the data model for reminders. We have the user intent signal (the "Interested" flag). The gap is that we haven't wired the automatic trigger.
The reason it's not done yet is partly scope and partly that automatic reminders require notification permissions, which add friction to onboarding. We optimized onboarding for low friction early on. Notification permission prompts add a step that some users drop off at.
The resolution is probably to ask for notification permission at the point a user schedules their first viewing, when the value prop ("get reminded about this viewing") is concrete. Not at onboarding when it's abstract. That's the standard pattern for permission request timing and it applies here.
What the state machine doesn't model well
Applications are more document-intensive than viewings. For a formal application you might submit a reference letter, an employment letter, 3 months of bank statements, proof of current address, and a photo ID. Tracking which documents you've gathered, which properties you've submitted to, and whether the landlord has confirmed receipt is its own workflow.
We have basic document tracking (checkboxes for standard document types) but it's not well integrated with the per-property status. This is a real gap. The users who manage applications to multiple properties simultaneously are the ones who most need this, and they're also the users with the most acute pain if they forget to submit something.
It's also a potentially sensitive feature: these are financial and identity documents. Any feature that involves storing or transmitting them needs to be handled carefully. We currently don't store documents, just track whether you've gathered them. That's the right call for now.
The full application management problem, including document collection and submission, is probably a separate product surface from viewing management. They're related workflows but distinct enough that bundling them into one view creates cognitive load.
I mapped out the whole viewing logistics problem from the user side here: https://homescout.io/guide/managing-rental-viewings-dublin-without-losing-mind
Caspar Bannink. Founder of HomeScout.io. Building AI-powered rental search for Dublin.
Top comments (0)