writing · work
zown
Preface
zown is a digital real estate platform in canada aimed at making home buying more accessible by returning part of the commission to buyers.
Overview
i spent my time at zown as the fifth engineer, working on zoro (the ai realtor), an api that serves home data, and hubspot.
they say you can never find the same person twice (even in the same person). that's how i felt after my stint at zown, and how i feel writing this now, heading back to school.
first impressions
on my first day, i didn't really know what to expect. i had been around friends who worked full time around the world, who told me about the culture they dealt with and experience. however, in my mind, i had one goal, result oriented engineering. i knew from past experiences that i didn't want to get caught in the "barbwire" of busywork, so i immediately started asking about bottlenecks and tried identifying which giant i could take down. my role was never concretely defined by an outcome, however, the accountability was. in the first week, i had already started debating and questioning the way the processes were being used right now. obviously, i don't know everything, but it is imperative that i assume i can do anything. that led to some fun and interesting arguments i took part in.
out of the several problems and decisions i owned in my time at zown, there are three big ones that i'll be covering in this piece.
focus 1: zoro
The most interesting problem I got to work on at Zown started with Ibrahim Awad and a question: how would an AI realtor actually work? Buying a home is one of the biggest decisions someone makes, so handing the whole thing to an agent felt wrong. Instead we took a realtor’s tasks one at a time: the CMA, response times, follow-ups, listing alerts, the questions that came in at odd hours that our realtors couldn’t answer. But we knew there always has to be a human in the loop, and in this case, a realtor in the loop.
from a sales tool to something that had to ship
This project didn’t start as a consumer chatbot. It started as an internal sales tool, so account managers’ chats were taken care of while they were away. Most of this happened in WhatsApp groups, where realtors were answering chats and updating HubSpot by hand. At first we had Zoro announce himself: “I’m [agent name]’s assistant!”, even though the messages came from the same number as the realtor.
We built the agent in Python + LangChain first. After the first version, I went AM by AM (account manager by account manager), and we deployed it to 12 chats and collected feedback on performance, effectiveness, and style. We decided to switch over to TypeScript and Mastra early, because it scales and we saw how fast the tool was being adopted. The difficulty wasn’t really the tech. It was more the willingness of the account managers to use the tool.
At first there was a lot of chatter about the tool replacing them, when in fact the goal was to increase their throughput. I took every account manager out for coffee and talked through their concerns and what this could do for them. Their incentive: if it closed even two more deals for them, that was more in commissions. Looking back, I don’t even know why I did this, but it felt like the right thing to do, and in hindsight it was the smoothest turnaround I’ve had in a while.
The LangChain version leaned on a lot of quality checks and confidence scoring, because we were asking the model to come up with the answer itself, and then had to check whether it was right. In the rewrite I cut most of those extra checks. Instead of asking the model to generate a solution, we designed tools for it: pull the listing, run the CMA, look up the buyer’s stage. The model picks the right tool, and the tool gives a correct answer. The shape stayed simple: check the message coming in, let the model think and use tools, check the reply going out.
We created a dev environment with demo listings instead of the full GTA database, which was constantly updating, and set up a test WhatsApp number. Then we put the bot in a container on Fargate. Its files (the knowledge base, CMA PDFs) sit in storage, and follow-ups, reminders and listing alerts run on cron jobs. The old Python service stayed up on EC2 beside the new one for a long time. Moving WhatsApp over was one setting in Maytapi, the service that connects us to WhatsApp, so moving back was the same setting.
market strategies and common tactics
One of the most annoying problems wasn’t the logic. It was picking up on the strategies people tend to use when they list.
The first was driving a bid. A lot of sellers list a home ridiculously lower than it’s actually worth, so they get lots of offers. Then they drive the price up by telling one buyer they have seven offers, but for them they’ll close ASAP.
The second was stagnant. They would just list and wait until they got what they wanted.
There were a lot more of the first. The solution we found was to look at the history of how many times a property was listed. If it was listed more than once, we compared the pricing and ran an LLM call on the changes, with the different strategies as context.
I feel like in this market, where AI is so hyped up, there’s a lot of focus on slapping an LLM call anywhere and everywhere. Instead of delivering facts, we’re replacing them with generated ones, and that’s resulting in bad harnesses.
mobile enhancements
A couple of weeks before the launch, my priorities shifted heavily: not only serving the AI reliably through the app, but also making the experience an eleven star one.
One thing I noticed was how our output was structured. Each answer had two parts: listings processed through our app, and a reply to the user’s question. At first we waited for both to finish before sending anything. That took almost 28 seconds, which was annoying, because the LLM’s reply was already done. So I enabled SSE (server-sent events) and split the response in two. The reply arrives first, the listing cards show a blank state, and the cards fill in shortly after.
I also created assets that redirected from the chat to the actual homes. Messing around with micro-interactions in the chat was enjoyable too, like being able to drag a suggested listing and drop it into the chat.
focus 2: api
what good is an agent without data
Underneath everything was the data: an API that serves home data. Live search and filters, photos, property history, analytics, price estimates, alerts, and webhooks.
We used Repliers at the start, but we needed more: schools, neighbourhood scores, air quality and a lot else. So we started building our own API.
Repliers stayed as the live MLS. In a conversation fresh data matters, so sold history and photo search still go there. Everything the buyer sees in the app (the map, the filters, the photos) moved to us: board feeds come in, get cleaned up, and land in Postgres. The listings API reads only from that database and never touches Repliers. On the agent side, every MLS request goes through one connector, so switching providers one day means changing one file. When Zoro searches, he asks Repliers first and tops up from our database if results are thin.
For walkability, I used Apple Maps to understand what’s around each home. Every address gets pinned on the map when it comes in, and a sweep with OpenStreetMap fills a catalog of nearby places like malls and schools, so we know how far each home is from them. When a listing page asks what’s nearby, we don’t call Apple again. We ask our database (PostGIS) what’s within a kilometre of a point we already stored.
For schools, I fetched EQAO scores for each school, along with Ontario school lists, Fraser rankings and catchment maps. For neighbourhoods, we pulled in reports the government publishes online. We used to lean on a third party to tell us what a neighbourhood was like. We replaced that with our own data: census, climate, air quality, walk and transit scores, market stats, schools. It refreshes on a schedule, with a lock so two jobs never scrape the province twice.
Zoro doesn’t read that whole pile. He makes one lookup against a summary table (a materialized view): the home and everything around it, already joined, in a shape he can say out loud. That was the point of all the plumbing. Zoro could only know what he was talking about if the answer arrived pre-chewed.
Price estimates split the same way. The listing page gets an automated estimate from the API. In chat, Zoro builds a CMA from live comparable sales plus our own math. Alerts come from saved searches and from price changes we actually saw come in. History is a tool Zoro can call, not every MLS record dumped into the prompt.
A lot of local data, one door for the brain. That’s the unglamorous half of “we gave it tools to connect to the real estate board.”
focus 3: hubspot
I think the first time I got access to HubSpot at Zown, I almost shed a tear. Not because it was beautiful.
For Zoro to know where a customer stood, the data had to be clean enough to trust. It wasn’t. Every record had 700+ properties and was tangled in multiple workflows. The advice I got was to go through it by hand.
This task initially seemed like a huge mess that would take days, maybe weeks, to unravel, and everyone dreaded the thought of taking it on. Being claudepilled helped a lot here: I knew a lot of the manual labour could be eliminated easily. Dr. Awad and I took a bomboclaude of agents and went through the properties and processes one by one. Within a week and a half, we had redone the whole CRM and made the processes leaner and more efficient.
The most enjoyable part was not unweeding the CRM. It was introducing Omar to Claude and Claude Code, and watching him fall in love with using MCPs in real time. Back in April, we started with a special app inside HubSpot, because HubSpot was only just starting to release its MCP.
We wrote a list of approved fields, so the website form and the mobile app send exactly the same data. Some forms called the same field cid and others zcid, so we treated them as one, because HubSpot will happily reject you for a duplicate. Outgoing updates wait in a queue, so a flaky request can’t create two leads. Each buyer gets one qualification lead, and account managers own the pipeline after that. Editing a form gets a different note title from submitting it, so a workflow that calls people on “Form Submission” doesn’t ring someone again because they fixed a typo.
Coming the other way, we listened for deal stage changes and saved the stage name exactly as written. We ignored the echo when HubSpot told us about a deal we’d just created ourselves. And we never let the chat app set a stage. If you could send HIGHLY_QUALIFIED from the chat, you could talk the bot past every rule in the table.
Admin CRM, our ops tool, doesn’t talk to HubSpot at all. Two systems, side by side, on purpose. Keeping them in sync was a problem we chose not to have.
farewell
i honestly couldn’t have asked for a better summer in toronto, thanks to rishard, omar, hamza, and everyone on the zown team who made this not just an experience but a memory.