Why We Built Our Own Outreach Pipeline (Not Another SaaS Inbox)
We stopped stacking outreach SaaS on top of Gmail. Here's the pipeline we actually run — dedup, warmup, reply routing — with screenshots from prod.

We already ship production AI — WhatsApp on vessels (1,000+ messages a week), Academy intern workflows, SEO agents on the same FastAPI router. Outbound was the awkward cousin: Hunter for emails, something like Instantly for sends, replies in a Gmail tab nobody checked on weekends.
It worked until it didn't. A prospect replied on Thursday. I found it Monday, buried under newsletters. Same person existed in two lists with slightly different titles — we almost emailed them twice. "AI personalization" was {first_name} plus a sentence scraped from LinkedIn About. Not great.
So we built outreach into the stack we already run. Not because cold email is magic — because coordination and deliverability kept losing to tools that don't talk to each other.

The admin panel (real prod, not Figma)
The GIF below is the actual dashboard we use — outreach funnel, sent prospects, unified email inbox. No mock data.

What was breaking
Replies vanishing. Sends go from [email protected]. Replies should update CRM state. They didn't — they sat in Gmail until someone remembered to look.
Duplicate rows. Two discovery runs, same email, different company spellings. Without a hard dedup gate, you send twice and look spammy.
Deliverability as a one-time DNS task. SPF/DKIM/DMARC green on day one means nothing if you blast 40 emails on a fresh domain at 2am UTC. Warmup has to be code, not a spreadsheet reminder.
Empty inbox bodies. Resend's webhook gives you metadata. HTML-only replies showed up in admin with blank bodies — useless when you're trying to decide if someone is warm.
What we built
| Piece | What it does | Why it exists |
|---|---|---|
| Discovery | Finds ops-heavy companies via DDG + intent APIs | Rows land as pending, not mystery CSVs |
| Quality gates | Blocks role emails, directory sites, junk domains | Saves reputation before SMTP |
| Email dedup | One active row per normalized address | Same person, two lists → one send |
| Draft agent | LLM writes short, question-led mail | We want a reply, not a pitch deck |
| Send scheduler | Mon–Fri, 9–11am recipient local time, daily cap | Humans actually read mail in the morning |
| Unified inbox | Reply → Resend → router → Admin Email + status=replied |
Warm lead in one panel, Discord ping included |

Cadence in prod
Hourly job: draft up to five pending prospects, send up to three — capped by warmup phase. Sunday night discovery. Monday 09:30 UTC still posts a batch to Discord #outreach-agent for a human skim before anything controversial goes out.
outreach_discover: "Sun 22:00 UTC"
outreach_pipeline: "hourly POST /v1/outreach/pipeline"
outreach_legacy_draft: "Mon 09:30 UTC → Discord #outreach-agent"
warmup_caps: [8, 15, 25, 40] # by lifetime sends on mail.appopoleis.com
send_window: "Mon–Fri 09:00–11:00 recipient local"
At ~76 lifetime sends: zero bounces, SPF/DKIM/DMARC still green. Slow on purpose. The domain is young; we'd rather send 15 good emails than 150 that torch reputation.
When someone hits Reply
Prospect replies to cold mail → Resend receives it → we pull the full MIME (HTML stripped to plain if needed) → match sender email to an outreach row → flip status=replied → thread shows in Admin → Email.

Recent inbox fixes (the unglamorous stuff that actually matters):
- Spam sender blocklist — mark once, auto-filter the next time
- Resend receiving API — full body, not empty shells on HTML clients
- Attachments — PDFs and proposals downloadable from the panel
- Outreach filter —
[email protected]separated from catch-all noise
Pipeline sketch
Same pattern as everything else we ship
Outreach isn't a side project bolted onto the website. It's the same shape as our WhatsApp handler and LLM router: hard rules before the model runs, audit trail in Supabase, one admin surface instead of five logins.
We're not promising "100 meetings a month." We're building the boring rails — dedup, warmup, bounce pause, reply routing — so when someone actually responds, you notice the same day.
If you're running custom AI already and outreach is still a spreadsheet plus three SaaS tabs, talk to us. We'd rather deploy something that survives your inbox than another demo that looks fine until week three.
Work with us
Send the brief
Name the app, site, or WhatsApp system. Shipped work you can open: LalaRente, Khaki Post, and marine voice AI. Studio WhatsApp: 9892110925.
More from the blog
AI Architecture Generator
An AI architecture generator turns a plain description of a business process into a diagram of the systems that can automate it. The one on this site is free to try and does not ask for an account first.
We used Vulcanbench Coding Agent Benchmark (Here's What We Found)
We stopped trusting vendor benchmarks for local model selection. So we built VulcanBench — 52 real coding tasks, Docker sandbox, 5-metric scoring. Here's what our first runs revealed.