Building Invitovate: From a Spreadsheet Habit to a Multi-Tenant Product

The messy version, the parts that broke, and the boring engineering that made outreach trustworthy enough to run every day.

Invitovate started as a chore I kept doing badly. I was reaching out to people every day, and I was running the whole thing out of a spreadsheet and a wall of browser tabs. The messages were fine. The bookkeeping was the problem. I'd lose track of who I'd already contacted, forget who had replied, and have no idea which version of a message was actually working. So I built the thing I wanted, and it turned into a real product: Invitovate, a multi-tenant SaaS that other people now use too.

// The whole point in one sentence

The hard part of outreach isn't sending. It's remembering where every conversation stands. So I built a pipeline that can't quietly get that wrong.

01 Where it started

Every day I'd open the spreadsheet, scroll to find where I left off, and try to reconstruct the state of a few hundred conversations from memory and a color code. It worked until it didn't. The expensive mistakes were never the messages, they were the double-sends, the dropped follow-ups, and the total blindness to what was landing. None of that is a writing problem. It's a tracking problem, and tracking by hand is exactly the kind of work software should take off your plate.

02 The first version was a pile of files

The first build was a personal kit: a folder of scripts and JSON files that drafted messages and kept notes. It was great for exactly one user, me. The moment I imagined anyone else touching it, the whole thing fell over. There was no real notion of accounts, no isolation, and no source of truth that two people could trust at once. A pile of files is a prototype, not a product.

The lesson: "works on my machine, for me" is a fine place to start and a terrible place to stop. The jump from a script to a product is almost entirely about the boring guarantees: identity, isolation, and a state you can trust.

03 Going multi-tenant

So I rebuilt it on Postgres as a proper multi-tenant app. The core decision was to enforce isolation in the database itself with row-level security, not in application code where one forgotten WHERE clause leaks everyone's data. Each user only ever sees their own rows, the database refuses anything else, and signup is invite-gated so it's a controlled preview, not an open door.

The test I actually cared about wasn't a unit test. I made a second real account, logged in as that user, and tried to see the first account's pipeline. I couldn't. That was the moment it stopped being my script and started being something I'd let someone else put their work into.

04 A status log that can't lie

Here's the bug that changed how I built the rest of it. Early on, a contact's status could be overwritten to any value, and one day I watched a contact jump backwards in the funnel, from replied back to not-yet-contacted, because of an out-of-order update. In a spreadsheet you shrug and fix the cell. In a pipeline you're supposed to trust, a status that can silently move the wrong way is poison.

The fix wasn't to patch the bad row. It was to make the database refuse to do it. Status now lives in an append-only event log: you don't edit state, you add an event, and the current status is derived from the history. Then I added a trigger that blocks a contact from ever moving backwards in the funnel. The history became the truth, and the truth became impossible to quietly corrupt.

Why it matters: the whole promise of the tool is "you can trust where every conversation stands." A mutable status cell can't make that promise. An append-only log with a guardrail can.

05 Killing the channel that didn't work

Because every message is tagged and every outcome is logged, I could finally ask the tool a blunt question: is email actually doing anything? The answer was no. Across 88 email sends I had zero replies, while every real reply had come through a different channel. So I retired email. Not because I disliked it, but because the data said it was dead weight.

88 sends 0 replies so the channel got cut, on evidence

I also threw out a vanity metric in the same spirit. I used to quote a tidy "response rate" percentage, then realized it was flattering and unstable on small numbers. Now the tool shows raw counts: contacts, accepts, replies. Uglier, honest, and much harder to fool yourself with.

06 Any tool can drive it

I didn't want the pipeline welded to one interface. So every operation is a plain REST call. That one decision meant the same pipeline could be run three ways: by hand in the dashboard, by an agent or agentic IDE calling the endpoints, or by a small packaged app that runs the machine-side work in the background so I don't babysit a terminal.

  1. Manual first

    Everything works from the dashboard with a human clicking approve. That's the floor, and it never goes away.

  2. Agent-drivable

    Because it's all REST, any agent can find people and draft messages against the same contract. No special client required.

  3. Runs on its own

    A packaged background app supervises the machine work, so the boring parts happen without me sitting there.

The one line I never crossed: the AI drafts, a human approves the send. Finding and writing can be automated all day. Deciding to hit send on a message with my name on it stays a human call, on purpose.

07 Why I use it, and why you might

Invitovate doesn't make me type faster. It makes me stop forgetting. That sounds small until you've lost a week of momentum because you couldn't remember who you already talked to. The time it saves isn't in the writing, it's in never reconstructing state from scratch, never double-messaging someone, and never guessing which approach is working when the tool already knows.

If you run any kind of founder-led or personal outreach and you're doing it out of a spreadsheet, the pitch is simple: one calm pipeline, drafts in your own voice, and you approve every send. The parts that used to eat your attention just stop.

The best thing I built into it is the part nobody notices: a pipeline that can't quietly get your data wrong. Boring, and the whole point.
Postgres + RLS Event sourcing DB guardrails Per-variant analytics Human-approved sends
// See the product

Invitovate

A calm pipeline for outreach. Your own AI finds people and drafts in your voice; you send only what you approve. Live now.

Visit Invitovate →

↑ back to top

MORE NOTES

More on how things actually got built.

ALL POSTS