Craft B2B cold outreach emails and follow-up sequences that sound like they came from a thoughtful human and actually get replies. You will produce ready-to-send copy including subject lines, openers, and CTAs tailored to your specific prospects and goals. Reach for this whenever you need to contact new prospects who haven't asked to hear from you yet.
name: “cold-email”
description: “When the user wants to write, improve, or build a sequence of B2B cold outreach emails to prospects who haven’t asked to hear from them. Use when the user mentions ‘cold email,’ ‘cold outreach,’ ‘prospecting emails,’ ‘SDR emails,’ ‘sales emails,’ ‘first touch email,’ ‘follow-up sequence,’ or ‘email prospecting.’ Also use when they share an email draft that sounds too sales-y and needs to be humanized. Distinct from email-sequence (lifecycle/nurture to opted-in subscribers) — this is unsolicited outreach to new prospects. NOT for lifecycle emails, newsletters, or drip campaigns (use email-sequence).”
license: MIT
metadata:
version: 1.0.0
author: Alireza Rezvani
category: marketing
updated: 2026-03-06
Cold Email Outreach
You are an expert in B2B cold email outreach. Your goal is to help write, build, and iterate on cold email sequences that sound like they came from a thoughtful human — not a sales machine — and actually get replies.
Before Starting
Check for context first:
If marketing-context.md exists, read it before asking questions.
Gather this context:
1. The Sender
Who are they at this company? (Role, seniority — affects how they write)
What do they sell and who buys it?
Do they have any real customer results or proof points they can reference?
Are they sending as an individual or as a company?
2. The Prospect
Who is the target? (Job title, company type, company size)
What problem does this person likely have that the sender can solve?
Is there a specific trigger or reason to reach out now? (funding, hiring, news, tech stack signal)
Do they have specific names and companies to personalize to, or is this a template for a segment?
3. The Ask
What’s the goal of the first email? (Book a call? Get a reply? Get a referral?)
How aggressive is the timeline? (SDR with daily send volume vs founder doing targeted outreach)
How This Skill Works
Mode 1: Write the First Email
When they need a single first-touch email or a template for a segment.
Understand the ICP, the problem, and the trigger
Choose the right framework (see references/frameworks.md)
Draft first email: subject line, opener, body, CTA
Review against the principles below — cut anything that doesn’t earn its place
When they need a multi-email sequence (typically 4-6 emails).
Start with the first email (Mode 1)
Plan follow-up angles — each email needs a different angle, not just a nudge
Set the gap cadence (Day 1, Day 4, Day 9, Day 16, Day 25)
Write each follow-up with a standalone hook that doesn’t require reading previous emails
End with a breakup email that closes the loop professionally
Deliver: full sequence with send gaps, subject lines, and brief on what each email does
Mode 3: Iterate from Performance Data
When they have an active sequence and want to improve it.
Review their current sequence emails and performance (open rate, reply rate)
Diagnose: is the problem subject lines (low open rate), email body (opens but no replies), or CTA (replies but wrong outcome)?
Rewrite the underperforming element
Deliver: revised emails + diagnosis + test recommendation
Core Writing Principles
1. Write Like a Peer, Not a Vendor
The moment your email sounds like marketing copy, it’s over. Think about how you’d actually email a smart colleague at another company who you want to have a conversation with.
The test: Would a friend send this to another friend in business? If the answer is no — rewrite it.
❌ “I’m reaching out because our platform helps companies like yours achieve unprecedented growth…”
✅ “Noticed you’re scaling your SDR team — timing question: are you doing outbound email in-house or using an agency?“
2. Every Sentence Earns Its Place
Cold email is the wrong place to be thorough. Every sentence should do one of these jobs: create curiosity, establish relevance, build credibility, or drive to the ask. If a sentence doesn’t do one of those — cut it.
Read your draft aloud. The moment you hear yourself droning, stop and cut.
3. Personalization Must Connect to the Problem
Generic personalization is worse than none. “I saw you went to MIT” followed by a pitch has nothing to do with MIT. That’s fake personalization.
Real personalization: “I saw you’re hiring three SDRs — usually a signal that you’re trying to scale cold outreach. That’s exactly the challenge we help with.”
The personalization must connect to the reason you’re reaching out.
4. Lead With Their World, Not Yours
The opener should be about them — their situation, their problem, their context. Not about you or your product.
❌ “We’re a sales intelligence platform that…”
✅ “Your recent TechCrunch piece mentioned you’re entering the SMB market — that transition is notoriously hard to do with an enterprise-built playbook.”
5. One Ask Per Email
Don’t ask them to book a call, watch a demo, read a case study, AND reply with their timeline. Pick one ask. The more you ask for, the less likely any of it happens.
Voice Calibration by Audience
Adjust tone, length, and specificity based on who you’re writing to:
Audience
Length
Tone
Subject Line Style
What Works
C-suite (CEO, CRO, CMO)
3-4 sentences
Ultra-brief, peer-level, strategic
Short, vague, internal-looking
Big problem → relevant proof → one question
VP / Director
5-7 sentences
Direct, metrics-conscious
Slightly more specific
Specific observation + clear business angle
Mid-level (Manager, Analyst)
7-10 sentences
Practical, shows you did homework
Can be more descriptive
Specific problem + practical value + easy CTA
Technical (Engineer, Architect)
7-10 sentences
Precise, no fluff
Technical specificity
Exact problem → precise solution → low-friction ask
The higher up the org chart, the shorter your email needs to be. A CEO gets 100+ emails per day. Three sentences and a clear question is a gift, not a slight.
Subject Lines: The Anti-Marketing Approach
The goal of a subject line is to get the email opened — not to convey value, not to be clever, not to impress anyone. Just open it.
The best cold email subject lines look like internal emails. They’re short, slightly vague, and create just enough curiosity to click.
What Works
Pattern
Example
Why It Works
Two or three words
quick question
Looks like an actual email from a colleague
Specific trigger + question
your TechCrunch piece
Specific enough to not look like spam
Shared context
re: Series B
Feels like a follow-up, not cold
Observation
your ATS setup
Specific, relevant, not salesy
Referral hook
[mutual name] suggested I reach out
Social proof front-loaded
What Kills Opens
ALL CAPS anything
Emojis in subject lines (polarizing, often spam-filtered)
Fake Re: or Fwd: (people have learned this trick — it damages trust)
Asking a question in the subject line (e.g., “Are you struggling with X?”) — sounds like an ad
Mentioning your company name (“Acme Corp: helping you achieve…”)
Numbers that feel like blog headlines (“5 ways to improve your…”)
Follow-Up Strategy
Most deals happen in follow-ups. Most follow-ups are useless. The difference is whether the follow-up adds value or just creates noise.
Cadence
Email
Send Day
Gap
Email 1
Day 1
—
Email 2
Day 4
+3 days
Email 3
Day 9
+5 days
Email 4
Day 16
+7 days
Email 5
Day 25
+9 days
Breakup
Day 35
+10 days
Gaps increase over time. You’re persistent but not annoying.
Follow-Up Rules
Each follow-up must have a new angle. Rotate through:
New piece of evidence (case study, data point, recent result)
New angle on the problem (a different pain point in their world)
Related insight (something you noticed about their industry, tech stack, or news)
Direct question (just ask plainly — sometimes clarity cuts through)
Reverse ask (ask for referral to the right person if you can’t reach them)
Never “just check in.” “Just following up to see if you had a chance to read my last email” is a waste of both your time and theirs. If you have nothing new to add, don’t send the email.
Don’t reference all previous emails. Each follow-up should stand alone. The prospect doesn’t remember your earlier emails. Don’t make them scroll.
The Breakup Email
The last email in a sequence should close the loop professionally. It signals this is the last one — which paradoxically increases reply rate because people don’t like loose ends.
Example breakup:
“I’ll stop cluttering your inbox after this one. If [problem] ever becomes a priority, happy to reconnect — just reply here and I’ll pick it up.
If there’s someone else at [Company] I should speak with, a name would go a long way.
Either way — good luck with [whatever’s relevant].”
See references/follow-up-playbook.md for full cadence templates and angle rotation guide.
What to Avoid
These are not suggestions — they’re patterns that mark you as a non-human and kill reply rates:
❌ Avoid
Why It Fails
”I hope this email finds you well”
Instant tell that this is templated. Cut it.
”I wanted to reach out because…“
3-word delay before actually saying anything
Feature dump in email 1
Nobody cares about features when they don’t trust you yet
HTML templates with logos and colors
Looks like marketing, gets spam-filtered
Fake Re:/Fwd: subject lines
Feels deceptive — kills trust before the first word
”Just checking in” follow-ups
Adds no value, removes credibility
Opening with “My name is X and I work at Y”
They can see your name. Start with something interesting.
Social proof that doesn’t connect to their problem
”We work with 500 companies” means nothing without context
Long-form case study in email 1
Save it for follow-up when they’ve shown interest
Passive CTAs (“Let me know if you’re interested”)
Weak. Ask a direct question or propose a specific next step.
Deliverability Basics
A great email sent from a flagged domain never lands. Basics you need to have in place:
Dedicated sending domain — don’t send cold email from your primary domain. Use mail.yourdomain.com or outreach.yourdomain.com.
SPF, DKIM, DMARC — all three must be configured and passing. Use mail-tester.com to verify.
Domain warmup — new domains need 4-6 weeks of warmup (start with 20/day, ramp up over time).
Plain text emails — or minimal HTML. Heavy HTML triggers spam filters.
Unsubscribe mechanism — required legally (CAN-SPAM, GDPR). Include a simple opt-out.
Sending limits — stay under 100-200 emails/day per domain until established reputation.
See references/deliverability-guide.md for domain warmup schedule, SPF/DKIM setup, and spam trigger word list.
Proactive Triggers
Surface these without being asked:
Email opens with “My name is” or “I’m reaching out because” → rewrite the opener. These are dead-on-arrival openers. Flag and offer an alternative that leads with their world.
First email is longer than 150 words → almost certainly too long. Flag word count and offer to trim.
No personalization beyond first name → templated feel will hurt reply rates. Ask if there’s a trigger or signal they can work with.
Follow-up says “just checking in” or “circling back” → useless follow-up. Ask what new angle or value they can bring to that touchpoint.
HTML email template → recommend plain text. Plain text emails have higher deliverability and look less like marketing blasts.
CTA asks for 30-45 minute meeting in email 1 → too high-friction for cold outreach. Recommend a lower-commitment ask (a 15-minute call, or just a question to gauge interest first).
Output Artifacts
When you ask for…
You get…
Write a cold email
First-touch email + 3 subject line variants + brief rationale for structure choices
Build a sequence
5-6 email sequence with send gaps, subject lines per email, and angle summary for each follow-up
Critique my email
Line-by-line assessment + rewrite + explanation of each change
Write follow-ups only
Follow-up emails 2-6 with unique angles per email + breakup email
Analyze sequence performance
Diagnosis of where the sequence breaks (subject/body/CTA) + specific rewrite recommendations
Communication
All output follows the structured communication standard:
Bottom line first — answer before explanation
What + Why + How — every finding has all three
Actions have owners and deadlines — no “we should consider”
email-sequence: For lifecycle and nurture emails to opted-in subscribers. Use email-sequence for onboarding flows, re-engagement campaigns, and automated drips. NOT for cold outreach — that’s cold-email.
copywriting: For marketing page copy. Principles overlap, but cold email has different constraints — shorter, no CTAs like buttons, must feel personal.
content-strategy: For creating the content assets (case studies, guides) you reference in cold email follow-ups. Good follow-up sequences often link to content.
marketing-strategy-pmm: For positioning and ICP definition. If you don’t know who you’re targeting and why, cold email is the wrong tool to figure that out.
Deliverability Guide
A cold email that lands in spam is worse than no email at all — it damages your sender reputation for future sends. Get deliverability right before you worry about copy.
The Deliverability Stack
Email deliverability is a layer cake. Every layer has to be correct:
Domain reputation (is your domain trusted by inbox providers?) ↓Authentication (SPF, DKIM, DMARC — are you who you say you are?) ↓Sending infrastructure (IP reputation, sending limits, ramp-up) ↓List quality (are you sending to real, active addresses?) ↓Email content (does the content look like spam?) ↓Engagement signals (opens, replies, not-spam clicks)
Fix problems from the bottom up. No point perfecting copy if your domain is blacklisted.
Domain Setup
Use a Dedicated Sending Domain
Never send cold email from your primary company domain (acme.com). If your cold email domain gets flagged or blacklisted, you lose your main domain's email reputation.
Setup options:
mail.acme.com — subdomain of main domain
acme-hq.com — separate domain with similar name
getacme.com / tryacme.com — common pattern for SaaS
Rules for the sending domain:
Set up a proper website (even a simple redirect to main site) — bare domains look suspicious
Match the company name visually — unrelated domains look like phishing
Get a G Suite / Microsoft 365 mailbox on it — shared hosting email servers have worse reputation
SPF Record
SPF (Sender Policy Framework) tells receiving servers which IP addresses are allowed to send email from your domain. Without it, your emails look unauthenticated.
DNS TXT record:
v=spf1 include:_spf.google.com ~all
Replace _spf.google.com with your sending provider's SPF include. Check your provider's documentation for the exact value (Google Workspace, SendGrid, Mailgun, etc. all have their own).
Important: Only have ONE SPF record per domain. If you have multiple, they conflict and authentication fails.
DKIM
DKIM (DomainKeys Identified Mail) adds a cryptographic signature to your emails, proving they weren't tampered with in transit.
Setup is done through your email provider — they give you a DNS TXT record to add. It looks like:
google._domainkey.yourdomain.com IN TXT "v=DKIM1; k=rsa; p=MIGfMA0..."
The public key in that record lets receiving servers verify your email's signature.
DMARC
DMARC ties SPF and DKIM together and tells receiving servers what to do when authentication fails.
Starter DMARC record (monitoring mode):
_dmarc.yourdomain.com IN TXT "v=DMARC1; p=none; rua=mailto:[email protected]"
p=none means monitor but don't block — good to start with. Once you've confirmed SPF and DKIM are working cleanly, move to p=quarantine or p=reject.
Verify Everything
Use mail-tester.com: send a test email to their address, then check your score. 9/10 or higher means your authentication is clean. Below 7/10 means something is broken.
Domain Warmup
A brand new domain has no sending reputation. Email providers don't trust it. If you start sending 200 emails/day on day one, you will be flagged.
Warmup = building reputation gradually by sending low volumes and getting positive engagement.
Warmup Schedule
Week
Emails/Day
Focus
1
5-10
Real conversations only — send to colleagues, get replies
2
20-30
Small cold outreach batches — highly targeted, good lists
3
40-60
Expand slightly — maintain >30% open rate
4
80-100
Normal volume — watch bounce and spam complaint rates
5+
Up to 200
Full volume — monitor daily
Warning signs that warmup is failing:
Open rate drops below 20%
Bounce rate above 3%
Spam complaint rate above 0.1%
Emails landing in Gmail Promotions tab
Manual warmup vs tools: Tools like Lemwarm, Warmup Inbox, or Mailreach automate warmup by sending emails to a network of inboxes that automatically open and engage. These help build reputation faster. They're worth it for new domains.
List Quality
Sending to bad email addresses destroys your sender reputation. Every hard bounce tells inbox providers your list is dirty.
Before Sending
Verify email addresses — Use a verification tool (NeverBounce, ZeroBounce, Hunter's verify, etc.) before importing any list. Remove invalid, catch-all, and risky emails.
Target bounce rate: Keep it below 2%. Above 5% is dangerous territory.
Remove catch-all domains carefully — Catch-all domains accept any email regardless of whether the mailbox exists. Your emails won't hard-bounce, but they may go nowhere.
Never buy lists — Purchased lists are old, dirty, unverified, and frequently include spam traps (addresses placed by inbox providers to catch spammers). One spam trap hit can blacklist your domain.
Ongoing Hygiene
Remove anyone who hasn't opened in 90 days from your sequence (move to a re-engagement campaign or suppress)
Remove unsubscribes immediately — required legally and good for reputation
Remove bounces from all future sends automatically
Content That Hurts Deliverability
Spam filters evaluate content alongside authentication and reputation. These patterns trigger filters:
Spam Trigger Words to Avoid
High-risk words and phrases (use sparingly or avoid):
"Free" (especially in subject lines)
"Guaranteed" / "100% guaranteed"
"No obligation"
"Act now" / "Limited time"
"Congratulations"
"You've been selected"
"Click here"
"Earn money" / "Make money"
"Risk-free"
"Special offer"
Excessive exclamation points!!!
ALL CAPS words
These don't automatically spam-filter you, but they're additive — the more of them in a single email, the higher the spam score.
Content Rules
Do
Don't
Plain text or minimal HTML
Heavy HTML with complex tables, images
One link max per email
5+ links — looks like phishing or newsletter
Personalized subject lines
Batch-blasted "LAST CHANCE" subject lines
Unsubscribe link
No unsubscribe mechanism
Consistent from name
Rotating from names
Short emails
Wall-of-text emails
The HTML Question
Plain text emails consistently get better deliverability than HTML emails for cold outreach. They look like real emails from real people — because they are.
If you need to include your company logo and a fancy template: don't. Save that for newsletters to opted-in subscribers. Cold email = plain text, signed like a person.
Sending Limits by Platform
Platform
Safe Daily Volume
Notes
Google Workspace (paid)
500/day
Shared across all outgoing
Google Workspace + Warmup
Up to 2000/day
After full warmup
Microsoft 365
10,000/day
Generous, but still subject to reputation
SendGrid
Depends on plan
IP reputation matters at scale
Mailgun
Depends on plan
Good for transactional, OK for cold
Lemlist / Instantly / Apollo
Platform-managed
Warmup built in, use their sending infrastructure
For cold outreach at scale (>500/day), dedicated sending platforms are better than Google/Microsoft direct — they're designed to manage reputation across many users.
Checking Your Reputation
If you suspect deliverability problems, check these:
Mail-tester.com — Authentication and content score (10/10 is perfect)
MXToolbox Blacklist Check — Check if your domain or IP is on any blacklists
Google Postmaster Tools — Shows your domain reputation with Gmail (spam rate, auth failures)
Microsoft SNDS — Similar to Google Postmaster for Outlook/Hotmail
If you're on a blacklist:
Stop sending immediately from that domain
Identify the cause (bad list, spam complaints, warmup failure)
Follow the blacklist's delisting process (each has its own)
Consider using a new domain while the old one recovers
Legal Requirements
Cold email has legal requirements in most markets. Breaking them isn't just unethical — it's fined.
Requires express or implied consent — much stricter than CAN-SPAM
GDPR
EU/EEA
Legitimate interest basis required; no soft opt-in
PECR
UK
Similar to GDPR; ICO enforcement
Minimum compliance for most cold email:
Include your company name and physical address in every email
Provide a working unsubscribe link or reply-to-unsubscribe instruction
Honor unsubscribes within 10 business days (CAN-SPAM) or immediately (GDPR best practice)
Don't use misleading subject lines or from names
Disclaimer: This is practical guidance, not legal advice. For EU/Canada outreach, consult a lawyer who specializes in email marketing law — GDPR and CASL are stricter than most people realize.
Follow-Up Playbook
Full cadence guide, angle rotation, and breakup email templates. The goal: stay persistent without becoming noise.
The Core Problem with Follow-Ups
Most follow-up emails are a form of wishful thinking: "Maybe they missed it. I'll send it again." They didn't miss it. They read it, didn't feel urgency, and moved on. Another "just checking in" doesn't create urgency — it signals that you have nothing new to offer.
The only follow-up worth sending is one that adds something: a new angle, a new proof point, a new question, or a new frame.
The Full Cadence
Email
Label
Day
Gap
Purpose
1
First touch
Day 1
—
Lead with their world, establish relevance
2
New angle
Day 4
+3
Different problem angle or social proof
3
Value add
Day 9
+5
Resource, insight, or data point
4
Direct question
Day 16
+7
Cut through with a plain, direct ask
5
Reverse
Day 25
+9
Ask for referral to the right person
6
Breakup
Day 35
+10
Close the loop, leave door open
Gaps increase over time. You're persistent but not desperate.
6 emails is the upper limit for most cold outreach. For very high-value accounts (ABM-style), you might go to 8. For volume prospecting, 4-5 is often more practical.
Email-by-Email Guide
Email 1: First Touch
Already covered in frameworks.md. The anchor of the sequence.
What it needs:
Specific, relevant opener
Clear connection between their situation and what you do
One ask, low friction
Email 2: New Angle (Day 4)
This is where most sequences fail — they send a "following up" reminder. Don't.
Email 2 should approach the problem from a different angle than Email 1. If Email 1 was about their hiring signal, Email 2 might be about the operational risk that follows from rapid hiring. Different angle, same direction.
Angle options for Email 2:
Different pain point in the same domain
Social proof / customer story that's highly relevant to their context
An industry trend that makes the problem more urgent
A specific, relevant statistic you haven't mentioned yet
Template structure:
[New angle or observation — 1-2 sentences][Expand on why it matters for their situation — 1-2 sentences][Soft CTA — question or invitation]
Example:
A lot of the teams I talk to at your stage are hitting the same wall: the ramp time on new SDRs has stretched from 3 months to 5+ months because the playbook that worked at 5 reps doesn't scale to 15.
It's not a hiring problem — it's an enablement infrastructure problem that doesn't become visible until you're already in it.
Is that a challenge you're actively working on, or is it on the radar for later?
Email 3: Value Add (Day 9)
Give something useful before asking again. This builds goodwill and separates you from the other 30 emails in their inbox that only ask.
What counts as value:
A relevant guide, benchmark report, or template (if you have one)
A specific insight about their market or competitor landscape
A practical suggestion based on what you know about their situation
A useful question that helps them think about their problem differently
Template structure:
[Reference to something specific about them — 1 sentence][The value: insight, resource, or useful observation — 2-3 sentences][Low-friction CTA: "useful?" or "happy to elaborate" or specific ask]
Example:
We just published a benchmark of SDR ramp times across 40 SaaS companies by stage — the data is pretty surprising (the fastest don't hire the most experienced reps, they do onboarding completely differently).
Thought of your situation when reviewing it. Happy to share the relevant section if useful — no strings, just might be helpful context for where you're headed.
Email 4: Direct Question (Day 16)
By Email 4, subtlety has run its course. Sometimes the most effective move is to ask a direct, plain question. No setup, no story.
This email is short. Often just two or three lines.
Options:
Ask what's getting in the way
Ask if your assumption about their problem is wrong
Ask if the timing just isn't right
Ask who the right person to talk to is
Template structure:
[One direct question — sometimes that's all this email needs to be][Optional: one sentence of context if needed][Nothing else]
Example:
Is SDR ramp time actually a priority for you right now, or is the timing just off?
No judgment either way — just helps me know whether it's worth staying in touch.
Or even shorter:
Am I reaching the wrong person here — is there someone else on your team who owns sales enablement?
Email 5: Reverse / Referral (Day 25)
If you haven't reached the right person, this email shifts to asking for the referral. If you have reached the right person but they haven't replied, the referral ask sometimes unlocks a conversation because it's a different and lower-commitment request.
Template structure:
[Acknowledge you may not be reaching the right person — 1 sentence][Who you're actually looking for — specific role or function — 1 sentence][Referral ask — 1 sentence]
Example:
I might be reaching out to the wrong person — the conversations I typically have are with whoever owns sales onboarding and enablement, which may not be you.
If there's a name you could point me toward, I'd really appreciate it. And if it is you — totally understand if the timing isn't right.
Email 6: Breakup (Day 35)
The last email. Its job is to close the loop professionally and leave the relationship in a better place than if you'd just gone silent.
The breakup email often generates the highest reply rate of the entire sequence — people don't like unanswered threads.
What makes a good breakup:
Signals clearly that this is the last one (without being dramatic about it)
Leaves the door open — no hard feelings
Offers one final path to action (reply, referral, or reconnect later)
Keeps it under 5 sentences
Template:
[Signal this is your last email — 1 sentence][Genuine offer to reconnect when timing changes — 1 sentence][Referral ask as a final option — 1 sentence][Warm close — 1 sentence]
Example:
I'll stop cluttering your inbox after this one.
If scaling your outbound motion ever becomes a priority, happy to pick this back up — just reply here and I'll be there.
If there's someone else at [Company] who owns this, a name would be genuinely helpful.
Either way, good luck with the Berlin expansion.
What to avoid in the breakup:
Passive-aggressive tone ("I've tried to reach you several times now...")
Fake urgency ("This is your last chance to...")
Asking for feedback on why they didn't reply (annoying, not useful)
Angle Rotation Guide
Never repeat the same angle twice. Here are enough angles for a full 6-email sequence on any B2B topic:
Angle
Description
Example
Trigger event
The specific reason you reached out
"Saw the funding announcement..."
Adjacent pain
A related problem they also likely have
"The challenge after that usually is..."
Social proof
Customer story or result
"We helped a team in your situation..."
Industry trend
External force making the problem more urgent
"EMEA data residency rules are tightening..."
Data/benchmark
A specific number that reframes the problem
"The average ramp time in your segment is 4.2 months..."
Counterintuitive insight
Something most people in their role get wrong
"Most teams solve this by hiring more, which makes it worse..."
Resource offer
Something genuinely useful, no strings
"We just published a guide on exactly this..."
Direct question
Plain, honest ask
"Is this even a priority right now?"
Referral ask
Ask for the right person if not them
"Am I talking to the right person here?"
Breakup
Close the loop
"I'll stop after this one..."
Sequence design tip: never use two heavy asks back to back. Pattern: trigger → social proof → value → direct → referral → breakup works better than ask → ask → ask → ask → ask → breakup.
Short Sequence Variations
3-Email Sequence (High-Volume SDR)
Day 1: OPPA framework (trigger + problem + proof + ask)
Day 5: Value add (resource, insight, or data point)
Day 12: Breakup
4-Email Sequence (Balanced)
Day 1: First touch
Day 4: New angle / social proof
Day 10: Direct question
Day 20: Breakup
6-Email Sequence (ABM / High-Value Accounts)
Full sequence as described above.
What Never to Send
"Just following up" — Adds nothing. Deletes itself.
"Did you see my last email?" — They saw it. This is passive-aggressive.
"I wanted to make sure this didn't get lost" — Patronizing.
"I know you're busy but..." — Everyone's busy. Don't invoke it.
A forwarded copy of the original email — They have the original. This is lazy.
Back-to-back emails on the same day — Unless it's a clear error correction.
Cold Email Outreach Frameworks
Three frameworks that work, when to use each, and how to apply them with examples.
How to Use This Guide
A framework is a structure, not a script. Use it to organize your thinking, then write in your own voice. If an email sounds like it was written from a template, the framework failed.
Each framework works best in specific situations — the mismatch between framework and context is why most cold emails fall flat.
Framework 1: Observation → Problem → Proof → Ask (OPPA)
Best for: Prospects where you have a specific, real observation (trigger event, signal, public info). This is the most versatile framework and the default for most B2B cold email.
What it does: Starts with something real and specific about them, connects it to a problem they likely have, brings in credibility, and asks a focused question.
Structure
[Observation]: Something specific and true about them right now.[Problem]: The logical challenge or risk that creates.[Proof]: One concrete piece of evidence you can solve it.[Ask]: A single, low-friction question or request.
How to Write Each Part
Observation — This must be:
Specific (not "I see you're in the software industry")
"Saw the announcement that you're opening a Berlin office."
"Noticed you're hiring 4 SDRs simultaneously — unusual to scale the team that fast."
"Your last three blog posts have all been about compliance — guessing that's a pressure point right now."
Problem — This should feel like something they already know is true, not something you're trying to convince them of.
❌ "Companies like yours struggle with X."
✅ "That scale-up usually surfaces a bunch of process gaps that are invisible when you're smaller."
Proof — Keep it tight. One result, one customer name (if allowed), or one specific claim. Not a list.
❌ "We work with 300+ companies and have won 7 awards."
✅ "We helped a similar-sized team in fintech cut SDR ramp time by 40% in the first quarter."
Ask — One ask. Low friction. Makes it easy to say yes or no.
❌ "Would you be open to a 45-minute product walkthrough with our sales team?"
✅ "Worth 15 minutes to compare notes on how you're handling this?"
Full Example
Subject: your Berlin expansion
Congrats on the Berlin announcement — Series B followed by a new market in the same quarter is a big move.
The part that usually bites teams at this stage: the go-to-market motion that worked for your home market rarely translates directly, especially if you're dealing with different buyer personas and a cold pipeline.
We've helped three B2B SaaS teams with exactly this transition — the fastest got pipeline moving in Germany within 90 days. Happy to share what worked.
Worth a 20-minute call to compare notes?
Framework 2: Question → Value → Ask (QVA)
Best for: Situations where you don't have a strong trigger event, but you understand the prospect's world well enough to lead with a sharp insight or question. Good for segmented outreach to a persona with a known, common pain.
What it does: Opens with a question that creates cognitive engagement — they can't help but answer it in their head. Then delivers value before asking for anything.
Structure
[Question]: A question they're probably already asking themselves.[Value]: An insight, reframe, or resource that helps them — before they've agreed to anything.[Ask]: Low-friction request to continue the conversation.
How to Write Each Part
Question — Not a rhetorical sales question ("Are you struggling with X?"). An actual, thoughtful question they'd ask at a team meeting.
❌ "Are you struggling to hit your pipeline targets?"
✅ "What's your current approach to EMEA expansion — inside sales, channel, or hybrid?"
The question works because it's specific enough that only a relevant person can answer it, and answering it in their head pulls them into the email.
Value — Give something before asking for anything. This is the differentiator. Options:
A useful insight from your experience working in their space
A specific data point or benchmark they probably don't have
A framework or reframe that's genuinely useful
A short, actionable observation about their situation
This doesn't need to be long. Two sentences of genuine value beats two paragraphs of soft selling.
Ask — Same rules as OPPA. One ask, low friction, specific.
Full Example
Subject: EMEA expansion approach
Quick question — are you planning to open EMEA with a field sales team, or running it remotely from the US for the first 12 months?
I ask because we've seen both approaches play out across about 30 SaaS companies doing this move, and the one that consistently underperforms is the "remote first, hire local later" model — not because of the sales motion, but because of the support/onboarding gap that follows when you close enterprise deals in a timezone you don't cover.
Happy to share a quick breakdown of what the fastest-scaling teams do differently if that's useful. 15 minutes?
Framework 3: Trigger → Insight → Ask (TIA)
Best for: When you have a very specific, time-sensitive trigger event and want to move fast. Great for sales teams with intent signals, tech stack changes, funding news, leadership changes, or industry regulatory shifts.
What it does: Names the trigger directly, provides a non-obvious insight about what that trigger means, and asks a focused question while the timing is relevant.
Structure
[Trigger]: Name the specific event/signal you observed.[Insight]: Something non-obvious about what that trigger typically means/leads to.[Ask]: Direct, time-aware request.
How to Write Each Part
Trigger — Be specific and direct. Don't be coy about why you're reaching out.
❌ "I was browsing LinkedIn and happened to notice..."
✅ "Saw the funding announcement this morning."
Insight — The non-obvious part is what separates this from lazy trigger-based outreach. You're not just saying "congrats on the funding" — you're showing you understand what that trigger means operationally.
Pattern: "That usually means [specific operational challenge] that most [their role] underestimate."
❌ "Congrats! We'd love to help you grow."
✅ "Series A typically means the first real pressure to build repeatable pipeline — and most companies at this stage haven't yet figured out which channels actually scale."
Ask — Frame the timing as genuine, not manufactured urgency.
❌ "Act now before it's too late!"
✅ "First 60 days post-funding is when this gets set up or doesn't — worth a quick call?"
Full Example
Subject: post-Series A pipeline
Saw the Series A close — congrats.
The next 90 days are when pipeline architecture either gets built properly or gets bolted together in a way that causes problems at Series B. Most founders don't realize until 18 months later that they're paying for shortcuts made now.
We work specifically with post-Series A B2B SaaS teams setting up their outbound motion for the first time. Happy to do a no-strings 20-minute call on what works and what doesn't at your stage.
These frameworks aren't rigid. In practice, the best emails blend elements:
TIA trigger + OPPA proof
QVA question + TIA timing
OPPA observation + QVA value
What you can't blend: two questions, two proof points, or two asks. One of each, always.
Subject Line Frameworks
Subject lines have their own logic — separate from the email body.
The Blank Subject
Two or three words, no capitalization, feels like an internal message.
quick question
cold outreach
your q3 pipeline
The Named Trigger
Specific enough to signal you did research, vague enough to create curiosity.
your Series A
Berlin office
your ATS stack
The Shared Context
Implies a pre-existing relationship or shared frame.
re: EMEA expansion
following up on the hiring spike
The Named Person (Referral)
Only use if the referral is real — never fake this.
[Mutual Name] suggested I reach out
[Name] mentioned you're building out your SDR team
Never Use
Quick question about your [product category] strategy!
Revolutionize your [function] with [product name]
[FIRST NAME], we have a special offer for you
Emojis
ALL CAPS
Question marks (feels like an ad)
#!/usr/bin/env python3"""email_sequence_analyzer.py — Analyzes a cold email sequence for quality signals.Evaluates each email on: - Word count (shorter is usually better for cold email) - Reading level estimate (Flesch-Kincaid approximation via avg sentence/word length) - Personalization density (signals of specific, targeted writing) - CTA clarity (is there a clear ask?) - Spam trigger words (words that hurt deliverability) - Subject line analysis (length, warning patterns) - Overall score: 0-100Usage: python3 email_sequence_analyzer.py [sequence.json] cat sequence.json | python3 email_sequence_analyzer.pyIf no file provided, runs on embedded sample sequence.Input format (JSON): [ { "email": 1, "subject": "...", "body": "..." }, ... ]Stdlib only — no external dependencies."""import jsonimport reimport sysimport mathimport selectfrom typing import List, Dict, Any# ─── Spam trigger words ───────────────────────────────────────────────────────SPAM_TRIGGERS = [ "free", "guaranteed", "no obligation", "act now", "limited time", "click here", "earn money", "make money", "risk-free", "special offer", "no cost", "winner", "congratulations", "you've been selected", "once in a lifetime", "urgent", "don't miss out", "buy now", "order now", "100%", "best price", "lowest price", "incredible deal", "amazing offer", "cash bonus", "extra cash", "fast cash", "you have been chosen", "exclusive deal", "as seen on", "dear friend", "valued customer",]# ─── Personalization signals ──────────────────────────────────────────────────PERSONALIZATION_SIGNALS = [ # Direct references to "you" r'\byou(?:r|rs|\'re|\'ve|\'d|\'ll)?\b', # Trigger references r'\b(?:saw|noticed|read|heard|saw|found|noted)\b', # Named observation patterns r'\b(?:your team|your company|your role|your work|your recent|your post)\b', # Industry/role-specific references r'\b(?:as a|in your|at your|given your)\b', # Specific numbers or facts r'\b\d{4}\b', # years — often a sign of specific research r'\$\d+|\d+%', # numbers with $ or %]# ─── Dead opener phrases ──────────────────────────────────────────────────────DEAD_OPENERS = [ "i hope this email finds you well", "i hope this finds you", "i wanted to reach out", "i am reaching out", "my name is", "i'm writing to", "i am writing to", "hope you're doing well", "i hope you are doing well", "just following up", "just checking in", "circling back", "touching base", "per my last email", "as per my previous",]# ─── Weak CTA patterns ────────────────────────────────────────────────────────WEAK_CTA = [ "let me know if you're interested", "let me know if you would be interested", "feel free to", "please don't hesitate", "if you have any questions", "looking forward to hearing from you", "i look forward to connecting", "hope we can connect",]# ─── Strong CTA signals ───────────────────────────────────────────────────────STRONG_CTA_PATTERNS = [ r'\b(?:15|20|30|45|60)[\s-]?minute\b', # time-specific meeting ask r'\b(?:call|chat|talk|speak|connect|meet)\b.*\?', # question + meeting word r'worth\s+(?:a|an)\b', # "worth a call?" r'\?$', # ends with question r'\buseful\b\s*\?', # "useful?" r'\b(?:reply|respond)\b', # explicit reply ask]# ─── Text utilities ───────────────────────────────────────────────────────────def count_words(text: str) -> int: return len(text.split())def count_sentences(text: str) -> int: """Rough sentence count by terminal punctuation.""" sentences = re.split(r'[.!?]+', text) return max(1, len([s for s in sentences if s.strip()]))def avg_words_per_sentence(text: str) -> float: words = count_words(text) sentences = count_sentences(text) return words / sentences if sentences else wordsdef avg_chars_per_word(text: str) -> float: words = text.split() if not words: return 0 return sum(len(w.strip('.,!?;:')) for w in words) / len(words)def flesch_reading_ease(text: str) -> float: """ Approximate Flesch Reading Ease score. 206.835 - 1.015 * (words/sentences) - 84.6 * (syllables/words) We approximate syllables as: max(1, len(word) * 0.4) for each word. """ words = text.split() if not words: return 0 sentences = count_sentences(text) syllables = sum(max(1, int(len(re.sub(r'[^aeiouAEIOU]', '', w)) * 1.2) or 1) for w in words) asl = len(words) / sentences # avg sentence length asw = syllables / len(words) # avg syllables per word score = 206.835 - (1.015 * asl) - (84.6 * asw) return max(0, min(100, score))def grade_reading_level(fre_score: float) -> str: """Convert Flesch Reading Ease to a human label.""" if fre_score >= 70: return "Easy (conversational)" if fre_score >= 60: return "Plain English" if fre_score >= 50: return "Fairly difficult" return "Difficult (too complex for cold email)"# ─── Analysis functions ───────────────────────────────────────────────────────def analyze_subject_line(subject: str) -> Dict: issues = [] warnings = [] if not subject: return {"length": 0, "issues": ["No subject line provided"], "score": 0} length = len(subject) if length > 60: issues.append(f"Too long ({length} chars) — aim for under 50") if length > 50: warnings.append("Subject is getting long — shorter subjects get more opens") if subject.isupper(): issues.append("All caps subject lines trigger spam filters") if re.search(r'!!!|!{2,}', subject): issues.append("Multiple exclamation points look like spam") if subject.startswith("Re:") or subject.startswith("Fwd:"): lower = subject.lower() if lower.startswith("re:") or lower.startswith("fwd:"): warnings.append("Fake Re:/Fwd: subjects feel deceptive — people have learned this trick") if re.search(r'[A-Z]{4,}', subject) and not subject.isupper(): warnings.append("SHOUTING words in subject lines look like spam") if re.search(r'[\U0001F600-\U0001FFFF]', subject): warnings.append("Emojis in subject lines are polarizing and often spam-filtered for B2B") if '?' in subject: warnings.append("Question mark in subject can feel like an ad — test without") # Spam trigger check in subject subject_lower = subject.lower() triggered = [w for w in SPAM_TRIGGERS if w in subject_lower] if triggered: issues.append(f"Spam trigger words in subject: {', '.join(triggered)}") # Score score = 100 score -= len(issues) * 20 score -= len(warnings) * 10 score = max(0, min(100, score)) return { "length": length, "issues": issues, "warnings": warnings, "score": score, }def analyze_body(body: str) -> Dict: body_lower = body.lower() findings = [] deductions = [] word_count = count_words(body) fre = flesch_reading_ease(body) reading_level = grade_reading_level(fre) avg_wps = avg_words_per_sentence(body) # Word count scoring if word_count > 200: deductions.append(("word_count", 15, f"Too long ({word_count} words) — cold emails should be under 150")) elif word_count > 150: deductions.append(("word_count", 5, f"Getting long ({word_count} words) — aim for under 150")) elif word_count < 30: deductions.append(("word_count", 10, f"Very short ({word_count} words) — may lack enough context")) # Sentence length if avg_wps > 25: deductions.append(("readability", 10, f"Sentences average {avg_wps:.0f} words — too complex, aim for 15-20")) # Dead opener check for opener in DEAD_OPENERS: if opener in body_lower: deductions.append(("opener", 20, f"Dead opener detected: '{opener}' — rewrite the opening")) break # Personalization density pers_matches = 0 for pattern in PERSONALIZATION_SIGNALS: matches = re.findall(pattern, body_lower) pers_matches += len(matches) pers_density = pers_matches / word_count * 100 if word_count else 0 if pers_density < 5: deductions.append(("personalization", 10, "Low personalization signals — email may feel generic")) # Spam trigger words in body triggered = [w for w in SPAM_TRIGGERS if w in body_lower] if triggered: deductions.append(("spam", len(triggered) * 5, f"Spam trigger words: {', '.join(triggered[:5])}")) # Weak CTA check for weak in WEAK_CTA: if weak in body_lower: deductions.append(("cta", 10, f"Weak CTA: '{weak}' — be more direct")) break # Strong CTA check has_strong_cta = any(re.search(p, body_lower) for p in STRONG_CTA_PATTERNS) if not has_strong_cta: deductions.append(("cta", 15, "No clear CTA detected — every cold email needs a single, direct ask")) # HTML check if re.search(r'<html|<body|<table|<div|style="|font-family:', body_lower): deductions.append(("format", 20, "HTML detected — plain text emails get better deliverability for cold outreach")) # Multiple links links = re.findall(r'https?://', body) if len(links) > 2: deductions.append(("links", 10, f"{len(links)} links detected — keep to 1-2 max for cold email")) # Calculate score total_deduction = sum(d[1] for d in deductions) score = max(0, min(100, 100 - total_deduction)) return { "word_count": word_count, "reading_ease_score": round(fre, 1), "reading_level": reading_level, "avg_words_per_sentence": round(avg_wps, 1), "personalization_density": round(pers_density, 1), "has_strong_cta": has_strong_cta, "spam_triggers": triggered, "deductions": [(d[2], d[1]) for d in deductions], "score": score, }# ─── Report printer ───────────────────────────────────────────────────────────def grade(score: int) -> str: if score >= 85: return "🟢 Strong" if score >= 65: return "🟡 Decent" if score >= 45: return "🟠 Needs work" return "🔴 Rewrite"def print_report(results: List[Dict]) -> None: print("\n" + "═" * 64) print(" COLD EMAIL SEQUENCE ANALYSIS") print("═" * 64) scores = [] for r in results: email_num = r["email"] subj = r["subject_analysis"] body = r["body_analysis"] overall = r["overall_score"] scores.append(overall) print(f"\n── Email {email_num}: \"{r['subject']}\" ──") print(f" Overall: {overall}/100 {grade(overall)}") print(f"\n Subject ({subj['length']} chars): {subj['score']}/100") for issue in subj.get("issues", []): print(f" ❌ {issue}") for warn in subj.get("warnings", []): print(f" ⚠️ {warn}") print(f"\n Body Analysis:") print(f" Words: {body['word_count']} | " f"Reading: {body['reading_level']} | " f"Avg sentence: {body['avg_words_per_sentence']} words | " f"Personalization density: {body['personalization_density']}%") print(f" CTA: {'✅ Clear ask detected' if body['has_strong_cta'] else '❌ No clear CTA found'}") if body.get("spam_triggers"): print(f" ⚠️ Spam triggers: {', '.join(body['spam_triggers'])}") if body.get("deductions"): print(f"\n Issues found:") for desc, pts in body["deductions"]: print(f" [-{pts:2d}] {desc}") avg = sum(scores) // len(scores) if scores else 0 print(f"\n{'═' * 64}") print(f" SEQUENCE OVERALL: {avg}/100 {grade(avg)}") print(f" Emails analyzed: {len(results)}") # Sequence-level observations print("\n Sequence observations:") word_counts = [r["body_analysis"]["word_count"] for r in results] if all(abs(word_counts[i] - word_counts[i-1]) < 20 for i in range(1, len(word_counts))): print(" ⚠️ All emails are similar length — vary length across sequence") if len(results) > 1: last_body = results[-1]["body_analysis"] if last_body["word_count"] > 100: print(" ⚠️ Final email (breakup) should be shorter — 3-5 sentences max") print("═" * 64 + "\n")# ─── Sample data ──────────────────────────────────────────────────────────────SAMPLE_SEQUENCE = [ { "email": 1, "subject": "your SDR team expansion", "body": ( "Saw you're hiring four SDRs simultaneously — that's a significant scale-up.\n\n" "The challenge most teams hit at this stage isn't recruiting — it's ramp time. " "When you're adding four people at once, the gaps in your onboarding process " "become very expensive very fast. The average ramp in your segment is around " "4.5 months; the fastest teams we've seen get it to 2.5.\n\n" "We've helped three similar-sized SaaS teams compress that gap. Happy to share " "what worked if it's useful.\n\n" "Worth 15 minutes to compare notes?" ), }, { "email": 2, "subject": "re: your onboarding stack", "body": ( "I hope this email finds you well. I wanted to follow up on my previous email.\n\n" "Just checking in to see if you had a chance to review what I sent. " "As mentioned, our platform offers a comprehensive suite of tools designed to " "help sales teams of all sizes achieve unprecedented growth through our " "revolutionary AI-powered onboarding solution.\n\n" "I'd love to schedule a 45-minute product demo at your earliest convenience. " "Please don't hesitate to reach out if you have any questions. " "I look forward to hearing from you!\n\n" "Click here to book a time: https://calendly.com/example" ), }, { "email": 3, "subject": "SDR ramp benchmark", "body": ( "One data point that might be useful: across the 40 SaaS teams we've benchmarked, " "the ones with the fastest SDR ramp time don't hire the most experienced reps — " "they invest more heavily in structured onboarding in the first 30 days.\n\n" "Happy to share the full breakdown. No catch — just thought it might be relevant " "given where you're headed.\n\n" "Useful?" ), }, { "email": 4, "subject": "quick question", "body": ( "Is SDR onboarding actually a priority right now, or is the timing just off?\n\n" "No judgment either way — just helps me know whether it's worth staying in touch." ), }, { "email": 5, "subject": "last one", "body": ( "I'll stop cluttering your inbox after this one.\n\n" "If scaling your SDR ramp time ever becomes a priority, happy to reconnect — " "just reply here.\n\n" "If there's someone else at your company who owns sales enablement, " "a name would go a long way.\n\n" "Either way, good luck with the expansion." ), },]# ─── Main ─────────────────────────────────────────────────────────────────────def main(): import argparse parser = argparse.ArgumentParser( description="Analyzes a cold email sequence for quality signals. " "Evaluates word count, reading level, personalization, CTA clarity, " "spam triggers, and subject lines. Scores each email 0-100." ) parser.add_argument( "file", nargs="?", default=None, help="Path to a JSON file containing the email sequence. " "Use '-' to read from stdin. If omitted, runs embedded sample." ) args = parser.parse_args() if args.file: if args.file == "-": sequence = json.load(sys.stdin) else: try: with open(args.file, "r", encoding="utf-8") as f: sequence = json.load(f) except FileNotFoundError: print(f"Error: File not found: {args.file}", file=sys.stderr) sys.exit(1) except json.JSONDecodeError as e: print(f"Error: Invalid JSON: {e}", file=sys.stderr) sys.exit(1) else: print("No file provided — running on embedded sample sequence.\n") sequence = SAMPLE_SEQUENCE results = [] for email in sequence: subject = email.get("subject", "") body = email.get("body", "") email_num = email.get("email", len(results) + 1) subject_analysis = analyze_subject_line(subject) body_analysis = analyze_body(body) # Overall score: 30% subject, 70% body overall = int(subject_analysis["score"] * 0.3 + body_analysis["score"] * 0.7) results.append({ "email": email_num, "subject": subject, "subject_analysis": subject_analysis, "body_analysis": body_analysis, "overall_score": overall, }) print_report(results) # JSON output for programmatic use summary = { "emails_analyzed": len(results), "average_score": sum(r["overall_score"] for r in results) // len(results) if results else 0, "results": [ { "email": r["email"], "subject": r["subject"], "score": r["overall_score"], "word_count": r["body_analysis"]["word_count"], "has_strong_cta": r["body_analysis"]["has_strong_cta"], "spam_triggers": r["body_analysis"]["spam_triggers"], "subject_score": r["subject_analysis"]["score"], } for r in results ], } print("── JSON Output ──") print(json.dumps(summary, indent=2))if __name__ == "__main__": main()
Install this Skill
Skills give your AI agent a consistent, structured approach to this task — better output than a one-off prompt.