TL;DR

  • We built our own CRM (React and Supabase, more than 20 shipped releases) around a cold-outreach theory of how JPL wins work. It ended up holding 282 prospect organizations and 72 contacts. The cold channel produced zero contracts.
  • Every engagement we actually closed traced back to a referral, a warm relationship or an existing client. Our pipeline is not a funnel. It is a network of 30 to 50 people.
  • So we stopped running relationships out of the app. They now live in plain markdown files, one per person (23 at the start, 38 six weeks later), plus a pass where an AI agent flags who is overdue and drafts the follow-up. A person sends every message. Nothing goes out on its own.
  • Six weeks in, the system has one outcome we can attribute to it: a stalled prospect thread that came back to life. The pass also exposed our own sloppiness more than once, and those failures are the most useful part of this note.
  • The honest limit: the cold campaign the list was built for never launched, so this does not show cold outreach is dead. It shows we had built the tool for a sales motion we do not run.

This is a field note about our own business development, not a client case. We sell digital growth work to Quebec small businesses, and we put real time into building software to manage a pipeline we turned out not to have. What follows is what the numbers said, what we replaced it with, and what six weeks of use has and has not shown.

What we built, and the theory behind it

The app was ContactHub: a React front end on Supabase, built in increments, with more than 20 releases shipped. It held organizations and contacts, took in leads through a webhook, checked each prospect’s website (status code, HTTPS, response time, page title) and could run email sequences. It was a good build. The problem was the premise. It assumed JPL grows by finding businesses it has never spoken to, qualifying them and working them through stages, the way a sales team with quota works a territory.

We loaded it the way that theory says to. By the end it held 282 prospect organizations and 72 named contacts, most of them assembled for a cold-calling campaign aimed at Quebec contractors. The last record was written on July 28, 2026.

Where did our contracts actually come from?

When we traced every engagement JPL closed back to its origin, none came from the cold list. They came from referrals, from relationships that were already warm, from demos given to people who already knew us, from maintenance takeovers, and from extra work for clients we already served. The cold channel’s count was zero.

The useful list was never the 282 organizations. It was 30 to 50 people: clients, the partners who send us work, the accountant and bookkeeper we ask for referrals, a few peers. A network that size does not need qualifying. It needs follow-up: answering the email that has sat for eight days, sending the follow-up we promised for October, checking in on someone who went quiet before the silence becomes the answer.

Is this an argument against cold outreach or CRMs?

The cold channel was never really tested. The calling campaign the list was assembled for never launched, so we cannot tell you cold outreach fails for a business like ours. What we can say is narrower: we built and maintained a tool for a sales motion we were not running, while the motion we were running had no tool at all.

This is not an argument against CRMs either. A team with several salespeople, a few hundred open deals and a forecast to defend needs pipeline stages, shared records and reporting, and the established CRMs do that well. The lesson is fit to motion. Ours is one senior operator and a few dozen relationships, and for that, a database with a kanban board was the wrong shape.

The app still runs and still holds the list. It is simply no longer where our relationships live.

What replaced it: one file per person

The replacement is a folder of markdown files on our own machine, one per person rather than per company, because relationships are with people and the company is just a field. Each file opens with a short header: what kind of relationship it is (client, prospect, referrer, partner, peer), how it started, how warm it is, the date of the last real exchange, the next date we committed to, the longest silence we accept, the preferred channel, and one line saying what is pending and on whose side. Below that, a log of touches, newest first, one line each.

Three rules carry most of the weight.

  • Lists are inventory, not relationships. A name on a purchased or scraped list does not get a file. It graduates into the ledger only when a real two-way conversation exists.
  • Nothing is sent automatically, ever. The agent drafts. A person reads, edits and sends.
  • Warmth is a human judgment. The pass reports silence. It never decides that a relationship has cooled.

There is no interface to build, no database to host and no deploy pipeline. The interface is the AI coding agent we already work in all day, which can read 38 short files in seconds. At this scale, an index would be overhead.

The pass: who is overdue, and what to say

The pass reads every file, checks the evidence, flags in a fixed priority order, and writes a draft for each flag.

Step two was not in the first design. It was added after the first week, for the reason below.

Six weeks of mileage

We meant the pass to run weekly. In practice it has produced three written reports in six weeks, run by hand, not on a schedule. Here is what each one found.

The first three relationship passes, from JPL’s own reports, August and September 2026. File counts are as each report logged them.
PassFiles readWhat it found
August 24, 2026 (first run)2315 relationships flagged (2 overdue commitments, 6 stale against cadence, 7 quiet threads); 7 follow-ups drafted
August 27, 2026 (rerun against client folders)22 (as logged)The August 24 run, on the ledger alone, had flagged 5 active relationships as stale; all 5 corrected from the client folders
September 17, 2026 (first run with the mailbox)3112 relationships needing action; 14 places where the ledger disagreed with the mailbox or the folders; 7 follow-ups drafted

The first run flagged 15 of 23 relationships. Some of that was real neglect, the kind that accumulates when nobody looks. Some of it was the ledger being wrong, and that is where the useful lessons came from.

The ledger lagged reality by up to three weeks. Exchanges got recorded in the client folders (a sent deliverable, an invoice, a note) and never made it into the ledger, so the first pass flagged five active relationships as stale. The fix was a rule, not more discipline: the ledger is an index, the client folders and the mailbox are the evidence, and when they disagree the latest evidence wins. The September pass added the mailbox as a third source, and since late September that check is a fixed step for every person before anyone gets flagged.

A draft is not a touch. The September pass found three messages that had been reported as prepared for sending and never appeared in the Sent folder, and three files whose last-exchange date was really the date a draft was written. On paper we were in touch. In the mailbox we were not.

The seeding missed a relationship. One prospect had agreed in July to hear from us again in October. That conversation ran on the mailbox of our previous domain name, which the seeding pass did not search, so the file did not exist. It was caught in early September and the follow-up went out on October 1, as promised.

All three were gaps in our own follow-up that predated the ledger. The ledger is just where they finally showed up.

The outcome we can attribute to it

One so far, and we are keeping its size honest. A prospect we had built preview material for in July had not replied since the end of that month, even after a follow-up in August. Checking the evidence showed part of the silence was ours: one of our July emails with the preview link had gone to our own address by mistake. Every pass kept the thread in front of us, and the September pass set a date to either revive it or close it consciously. At that date we sent a short check-in. The reply came the same day: they want to continue, they intend to pay for the work, and they will come back in a few weeks with one consolidated set of changes.

That is a revived thread, not a signed contract. It is also exactly what the old app was never going to produce, because the problem was never finding this person. It was remembering them.

What it costs, and what it does not do

The ledger has no pipeline stages, no forecast, no dashboards and no shared access for a team. It depends on an AI agent that can read local files and a mailbox, and on a person willing to read drafts before anything leaves. The files hold personal information (names, emails, relationship notes), so they live on our own machine rather than in a hosted app. The AI agent that reads them is still an outside service, though, and under Quebec’s Law 25, giving personal information to an outside provider comes with obligations. Leaving the hosted CRM narrowed that question. It did not remove it.

What it does well is narrow: it notices silence, and it puts a usable draft in front of us at the moment the silence starts to cost something. For a business whose growth runs through a few dozen people, that turns out to be most of the job.

Does a small service business need a CRM? Three questions first

Not always. It depends on how your work arrives and how many people your pipeline really is.

  • Where did your last five contracts come from? Trace each one to its first conversation. If most started with a referral or an existing client, your system should be built around those people, not around a list of strangers.
  • How many people is your real pipeline? If the answer is under a hundred, you may not need software at all. A file per person and a weekly look at who has gone quiet can carry it.
  • What actually gets dropped? For us it was the follow-up: the message promised for October, the email left unanswered for eight days. Whatever tool you choose, judge it on whether it catches that.

If you want this kind of setup in your own business (a simple system an AI agent keeps current, with a person in charge of every message), that is the work our automation practice does. We wrote about the broader idea in our post on AI automation for business operations.

We built a good CRM for a business we are not. The replacement is smaller, cheaper and closer to how our work actually arrives: through people we already know, as long as we remember to write back.