Inspiration

I am a solo founder, and I kept running into the same problem: I could build the product, but once it was time to market it, I got stuck.

I was working on another project and reached the point where I needed customers. I knew exactly what I needed to do: find the right companies, identify the right people, research them, write personalized outreach, send it, follow up, and manage the replies.

But doing all of that alone is a completely different job.

I started building a small internal tool to make that process easier for myself. It began as something I wanted to use to market my own product. But as I built it, I realized I wasn't solving a one-off problem. I was building the marketing team I wished I had.

That internal tool became Crabpitch.

The idea is simple: a founder should be able to give Crabpitch their website and let it figure out who might need their product, why they might be a good fit, and what to say to them.

Instead of building another dashboard that gives founders a list of leads and tells them to do the rest, I wanted to build the actual workflow.

What it does

A customer connects their own mailbox and describes what they sell. After that the engine runs unattended.

It searches for companies matching the customer's profile, reads their actual pages, and verifies addresses before spending a send on them. It judges fit and discards most candidates. For survivors it finds one checkable detail, often a job posting the company published itself, and opens the email with it.

Then it critiques its own work. A model from a different family scores the draft, and deterministic gates check what a model shouldn't be trusted to check: length, whether every number traces to a stated fact, whether a guarantee was invented, whether an opt-out line exists.

Approved mail sends from the customer's own domain inside safe daily limits. Replies get classified, answers get drafted from retrieved facts only, follow-ups cancel themselves when someone responds, and anyone who asks to be left alone is suppressed permanently.

The unit economics are the reason this can serve a business this small. Let $c_\ell$ be the AI cost of researching one lead and $n$ the leads in a plan's monthly allowance. Measured over a week of live traffic:

$$c_\ell = \$0.01, \qquad c_{\text{draft}} = \$0.03$$

For a plan priced $P$ with allowance $n$, the gross margin on inference is

$$\mathcal{M} = P - n \cdot c_\ell$$

which across the three paid tiers gives

$$\mathcal{M}{\text{starter}} = 39 - 1.95 = \$37.05, \quad \mathcal{M}{\text{growth}} = 129 - 6.50 = \$122.50, \quad \mathcal{M}_{\text{scale}} = 349 - 19.50 = \$329.50$$

A margin ratio of

$$\frac{\mathcal{M}_{\text{starter}}}{P} = \frac{37.05}{39} \approx 0.95$$

even when a customer consumes every lead they're entitled to. A human doing the same work would exceed the subscription within the first hour of the month.

How we built it

Gemini is asked first for every decision whose output a human reads: the first email, follow-ups, answers to a prospect's questions, and the classification of every inbound reply. Bedrock's Claude handles high-volume page reading and extraction, and catches anything Gemini's quota can't take.

  • Gemini for drafting, qualification, reply triage, scheduling
  • Amazon Bedrock (Claude) for bulk page reading and as the fallback path
  • Postgres + pgvector so every product claim traces to something the customer published
  • SearXNG, self-hosted, for prospecting without per-query fees
  • Reacher, self-hosted, for address verification before a send is spent
  • Playwright for pages that need JavaScript to render
  • Express + React, Dodo Payments for billing
  • EC2 and RDS behind a Cloudflare tunnel, deployed as immutable release directories

Sending capacity is pure arithmetic, and it decided the shape of the plans. With $M$ mailboxes at a deliverability-safe daily rate $r$:

$$C = M \cdot r \cdot 30$$

Solving for the mailboxes needed to reach a target volume $C^\star$ at $r = 30$:

$$M \geq \left\lceil \frac{C^\star}{r \cdot 30} \right\rceil = \left\lceil \frac{8000}{900} \right\rceil = 9$$

Selling a volume the infrastructure cannot safely deliver isn't a plan, it's a refund, so the allowance and the mailbox count are derived from the same equation rather than chosen independently.

Model cost is tracked per call from token counts, which is what makes the routing projection credible rather than hopeful. For input and output prices $p_{\text{in}}, p_{\text{out}}$ per million tokens:

$$\text{cost} = \frac{T_{\text{in}}}{10^6}\,p_{\text{in}} + \frac{T_{\text{out}}}{10^6}\,p_{\text{out}}$$

Over 4,416 calls we measured $T_{\text{in}} = 7{,}008{,}722$ and $T_{\text{out}} = 541{,}094$, giving \$10.0973 on Bedrock versus \$3.4554 for identical work at Gemini rates:

$$\frac{3.4554 - 10.0973}{10.0973} \approx -0.66$$

Routing everything to Gemini is 66% cheaper as well as more of the story.

Challenges we ran into

Deliverability is an infrastructure discipline, not a copywriting one. We spent weeks on the copy: better openers, tighter word limits, personalisation from hiring signals instead of generic homepage text. Then we ran a real campaign and read the raw message headers rather than a dashboard.

Authentication passed on every axis. SPF pass, DKIM pass, DMARC pass, and Microsoft's own compauth=pass reason=100. The mail was filtered anyway, at spam confidence level 5 with a bulk complaint level of 0.

The headers named the cause in plain text. Outbound was leaving through a relay shared with thousands of unrelated hosting customers, and the host's own filter had already marked the message before it left. We had been optimising a variable that wasn't binding.

That mattered because of who receives this mail. Across 516 real recipient domains the provider mix was

$$p_{\text{Microsoft}} = 0.39, \quad p_{\text{Google}} = 0.32, \quad p_{\text{other}} = 0.20, \quad p_{\text{gateway}} = 0.07$$

so the single largest bloc was the one most sensitive to the sending path. We built the detection into the product: on mailbox connect it walks the SPF chain, bounded to depth 2 and 8 lookups against the protocol's limit of 10, matches the SMTP host against known shared relays, and explains plainly what it found. It never blocks, because plenty of legitimate businesses run mail this way and it's their decision.

Making an AI decision auditable. "The AI decides" is easy to claim and hard to evidence. Every call is booked to a ledger with provider, model, purpose, token counts and cost, attributed to the workspace that caused it. Every draft records the model that wrote it. Of 76 drafts the reviewing model scored, it disagreed with the deterministic scorer by two points or more on 15:

$$\frac{15}{76} \approx 0.20$$

with mean scores of $3.37/7$ against the heuristic's $3.78/7$. A reviewer that never disagrees isn't reviewing.

Rationing a free quota without degrading the product. Free-tier limits are per-model, per-minute and per-day, and a naive retry loop spends the day's allowance in an afternoon. Pacing tracks each model's rate window separately and stands a model down only when its own allowance is genuinely exhausted.

Multi-tenancy where the blast radius is someone's reputation. Every query narrows by workspace explicitly rather than relying on an ambient tenant id, because an implicit scope is how one customer's contacts end up in another customer's response. Credentials are encrypted at rest and sessions are revocable server-side.

Accomplishments that we're proud of

The engine runs without anyone watching it: 3,114 automation runs in 24 hours across active workspaces, roughly $1{,}038$ per workspace per day, with zero runs stalled without a heartbeat.

Guardrails are code, not instructions. The word ceiling lives in the critic that enforces it, so the drafter is never asked for an email longer than the gate accepts. The daily cap cannot be evaded by falling back to another identity, because rotation returns nothing rather than reaching for a spare. A guarantee is a contract term, so the drafter cannot commit a customer to one that isn't in their stated offer.

44 test files and 776 tests, with the checks that matter most written against live DNS and real inboxes rather than fixtures.

And a piece of restraint I'm proudest of. The platform measures what infrastructure customers arrive on, and refuses to report a rate below ten accounts. With $n = 1$ and $\hat{p} = 1$, the 95% Wilson interval is approximately

$$\left[0.21,\ 1.00\right]$$

which is to say it tells you nothing. At $n = 10$ the interval is narrow enough to act on. Writing that threshold into the code was how we stopped ourselves making a confident decision about nothing.

What we learned

Verify against reality, not against your own assumptions. Probing 19 DKIM selectors sequentially took $70.8\,\text{s}$; concurrently it took $9.1\,\text{s}$, a speedup of

$$\frac{70.8}{9.1} \approx 7.8\times$$

We only discovered that by running it for real and getting bored waiting.

Say what you can prove, and bound the rest. Gemini answered 230 of 4,646 calls to date:

$$\frac{230}{4646} \approx 0.050$$

We could have quoted a larger-sounding figure by choosing a different denominator. Instead the evidence pack states the share, states that the limit is the free tier's daily request cap rather than anything in our code, and includes the cost projection above. A number that reproduces from the ledger is worth more than a flattering one that doesn't.

And the biggest lesson: the hardest problems weren't AI problems. The model wrote good emails early. Everything difficult after that was infrastructure, measurement, and the discipline to find out which variable was actually binding before optimising anything.

What's next for Crabpitch

Managed sending, so a customer whose infrastructure can't carry cold outreach can be handed one that can, without us becoming an email service provider. Attaching billing to Gemini to remove the quota ration entirely, which the projection says also lowers total AI spend by roughly 66%. OAuth-based mailbox connection ahead of the industry's move away from password authentication.

The engine works and it runs unattended. What we're chasing now is proof, from customers, that the emails it writes get answered.

Built With

Share this project:

Updates

Submission history