All work
Multi-tenant platforms · Live in production · 2026

Mortgage broker and client portal

Built for BridgeMortgage

A brokerage running on board software, a Word document and an inbox.

A mortgage brokerage tracked live deals on a board tool, sent its application form as a Word document by email, and collected client paperwork as attachments. We replaced all three with one portal: a six-step client journey, an application form inside it, and a document vault the broker can actually search.

313 live deals migrated into the new journey
54 application fields, off paper and into the portal
6 stages matching the real mortgage process

The problem

  • Deal pipeline on a per-seat board tool that held the broker's view and none of the client's.
  • The application form was a Word document, emailed both ways, then retyped by hand.
  • The first four-step journey did not match the real mortgage process, so the model was wrong.

What we built

  • A six-step client journey built from the broker's description of the real process, with the stage copy and the paperwork bound to each stage both editable at runtime rather than in code.
  • The application form moved inside the portal as a wizard, so answers land in the database validated instead of arriving as a document to be retyped.
  • A document vault laid out per client and per deal, encrypted at rest and virus-scanned on upload, with filenames on disk that carry no client information.
  • A broker pipeline over the same data, so the client view and the broker view are two readings of one record rather than two systems to reconcile.

Outcome

  • One system holds the deal, the application and the documents, so nothing is retyped between them.
  • Every existing deal was placed into the new journey by migration rather than by hand.
  • Per-seat cost is gone. Adding a broker or a client costs nothing, because there is nobody to pay per head.
  • The brokerage owns the server, the repository and the data, and can move all three without asking us.
Next.jsTypeScriptMySQLKnexRedisClamAVDockerCoolify

What something like this costs

It took 13 working days, counted out of the repository rather than estimated afterwards.

With us From $10,500to build, then $450/month
At a mid-market agency $15,600 to $20,800the same 13 days at $200/hr, billed at 6 to 8 hours a day

A rate comparison, not a bill. Our figure is the published ladder from the pricing page, for the closest match to this shape of work. Working days regenerate from the repository on every deploy.

The full write-up problem, constraints, and the whole debugging story. About a 4-minute read

01 The problem

The pipeline lived on a board tool, one card per deal, priced per seat. It held the broker's view of a deal and nothing of the client's. Everything the client had to supply arrived by email and was reconciled by hand.

The application itself was a Word document, emailed out and emailed back. Fifty-four fields, retyped into the pipeline by somebody every time, with no validation and no way to tell which client had answered what.

A first attempt modelled the client journey in four steps. The broker's reaction was that four steps is not how a mortgage works, which meant the data model was wrong rather than the copy.

02 Constraints

  • Real financial documents and identity paperwork, so storage had to be encrypted, virus-scanned and permission-checked rather than a shared folder.
  • Deals already in flight. A migration had to place every existing deal into the new journey without a broker sorting them by hand.
  • The people using the admin side are brokers, not operators. The internal UI had to hit the same bar as the client-facing one.
  • A returning client buying a second property must not end up with a duplicate login, and must not see the wrong deal.

03 What we built

  • A six-step client journey built from the broker's description of the real process, with the stage copy and the paperwork bound to each stage both editable at runtime rather than in code.
  • The application form moved inside the portal as a wizard, so answers land in the database validated instead of arriving as a document to be retyped.
  • A document vault laid out per client and per deal, encrypted at rest and virus-scanned on upload, with filenames on disk that carry no client information.
  • A broker pipeline over the same data, so the client view and the broker view are two readings of one record rather than two systems to reconcile.
  • An idempotent invite: a returning client gets a fresh sign-in link against their existing account, never a second login.
The hard part, in full

The switch that had to be off before it existed

The brokerage was mid-migration with real client records already loaded, and the instruction was unambiguous: no automatic email may reach an existing client. Not "we will turn it off before launch". Off, now, on production.

The obvious implementation is a settings flag defaulting to false. The trap is that a settings row which has never been written does not read as false. It reads as absent, and absent is not false unless somebody decides it is. A flag that defaults to off in code but reads null from an unwritten row is a flag that is off in the developer's head and undefined in production.

So the read path treats an absent row as off, deliberately, and the deployment carries no migration to create it. The setting is safe on production precisely because nothing had to run successfully for it to be safe. A missing row is the safe state rather than an unknown one.

Verifying it produced the more useful finding. The number everyone had been quoting for how many clients were reachable counted rows in the clients table. The number that matters counts clients with a portal login, because only those can be emailed at all. Those are different tables and, at that moment, very different numbers: one hundred and forty one against four.

Got something shaped like this?

Tell us what it is and what has stopped working. We will tell you what we would build.