Dev.to AI ๐Ÿค– Ai ๐Ÿ‘ 0 ๐Ÿ“– 2 min read

What Actually Gets Cold Outreach Emails Marked as Spam (It's Not What I Thought)

TL;DR: I run LeadPilot, a tool that finds leads and sends personalized cold emails on autopilot. The thing that actually tanked deliverability wasn't spammy-sounding copy โ€” it was sending infrastructure decisions I made

TL;DR: I run LeadPilot, a tool that finds leads and sends personalized cold emails on autopilot. The thing that actually tanked deliverability wasn't spammy-sounding copy โ€” it was sending infrastructure decisions I made in week one and didn't revisit until open rates quietly collapsed. Here's what I got wrong and what fixed it.

The assumption I started with

Like most people building an outbound tool, I assumed spam filtering was mostly about content: avoid words like "free," "guarantee," "act now," keep the copy conversational, don't overuse links. I optimized for that. Open rates were fine for about three weeks, then dropped by more than half over a single week with no copy changes on my end.

The content wasn't the problem. The sending pattern was.

What was actually happening

Shared IP reputation, inherited from strangers. I was sending through a generic transactional email provider's shared IP pool. Every other sender on that pool who got flagged for spam dragged my deliverability down with them, invisibly โ€” I had no way to see it happening from my side, only the symptom (dropping opens).

No warm-up curve. I went from zero sending volume to a few hundred emails a day immediately once a customer connected their account. Mailbox providers use sending-volume ramp as a trust signal; a brand-new sending identity that jumps straight to volume looks like exactly what a spammer does.

One domain doing everything. Marketing site, product emails, and cold outbound were all sending from the same domain. A spam complaint on a cold email hurts the reputation of transactional emails from that same domain too โ€” password resets, notifications, all of it shares the blast radius.

What actually fixed it

// simplified sending-domain strategy
const domains = {
  marketing: 'company.com',
  product: 'app.company.com',
  outbound: 'outreach.company-mail.com', // separate root domain
};

Isolating cold outbound onto its own domain, with its own SPF/DKIM/DMARC records, meant a deliverability problem there couldn't touch product emails. Then I built a warm-up scheduler: new sending identities start at 20 emails/day and increase by roughly 20% daily only if reply and bounce rates stay within healthy ranges, capping out after about three weeks.

async function getDailySendCap(identity: SendingIdentity): Promise<number> {
  const daysSinceStart = daysBetween(identity.createdAt, new Date());
  const bounceRate = await getBounceRate(identity, { days: 7 });
  if (bounceRate > 0.03) return identity.currentCap; // freeze ramp, don't increase
  const rampedCap = Math.floor(20 * Math.pow(1.2, daysSinceStart));
  return Math.min(rampedCap, 300);
}

Freezing the ramp โ€” not just slowing it โ€” the moment bounce rate crosses 3% turned out to matter more than the ramp curve itself. A sending identity that keeps climbing while its bounce rate creeps up is the fastest way back into a shared blocklist.

The honest trade-off

None of this makes outreach fast. A new customer's sending identity spends its first three weeks capped well below what they'd like to send, and I've had to explain "why can't it just send more" more than once. But the alternative โ€” no ramp โ€” is what caused the collapse in the first place, so it's a trade I'm not willing to undo for anyone's first month.

If your outbound tool's opens are dropping and the copy hasn't changed, check the sending infrastructure before you touch a single word of the email.

LeadPilot is free while it's early โ€” link's in my profile.

๐Ÿ“ฐ Read the original article on Dev.to AI

Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes โ€” full credit and traffic to the original publisher.