
Cold Email Outreach
- 77 installs
- 67 repo stars
- Updated August 4, 2026
- hyperfx-ai/marketing-skills
Helps with ai & agent building tasks.
About
cold-email-outreach is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- cold-email-outreach
- AI & Agent Building
- AI-coding skill
Cold Email Outreach by the numbers
- 77 all-time installs (skills.sh)
- +8 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #5,386 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/hyperfx-ai/marketing-skills --skill cold-email-outreachAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 77 |
|---|---|
| repo stars | ★ 67 |
| Last updated | August 4, 2026 |
| Repository | hyperfx-ai/marketing-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Cold Email Outreach
End-to-end cold outreach: research, draft, send, follow up, route replies. Strategy is grounded in proven hook frameworks (number-led / question / pain-point / benefit-first); the execution runs on Apollo, Firecrawl, the LinkedIn scraper, and Gmail through the Hyper MCP.
Out of scope — defer to other skills
| Request | Send them to |
|---|---|
| Lifecycle / nurture sequences for warm leads (welcome, onboarding, re-engagement, win-back) | email-lifecycle (planned) |
| LinkedIn DMs, connection requests, or Sales Navigator workflows | (planned) |
| Lead scoring, routing, deal-stage updates after a reply | crm-revops (planned) |
| Scraping competitor ads | meta-ads-library |
Requirements
- Hyper MCP installed and connected. https://app.hyperfx.ai/mcp
- Gmail integration connected at https://app.hyperfx.ai/integrations — supplies the sending account.
- Apollo integration connected — supplies prospect search and email enrichment.
- Firecrawl (bundled) — for company-page signals.
- Optional: LinkedIn scraper (bundled, runs through Apify) — for richer per-prospect personalization.
If gmail_send_message and apollo_mixed_people_search are not in the agent's tool list, stop and tell the user to enable the Hyper MCP and connect Gmail + Apollo.
Tool surface
| Phase | Tools |
|---|---|
| Prospect research | apollo_mixed_people_search, apollo_mixed_companies_search, apollo_people_bulk_match (preferred for 2+ enrich), apollo_people_match (single only) |
| Per-prospect signals | firecrawl_scrape_url, firecrawl_batch_scrape, firecrawl_extract_branding, firecrawl_screenshot, scrape_linkedin_profiles (conditional — requires LinkedIn Apify integration) |
| Drafting | gmail_create_draft, gmail_update_draft, gmail_get_draft, gmail_list_drafts |
| Sending | gmail_send_message, gmail_send_draft, gmail_reply_to_message |
| Reply routing | gmail_list_messages, gmail_get_message, gmail_create_label, gmail_add_labels, gmail_remove_labels, gmail_move_email_to_label (takes `label_id` string, not `label_ids` array) |
Critical rules
1. Never loop `apollo_people_match` for multiple prospects. For 2+ records always batch into apollo_people_bulk_match. Apollo's tool description warns about this explicitly — looping single-match calls burns credits and is much slower. 2. Default send mode = drafts-first for review. For any campaign with 4+ prospects, draft the first 1–3 with gmail_create_draft, show them to the user, get explicit approval, then batch-send the rest with gmail_send_message. Never send a full campaign without showing samples first. 3. One label per campaign. Create a cold/<campaign-name> label with gmail_create_label at the start, apply it to every send, then track replies by searching that label. This is what makes Phase 6 reply routing actually work. 4. Stay under Gmail's send limits. ~500 messages/day per consumer Gmail account, ~2,000/day per Workspace user. Space sends out — see `references/deliverability.md` for warming and per-day pacing. 5. Personalization must connect to the problem. If the personalized opener could be deleted and the email still makes sense, it isn't doing any work. The opener should naturally bridge into why you're emailing. 6. One ask per email, one CTA. Interest-based (Worth exploring?) beats meeting requests on cold touch 1. 7. Honor unsubscribes immediately. Apply an unsubscribed label on any "remove me / not interested" reply and never re-target that address from the same Hyper workspace.
Workflow
Phase 1 — Define the campaign (always do this first)
Get the user to commit to:
1. ICP — Role(s), industry, company size, tech stack, geography. Concrete: "Heads of Growth at US-based pre-seed-to-Series-A B2B SaaS, 10–50 employees, using HubSpot." 2. The ask — What does a "yes" look like? (15-min call, async reply, demo, intro to someone else.) 3. Value prop in one sentence — "We help X do Y so they can Z." 4. Proof point — One specific result: "We helped Notion cut their CAC by 31% in 90 days." (Made up examples are worse than no example — get a real one.) 5. Trigger / signal (optional but powerful) — Funding round, hiring, pricing-page change, recent blog post, product launch, leadership change. 6. Sender + reply-to — Which Gmail account is sending. (Confirm with gmail_list_labels to verify the integration is live.) 7. Volume + cadence — Total prospects, max sends/day, follow-up gap pattern.
If they're stuck on any of these, push back. A campaign without proof or a clear ask will not perform regardless of how clever the writing is.
Phase 2 — Build & enrich the prospect list
# Search by ICP
apollo_mixed_people_search(
person_titles=["Head of Growth", "VP Growth", "Director of Growth"],
organization_num_employees_ranges=["11,50"],
person_locations=["United States"],
per_page=50,
)Then for the prospects you actually want to contact, batch-enrich for emails:
# CORRECT — one bulk call for many prospects
apollo_people_bulk_match(
details=[
{"first_name": "...", "last_name": "...", "domain": "..."},
...up to 10 per call...
],
reveal_personal_emails=True,
)Only fall back to apollo_people_match for single-prospect lookups (e.g., the user pastes one LinkedIn URL).
For deeper company-level context (industry, revenue range, tech stack), call apollo_mixed_companies_search by organization name on the companies you want to enrich. Note that person search results already include core company fields (headcount, industry, location) — only reach for apollo_mixed_companies_search when you need data beyond what the person search returns.
Phase 3 — Per-prospect signals (the personalization layer)
For each prospect, gather one specific observation that connects to the problem you solve. Use the cheapest signal that works:
| Cost | Tool | Use it for |
|---|---|---|
| Free (already have it) | Apollo response fields | Title change, recent role start, company headcount jump, funding |
| Cheap | firecrawl_scrape_url of the careers / pricing / blog page | "You're hiring 4 SDRs", "Pricing pages says enterprise plan launching", "Latest blog post is about X" |
| Cheap (multi-page) | firecrawl_batch_scrape | Same observation across many sites in one call |
| Medium | firecrawl_extract_branding | Brand voice for the email tone, brand colors if you'll send a follow-up image |
| Higher (conditional) | scrape_linkedin_profiles(profile_urls=[...]) (requires LinkedIn Apify integration — skip if not connected) | Recent post, mutual connection, recent job change, school/employer overlap |
Personalization tiers (use the highest tier you can afford for this campaign):
- Tier 1 (mass / low-effort) — first name + role + company + industry. Acceptable only when the value prop is sharp enough to carry the email on its own. Reply rates: low.
- Tier 2 (signal-based) — the prospect is in a role/stage where the problem you solve is acute (e.g., a new Head of Growth in their first 60 days). Reply rates: meaningfully better.
- Tier 3 (observation-based) — references something from the company site, pricing page, careers page, or a recent product launch. This is the sweet spot.
- Tier 4 (deep) — references a recent LinkedIn post, blog post they wrote, or talk they gave. Reserve for high-value targets.
Anything below Tier 2 should be treated with suspicion — {{FirstName}} swaps don't count as personalization.
Phase 4 — Draft emails (drafts-first by default)
Pick a framework that matches the situation. The four shapes that consistently work:
- Observation → Problem → Proof → Ask — "You're hiring SDRs. That usually means meetings/SDR ratio is the bottleneck. We helped [company] hit X. Worth exploring?"
- Question → Value → Ask — "Struggling with [problem]? We do [Y]. [Company Z] saw [result]. Worth a look?"
- Trigger → Insight → Ask — "Congrats on [funding/launch]. That usually creates [Y challenge]. We've helped similar teams with that. Curious?"
- Story → Bridge → Ask — "[Similar company] had [problem]. They [solved it this way with us]. Relevant to you?"
See `references/frameworks.md` for full examples and when each shape works best.
Subject lines. Short, lowercase, internal-looking. 2–4 words. No emojis, no first names, no urgency tricks. Targets: looks-like-a-colleague-sent-it. Examples: quick question, reply rates, hiring ops, q3 forecast, for {{company}}. Avoid: Increase your revenue 10x!, John, are you free Thursday?, [URGENT] follow-up.
Voice rules.
- Write like a peer, not a vendor. Use contractions. Read it aloud — if it sounds like marketing copy, rewrite it.
- "You/your" should outnumber "I/we" by ≥2:1.
- Every sentence must move the reader toward replying. The best cold emails feel like they could have been shorter, not longer.
- Calibrate to seniority: C-suite → ultra-brief and peer-level. Mid-level → more specific value. Technical → precise, no fluff, respect their intelligence.
What to avoid (these are the AI-tells reviewers immediately spot):
- "I hope this email finds you well." / "I came across your profile."
- "leverage", "synergy", "best-in-class", "leading provider", "circle back"
- Feature dumps. One proof point beats ten features.
- HTML, images, multiple links.
- Fake
Re:/Fwd:subject lines. - Identical templates with only
{{FirstName}}swapped. - Asking for a 30-minute call on touch 1.
Drafts-first send pattern (default for any 4+ prospect campaign):
# 1. Create label for the campaign once — capture the returned id
label = gmail_create_label(name="cold/q3-growth-leads")
campaign_label_id = label["id"]
# 2. Draft the first 1-3 prospects for user review
for p in prospects[:3]:
gmail_create_draft(
to=p["email"],
subject="quick question",
body=render_email(framework="observation", prospect=p),
)
# 3. Show drafts to user, await explicit approval
# 4. After approval, send and label each message
for p in prospects[3:]:
result = gmail_send_message(
to=p["email"],
subject="quick question",
body=render_email(framework="observation", prospect=p),
)
gmail_add_labels(message_id=result["id"], label_ids=[campaign_label_id])If the user wants every email reviewed, use gmail_create_draft for all of them and send via gmail_send_draft after approval. If the user is confident and the templates are pre-approved (e.g., they've run this campaign shape before), you can skip directly to gmail_send_message from prospect 1. Default behavior is drafts-first.
Phase 5 — Run the follow-up cadence
3–5 total touches with widening gaps. Each follow-up adds something new — a different angle, fresh proof, a useful resource. "Just checking in" gives the reader no reason to respond.
Default cadence (adjust to the user's situation):
| Touch | Day | Angle | Tool |
|---|---|---|---|
| 1 | 0 | Initial framework (observation/question/trigger/story) | gmail_send_message |
| 2 | +3 | Reply in the same thread, add a one-line specific proof | gmail_reply_to_message |
| 3 | +7 | Different angle (if 1 was observation, try question or value-first) | gmail_reply_to_message |
| 4 | +14 | Useful free resource — case study, calculator, teardown | gmail_reply_to_message |
| 5 | +21 | Breakup email. "Closing your file unless I hear back. Worth keeping the door open?" | gmail_reply_to_message |
Always reply in the original thread (gmail_reply_to_message with the message_id returned from the touch-1 send) — preserves context and improves deliverability. See `references/follow-up-sequences.md` for angle rotation, breakup-email templates, and how to prune prospects mid-sequence.
Phase 6 — Track replies and route them
# Pull all replies on the campaign label from the last 7 days
gmail_list_messages(query="label:cold/q3-growth-leads is:unread newer_than:7d")For each reply, read the body with gmail_get_message(message_id=...), classify it, and label:
| Classification | Label | What to do |
|---|---|---|
| Interested ("yes / tell me more / send a calendar") | cold/q3-growth-leads/interested | Stop the sequence. Hand off (eventually crm-revops once shipped). |
| Objection ("we use X / no budget / try us in Q4") | cold/q3-growth-leads/objection | Reply with one specific response, then stop sequence. |
| Not now ("circle back later") | cold/q3-growth-leads/not-now | Stop sequence. Re-tag for re-engagement in 90 days. |
| Unsubscribe ("remove me / not interested") | cold/q3-growth-leads/unsubscribed | Stop sequence. Add unsubscribed global label. Never re-contact. |
| Out-of-office | cold/q3-growth-leads/ooo | Pause sequence, resume after the OOO end date in the message. |
Apply classification and clear unread with two separate calls:
gmail_add_labels(message_id=..., label_ids=[classification_label_id])
gmail_remove_labels(message_id=..., label_ids=["UNREAD"])Create the sub-labels once with gmail_create_label and capture their IDs before the routing loop.
Quality check (before any send)
Read each draft against this gut-check. Reject any that fail more than one:
- Does it sound like a human wrote it? (Read it aloud.)
- Would you reply if you got this?
- Does every sentence serve the reader, not the sender?
- Is the personalization connected to the problem you solve — not just a generic compliment?
- Is there one clear, low-friction ask?
- Does the subject line look like it came from a colleague?
- Is the email under ~120 words on touch 1?
Reference workflows
For long-form material — read on demand:
| Reference | When to read |
|---|---|
| `references/frameworks.md` | Choosing a framework, full examples, calibrating tone by seniority |
| `references/follow-up-sequences.md` | Building the multi-touch cadence, angle rotation, breakup email templates |
| `references/deliverability.md` | Gmail rate limits, sender warming, SPF/DKIM/DMARC, list hygiene, blocklist recovery |
Deliverability
The single biggest reason cold-email campaigns fail is not the copy — it's that the emails don't make it to the inbox. Send infrastructure is the part most operators ignore until they're already burned. Read this before the first send.
The mental model
Inbox providers (Gmail, Outlook, Apple Mail) decide whether to deliver, spam-folder, or block your mail based on three signals:
1. Identity — Can the provider verify the sender is who they claim to be? (SPF, DKIM, DMARC.) 2. Reputation — How does this domain / IP behave? (Bounce rate, spam complaints, engagement, age.) 3. Content — Does this email look like spam? (Spammy words, too many links, image-heavy, fake Re: prefixes, "no-reply" sender, unsubscribe nags.)
Cold outreach lives or dies on identity + reputation. The Hyper MCP can't fix either for you — they're DNS / inbox-level concerns. But it can avoid making them worse.
Hard rules — never break these
1. Never cold-send from your primary work domain. If your real email is you@yourcompany.com, do not run cold outbound from that mailbox. One spam complaint trains Gmail to send your internal mail to spam too. Use a dedicated sending domain (e.g., you@yourcompany-sales.com or you@get-yourcompany.com). 2. Never start a cold campaign from a fresh-zero-day mailbox. A brand-new Gmail / Workspace account that suddenly sends 100 emails on day 1 will be flagged within 24 hours. Warm for at least 2–4 weeks first. 3. Never include images, HTML formatting, or more than 1 link in a cold email. Plain text reads as human; rich formatting reads as marketing. 4. Never use fake `Re:` or `Fwd:` subject prefixes. Provider filters spot this and Gmail explicitly penalizes it. 5. Never email purchased lists or scraped emails you can't verify. Apollo's verified emails (with reveal_personal_emails=True) have far lower bounce rates than firstname@domain.com guesses. 6. Never let bounce rate exceed 5% on a campaign. Above that, the inbox provider will start spam-foldering you. Above 10%, you're at risk of getting suspended. See List hygiene below.
Sending limits
Gmail's published soft limits (per account, per 24h rolling window):
| Account type | Sends/day | Recipients/day (across all sends) |
|---|---|---|
| Free Gmail | ~500 | ~500 |
| Google Workspace (Business Starter / Standard) | ~2,000 | ~2,000 |
| Google Workspace (Business Plus / Enterprise) | ~2,000 | ~10,000 |
These are soft — Gmail throttles before they suspend, but if you hit the throttle the campaign stalls. Plan to use 50–70% of the cap so you have headroom for follow-ups.
For new / cold sending accounts, start at 20–40 sends/day and ramp gradually (see warming below). The cap is the limit; the actual safe volume on a fresh account is much lower.
Sender warming (the 2–4 week setup)
Skip warming and you'll burn the domain before the campaign clears Phase 4.
Week 1 — establish a real-mail baseline (20 sends/day)
- Send and reply to real human conversations from the new mailbox. Not bots — actual people.
- Subscribe to 3–5 newsletters (Stratechery, Lenny's, your industry's daily). Open and click sometimes.
- Don't send any cold mail.
Week 2 — small-batch warm sends (40 sends/day)
- Start sending real, opted-in mail (e.g., to existing customers, partners, internal team) to build positive engagement signal.
- Add the new sender's email signature to other mailboxes you control so people send mail to it (incoming mail = positive reputation).
Week 3 — first cold sends (50 sends/day)
- Begin Phase 4 sends in batches of 50 or fewer.
- Monitor: bounce rate < 3%, no spam complaints. If you cross either, pause and diagnose.
Week 4+ — scale to target volume
- Increase by ~25% per day if metrics stay clean.
- Cap at 70% of Gmail's daily limit (so ~350/day for free, ~1,400/day for Workspace).
There are also paid services (Mailwarm, Lemwarm, Warmup Inbox) that automate the warming bot-traffic. They speed things up but the inbox providers are getting better at detecting bot warming — manual + real correspondence is more durable.
DNS — SPF, DKIM, DMARC
These three records on the sending domain's DNS tell inbox providers "yes, this server / service is allowed to send mail as this domain."
| Record | What it does | Where to set |
|---|---|---|
| SPF | Lists the IPs / services authorized to send as this domain | DNS TXT record |
| DKIM | Cryptographic signature on the email proving it wasn't tampered with in transit | DNS TXT record (key provided by Google Workspace) |
| DMARC | Tells receivers what to do if SPF or DKIM fail (allow / quarantine / reject) and reports to you | DNS TXT record (_dmarc.yourdomain.com) |
Minimum config for cold sending from Google Workspace
# SPF — TXT record on yourdomain.com
v=spf1 include:_spf.google.com ~all
# DKIM — get the actual key from Workspace Admin → Apps → Google Workspace → Gmail → Authenticate email
google._domainkey.yourdomain.com TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqG..."
# DMARC — TXT record on _dmarc.yourdomain.com
v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@yourdomain.com; pct=100Start with p=quarantine (failed mail goes to spam, not rejected). Move to p=reject only after a few weeks of clean DMARC reports.
The Hyper MCP doesn't manage DNS — these have to be set on the registrar / DNS host. Verify them with dig or mxtoolbox.com before the first send.
List hygiene
Bad addresses tank deliverability faster than bad copy. Before adding a prospect to a campaign:
1. Use Apollo's verified emails. Pass reveal_personal_emails=True on apollo_people_bulk_match — Apollo flags verified vs guessed. 2. Skip the role-based addresses for cold outbound. info@, sales@, hello@ rarely reply and often hit shared inboxes that spam-flag everything. 3. Honor Apollo's `email_status`. If status is unverified or bounced, drop the prospect — don't try to email anyway. 4. Drop prospects who have ever replied "remove me / unsubscribe / not interested" — across any campaign in the workspace. Maintain a global unsubscribed Gmail label and check against it before each campaign:
gmail_list_messages(query="label:unsubscribed to:<prospect-email>")If anything comes back, do not send.
Pacing inside the day
Don't dump 100 sends in 5 minutes. Spread them out:
- Time of day: 8–11am and 1–3pm in the recipient's time zone get the highest open rates.
- Per-minute cap: Stay under 1 send per 6 seconds. Faster = bot-like.
- Per-hour cap: Stay under 30–50 sends/hour from a single mailbox.
- Day of week: Tuesday–Thursday outperform Monday and Friday for B2B. Avoid sends on weekends.
Implementation: run the send loop over a gmail_send_message call, sleep ~15–60 seconds between sends, batch ~30 sends per hour. The agent can drive this directly.
Monitoring — what to check after every campaign day
| Metric | Target | How to check | What it means if bad |
|---|---|---|---|
| Bounce rate | < 3% | Apollo + Gmail bounce notifications | List quality is poor — re-enrich or pause |
| Spam complaint rate | < 0.1% | Gmail Postmaster Tools | Copy reads as spam, or list is wrong-target |
| Reply rate | > 2% | gmail_list_messages(query="label:cold/<campaign> to:me newer_than:7d") | Copy / targeting is off, or list is dead |
| Open rate | If tracked: 40%+ | (open tracking pixels are detectable and hurt deliverability — generally skip) | Subject line is weak |
Gmail Postmaster Tools is the single most useful free dashboard. Set it up the day you set up the sending domain. It shows your domain's reputation directly.
When you've already been blocked or spam-foldered
Signals you're in trouble:
- Replies suddenly stop (without copy changes).
- Test sends from the cold mailbox to your own personal Gmail land in spam.
- Your sending IP shows up on a public blocklist (check at mxtoolbox.com).
Recovery playbook:
1. Stop the campaign immediately. Continuing makes it worse. 2. Diagnose: Bounce rate? Spam complaints? Sudden volume jump? Bad list? 3. Wait 2–4 weeks before sending any cold mail from that domain again. 4. Restart warming from week 1 — yes, all the way back. 5. *Move to a new sending sub-domain* (e.g., from get-yourcompany.com to try-yourcompany.com) if reputation damage is severe.
The fastest way to recover from a deliverability problem is not to be in one in the first place. Spend the 2–4 weeks on warming up front; the math works out.
Compliance — quick legal checklist
Cold outbound is legal in most jurisdictions, but with rules. Mostly common-sense:
- CAN-SPAM (US): Include a real physical address in the email footer. Honor opt-outs within 10 business days. Don't use deceptive subject lines.
- GDPR (EU): Cold B2B outreach is generally allowed under "legitimate interest" if (a) the email is on the prospect's published professional channels, (b) the message is relevant to their job, and (c) they have a clear way to opt out. Don't email EU consumers without explicit opt-in.
- CASL (Canada): Stricter — generally requires implied or express consent. Be careful with Canadian prospects.
- PIPL / CCPA / state privacy laws: Mostly affect data storage, not the cold-send itself, but worth verifying for your jurisdiction.
The skill defaults assume B2B outbound to professional contacts based on Apollo-verified data, with a clear opt-out, a real footer, and unsubscribe enforcement via the global unsubscribed label. That's compliant in most cases — but if you're operating in a high-risk jurisdiction or industry, get legal sign-off before scaling.
Follow-Up Sequences
The reply rate on the first touch is the floor, not the ceiling. Most positive replies in cold outbound come on touches 2–4. The follow-up sequence is where the campaign is won or lost.
The non-negotiables
1. Each follow-up adds something new. A different angle, fresh proof, a useful resource. "Just checking in" is a tax on the reader's attention with no payoff — it nukes reply rates and trains the prospect to ignore you. 2. Each email stands alone. Don't assume the prospect read the previous touches. A follow-up that only makes sense if you read touch 1 has already lost. 3. Always reply in the original thread. Use gmail_reply_to_message with the thread_id from touch 1 (saved from the gmail_send_message response). Threading preserves context and helps deliverability — Gmail treats threaded replies more favorably than fresh sends to the same address. 4. Widen the gaps as the sequence goes. First gap is short, last gap is long. Hammering on a tight cadence reads as desperate and trips spam filters. 5. Honor the breakup. If the breakup email is "closing your file unless I hear back," actually close the file. Sending a sneaky "actually one more thing" after a breakup nukes credibility.
Default cadence (5 touches over 21 days)
| Touch | Day | Gap | Angle | Subject pattern | Tool |
|---|---|---|---|---|---|
| 1 | 0 | — | Initial framework (Observation / Question / Trigger / Story) | Short and lowercase | gmail_send_message |
| 2 | +3 | 3d | Same angle, sharpened — add a one-line specific proof | Reply (no subject change) | gmail_reply_to_message |
| 3 | +7 | 4d | Different angle from touch 1 | Reply | gmail_reply_to_message |
| 4 | +14 | 7d | Useful free resource (case study, calculator, teardown) | Reply | gmail_reply_to_message |
| 5 | +21 | 7d | Breakup. "Closing your file." | Reply | gmail_reply_to_message |
Adjust to context: a high-stakes enterprise campaign might run 7 touches over 6 weeks; a SMB volume play might run 3 touches in 7 days. Five touches over 21 days is the sane default.
Angle rotation — what "different angle" actually means
If touch 1 was Observation → Problem → Proof → Ask, then touch 3 needs to not be a sharper observation. Rotate.
| Touch 1 angle | Touch 3 angle (good) | Touch 3 angle (bad — same shape) |
|---|---|---|
| Observation (what you noticed) | Question (force them to answer one in their head) | Another observation about a different page on their site |
| Question (what's going on with X) | Story (similar customer's outcome) | Variant of the same question |
| Trigger (recent event) | Mechanism (specifically how you'd help) | Same trigger restated |
| Story (customer A's outcome) | Question (does this map to your situation?) | Customer B's story |
The point of rotating is to give the prospect new information — a new way to evaluate whether you're worth a reply.
Touch-by-touch templates (Observation framework example)
Touch 1 — initial
See `frameworks.md` for the full Observation example. It opens the thread.
Touch 2 (+3 days) — sharpened proof
Touch 2 (reply in same thread, no subject change)
Quick follow-up — forgot to mention: the result with Notion was on a
team your size (12 SDRs). They cut their per-prospect research time
from 90s to 8s in the first 2 weeks.
Worth seeing how?
— SamTwo sentences. Adds a new concrete data point (team size match → makes the proof more credible for this reader). Same ask.
Touch 3 (+7 days) — different angle (Question)
Touch 3 (same thread)
Different angle — out of curiosity, are you running prospect research
in-house, with an agency, or with one of the prefab tools?
Asking because the play we use only really clicks if you're in one of
the first two camps.
— SamNow you're qualifying. Many prospects who didn't reply to touch 1–2 will reply to a question because it's a low-cost answer. And the answer routes the rest of the conversation.
Touch 4 (+14 days) — useful resource
Touch 4 (same thread)
Switching gears — wrote up the Ramp onboarding teardown I mentioned in
[short link]. Five-min read, no signup. Even if we're not a fit, the
section on the 90-day SDR ramp curve is probably useful.
Holler if anything resonates.
— SamThe resource has to be genuinely useful and related to the problem — not a sales deck dressed up as a "guide." If you don't have a real piece, skip touch 4 and go straight to the breakup.
Touch 5 (+21 days) — breakup
Touch 5 (same thread)
Going to close the file on my end since the timing's clearly off.
If outbound prospect research moves up the priority list later, my
inbox is open — no follow-up from me until then.
— SamTwo sentences. No "one last thing" pitch. The breakup email is your last touch — honor it. Conventional wisdom (and most data) says breakup emails get the second-highest reply rate after touch 1. They work because you mean it.
Pruning prospects mid-sequence
Stop sending immediately if any of these happen:
| Signal | Action | Tool |
|---|---|---|
| Reply (any classification — interested, objection, not now, unsubscribe) | Stop sequence. Apply classification label. | gmail_add_labels (and gmail_remove_labels if removing) |
| Hard bounce | Remove from sequence. Mark email as bad in Apollo (don't enrich again). | (manual) |
| Soft bounce 2x in a row | Pause sequence. Investigate (mailbox full, vacation auto-reply). | (manual) |
| OOO / vacation auto-reply | Pause sequence, resume after the OOO end date in the message body. | gmail_list_messages(query="is:oof from:<email>") |
| "Wrong contact, talk to X" | Stop sequence to original contact. Start a new (1-touch) sequence to X. | New apollo_people_match + gmail_send_message |
The single biggest reason cold-email campaigns get blocked: continuing to email people who replied "remove me" because the operator didn't classify the reply correctly. Build the label discipline early.
How to actually run the cadence
The Hyper MCP doesn't have a native cron — the agent / user has to drive the schedule. Two patterns:
Pattern A — Daily checkin (recommended for ≤200 prospects)
Once a day the user says: "Run the cold-email cadence — send any due touches and pull replies."
1. gmail_list_messages(query="label:cold/<campaign> is:unread newer_than:1d")
→ process replies, apply classification labels, prune sequence
2. For each prospect with no reply and last touch > N days ago:
- gmail_reply_to_message(thread_id=..., body=<next angle>)
- gmail_add_labels(message_id=..., label_ids=["cold/<campaign>/touch-N"])
3. Report: X sent, Y replied, breakdown by classification.The agent maintains state via Gmail labels — no external scheduler needed.
Pattern B — Per-touch single-prompt
The user prompts each touch separately:
"Send touch 2 to anyone in cold/q3-growth-leads who got touch 1 ≥ 3 days ago and hasn't replied."Same toolchain, just on-demand instead of daily. Useful for low-volume / high-stakes outreach where the user wants to eyeball the list each time.
Subject lines on follow-ups
Don't change the subject line on follow-ups — keeping the same subject preserves threading and signals to Gmail that this is conversational, not promotional. The gmail_reply_to_message tool handles this automatically (it uses Re: if Gmail's UI does).
If a thread goes very long (10+ messages), Gmail clients sometimes collapse it. At that point a fresh send with a slightly different subject is fine — but you've already lost the thread.
What never to do in follow-ups
- "Bumping this to the top of your inbox." Performative and annoying.
- "In case you missed it." They didn't miss it, they ignored it. Don't make them feel bad.
- "My boss is asking about you." Manipulative; reads as fake urgency.
- "Last chance!" There is no last chance for a cold email.
- Adding new recipients to the thread (CC'ing the boss). Aggressive and reads as escalation. Don't.
- Sending the same email twice with a different subject line. Gmail / spam filters spot this immediately and the prospect notices.
Volume math (so the campaign doesn't run you over)
5 touches × N prospects × cadence = total sends. Plan around your daily cap:
| Prospects | Sends in the 21-day window | Sends/day (if spread evenly) |
|---|---|---|
| 50 | 250 | ~12 |
| 200 | 1,000 | ~48 |
| 500 | 2,500 | ~120 |
| 1,000 | 5,000 | ~240 |
A consumer Gmail account caps at ~500/day; a Workspace account at ~2,000/day. Campaigns above 1,000 prospects need either a warmed dedicated sending account or a bigger time window. See `deliverability.md` for warming and pacing.
Cold Email Frameworks
Four structures that consistently work for cold outbound. Each one is a shape, not a template — the sentences should always be written fresh in the prospect's context. Pick the framework that fits the situation; don't force a situation into a framework.
When to pick which framework
| Framework | Best when | Risk |
|---|---|---|
| Observation → Problem → Proof → Ask | You have a real, specific signal about the prospect's company | Falls flat if the observation isn't actually relevant |
| Question → Value → Ask | The problem you solve is universally felt by the role | Sounds like a survey if the question is too generic |
| Trigger → Insight → Ask | A recent event (funding, launch, hire, leadership change) makes the problem acute | Window closes fast — use within 2–4 weeks of the trigger |
| Story → Bridge → Ask | You have a strong customer story for a similar company | Requires the prospect to see themselves in the story |
Framework 1 — Observation → Problem → Proof → Ask
The default. Works for ~70% of campaigns. The personalized observation must connect to the problem you solve, otherwise it's just flattery.
Shape
1. One sentence observation (Tier 3 personalization — pulled from firecrawl_scrape_url of the careers / pricing / blog page, or from scrape_linkedin_profiles). 2. The implied problem (one sentence — the because / which usually means bridge). 3. One proof point (one specific result, named customer or metric — never both vague). 4. One low-friction ask (interest-based, not a meeting request).
Example
Subject: hiring sdrs
Saw you're hiring 4 SDRs in NYC. That usually means meetings/SDR is the
gating metric for hitting Q3 targets.
We helped Notion's outbound team go from 1.4 to 3.2 meetings/SDR/week in
60 days by replacing manual research with prefilled signal cards.
Worth a 10-min look?
— SamWhy it works
- The observation is concrete (
hiring 4 SDRs in NYC), not generic (I see you're growing fast). - The bridge says why that matters without explaining the obvious (the prospect knows their own job).
- The proof point is one specific result, named customer, real metric.
- The ask is "10-min look", not "30-minute call to discuss synergies."
Common failure modes
- Observation has no bearing on the problem. "Saw your CEO went to Stanford" → and? If you can't bridge it to the problem in one sentence, drop it.
- Two proof points instead of one. Cuts the perceived sharpness in half.
- Asking for a meeting on touch 1. Use "worth a look?" or "useful?" — meeting comes on the reply.
Framework 2 — Question → Value → Ask
Use when the problem is universally true for the role and you can't get a strong per-prospect signal.
Shape
1. One direct question about the problem. 2. The way you solve it (one sentence — what we / the product does). 3. One proof or social-proof anchor. 4. The ask.
Example
Subject: reply rates
How are reply rates on your outbound right now?
We've built a way to score prospects by intent before SDRs touch the
list — teams using it land at 8-12% reply rates instead of 2-3%.
Beam, Webflow, and Census run this. Worth a peek at how?
— SamWhy it works
- The opening question forces the reader to answer it in their head — and if the answer isn't great, the rest of the email is the obvious next step.
- The value sentence is the mechanism, not just the outcome.
- Three named customers as social proof is fine when you can't single out one.
Common failure modes
- Question is too vague. "Are you happy with your sales numbers?" — everyone says no, but it doesn't qualify anyone.
- Question is rhetorical. "Wouldn't it be great if your team hit quota?" — eye-roll material.
- Skipping the mechanism and going straight to social proof. "Notion uses us. Want to chat?" — what do you actually do?
Framework 3 — Trigger → Insight → Ask
Use only when you have a real recent event for the prospect's company. The trigger must be ≤4 weeks old; older than that and you sound like a stalker / late.
Shape
1. The trigger (specific, dated, verifiable). 2. What the trigger usually means for the role you're emailing (the insight — show that you understand the implication). 3. The ask — usually framed as "happy to share what we've seen with similar [stage / situation]."
Example
Subject: post-series-b ops
Saw the $40M Series B announcement last week. Congrats.
The teams I've talked to at the Series B → C transition all run into
the same wall: the SDR motion that worked at Series A doesn't scale
past 25 reps without throwing CAC.
Happy to share what the 3-4 GTM teams I've worked with at this stage
ended up doing. Useful?
— SamWhy it works
- Trigger is dated, specific, and easy to verify.
- The insight earns trust — you're not pitching, you're naming a problem the prospect almost certainly already feels.
- The ask is consultative, not commercial.
Common failure modes
- Trigger is too old. "Saw your funding round 8 months ago…" → late and weird.
- Trigger is fake or speculative. "Heard you might be raising soon" → don't.
- The insight is generic. "Funding usually means growing pains." → say something the prospect doesn't already know.
Framework 4 — Story → Bridge → Ask
Use for high-trust outreach to enterprise / senior buyers when you have a strong customer story for a very similar company. The story is the email — the rest is just connective tissue.
Shape
1. A 2-sentence customer story — name, situation, result. 2. The bridge — "Reason I'm writing — looks like you might be in a similar spot." 3. The ask — usually framed as "want me to walk you through how they did it?"
Example
Subject: ramp's onboarding
Quick story: Ramp's outbound team had 60 SDRs onboarding in 4 months
last year and was losing 30% in the first 90 days because their ICP
research workflow was 3 hours of manual work before any prospect was
touched.
We rebuilt that workflow as a prefill on top of their CRM — onboarding
ramp time dropped from 11 weeks to 5.
Reason I'm writing — saw you're hiring at a similar pace. Want me to
walk you through how they did it?
— SamWhy it works
- The story is specific enough (numbers, named team, real metric) to feel earned.
- The bridge is the only place the prospect appears — keeps the focus on value already delivered rather than what you'd like to do.
- The ask is "want me to walk you through" — much lower friction than "let's set up a call."
Common failure modes
- The story doesn't apply. Story about a 500-person enterprise sales team sent to a 20-person seed-stage startup → instant pass.
- Two stories. Cuts the believability of both. Pick one.
- Skipping numbers. Story without a metric is gossip.
Calibrating tone by seniority
The framework stays the same; the length and posture change.
C-suite (CEO, CFO, founder)
- Target length: 40–60 words.
- One observation, one proof, one ask. Skip the middle "problem" step — they already know.
- Tone: peer, understated, never explanatory.
- Sign-off with first name only.
Subject: q3
Saw the post about Q3 targets. Curious — are you running the SDR
motion in-house or with an agency right now?
We've got a way to cut research time per prospect from 90s to 8s.
Worth 10 min?
— SamMid-level (Director, Manager)
- Target length: 80–120 words.
- Use the full framework. They want to see that you understand the role.
- Tone: knowledgeable peer, slightly more specific.
- Can include the proof customer's name + the metric.
Technical / IC (Senior SDR, RevOps lead, engineering manager)
- Target length: 60–100 words.
- Be precise. They will judge you on accuracy.
- Drop adjectives. Use the actual mechanism: "we score prospects via a 6-feature gradient boost trained on 2.4M historical replies" beats "we use AI to find better leads."
- Tone: respect their time and their intelligence.
Quick framework chooser
If you can answer yes to:
- "Do I have a specific, dated observation about this prospect's company?" → Framework 1.
- "Was there a funding round / launch / leadership change in the last 4 weeks?" → Framework 3.
- "Do I have a strong customer story for a near-identical company?" → Framework 4.
- "None of the above, but the problem I solve is universal for this role?" → Framework 2.
If none of those are true, the campaign isn't ready to send. Go back to Phase 1.