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 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.
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.
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.
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.
- Manual first
Everything works from the dashboard with a human clicking approve. That's the floor, and it never goes away.
- Agent-drivable
Because it's all REST, any agent can find people and draft messages against the same contract. No special client required.
- 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.
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 →