<!--
Cold Email Campaign Playbook

Part of the free Claude Code template library from BILT AI — https://biltai.io/claude-templates

These are the actual playbooks used to build and ship production SaaS apps with
Claude Code. They're written to be handed to an agent as-is: point Claude at one
and it has the full pattern, the schema, the gotchas, and the "don't do this"
list. Paths, project refs, and org names have been replaced with placeholders
(<your-app>, <supabase-project-ref>, <your-github-org>) — search for "<" and
fill in your own before running.

Free to use, modify, and ship. No attribution required.
-->

# Cold Email Campaign Playbook

**Portable operational workflow for running a cold-email launch campaign for any new SaaS app in the portfolio.**

This is NOT a coded subsystem — it's a discipline + folder convention + decision-log pattern. The reference implementation lives at `reference app` (Kompozy → BILT-warm RE list, ~100k addresses).

For SPF/DKIM/DMARC, IP/domain warmup mechanics, and inbox provisioning, see the sibling spec: `templates/s-tier/EMAIL-INFRASTRUCTURE-PLAYBOOK.md`.

---

## 0. Use this spec

**Inputs:**
- App name (becomes the folder slug — `<APP-NAME>-COLD-EMAIL`)
- Audience source: cold (no prior contact) / warm (engaged with prior campaigns) / hot (paying users of sibling apps)
- Offer: tier name, price, expiry, link destination
- Sending stack: Instantly / Smartlead / lemlist + how many inboxes are warmed
- Stripe products that EXIST today (not what you wish existed — see "Pricing-promise hygiene")

**Outputs:**
- Working folder under `Active/<APP-NAME>-COLD-EMAIL/` with versioned email copy, append-only daily metrics, and a rolling DECISIONS log
- A/B/C subject test result that picks the next batch's variant on **reply rate**, not opens
- Landing-page mismatch diagnosis the moment CTR > 5% with conversion = 0
- A defensible ramp schedule from 20 emails/inbox/day to scaled batches

---

## 1. Folder structure

Every campaign starts as:

```
Active/<APP-NAME>-COLD-EMAIL/
├── README.md                    # Campaign goal + status + index of all files
├── email-v1.md                  # Current canonical email copy (subject + body)
├── email-v2.md                  # Iteration after first batch result
├── subject-line-test.md         # A/B/C variant table + test recipe
├── metrics-v1.md                # First-batch send (initial baseline)
├── metrics-2026-05-18.md        # Daily append-only — never overwrite
├── metrics-2026-05-19.md
├── metrics-2026-05-20.md
├── landing-diagnosis.md         # Filed when CTR is healthy but conversions are zero
├── DECISIONS.md                 # Rolling log: date · change · hypothesis · result
└── assets/                      # Screenshots from Instantly/Smartlead, landing-page captures
```

**The hard rule: metrics files are append-only.** Each day = a new file. Never edit yesterday's file. The timeline IS the data — overwriting destroys the empirical basis for every later decision.

Reference: `Active/KOMPOZY-COLD-EMAIL/README.md`.

---

## 2. README.md template

```markdown
# <App> Cold Email Campaign

One-line campaign description (single email? sequence? to what list?).

## Campaign goal

- **Audience:** ~<N>k <segment description> (cold / warm / hot).
- **Offer:** <tier name> — <price>/mo, <expiry condition>.
- **Format:** Single email / 3-step sequence / etc.
- **Landing destination:** <url> (dedicated /founders route? or homepage?).

## Files

- [email-v1.md](./email-v1.md) — Current email body.
- [subject-line-test.md](./subject-line-test.md) — A/B/C test plan.
- [metrics-v1.md](./metrics-v1.md) — First-batch baseline.
- [landing-diagnosis.md](./landing-diagnosis.md) — Filed if CTR healthy + conversions zero.
- [DECISIONS.md](./DECISIONS.md) — What changed when and why.

## Status

- **Sent so far:** <N> emails across <M> batches.
- **Avg open rate:** <%> (unreliable — see §5).
- **Avg reply rate:** <%> — primary metric.
- **Avg click rate:** <%>.
- **Conversions:** <N> trial / <N> paid.

## Next decisions

1. <Concrete next action, never vibes.>
2. <Gate before scaling beyond current batch size.>
```

---

## 3. email-v1.md template

```markdown
# Email v1 — <App> cold to <segment>

## Subject
<single subject line — A/B/C variants go in subject-line-test.md, not here>

## Preheader
<60-90 chars — first thing shown after subject in iOS Mail / Gmail preview>

## Body

Hey {{first_name}},

<2-3 sentence cold open. Reference the segment specifically — "real estate agents", "Shopify store owners", etc. Pattern-match the reader into the page.>

<Single offer paragraph. Price + expiry + 1 concrete benefit. No tier ladder unless the ladder exists in Stripe — see Pricing-promise hygiene §10.>

<Single CTA link → landing page with UTM:>
{{cta_url}}?utm_source=cold&utm_medium=email&utm_campaign=<app>-v1&utm_content=<variant>

— <your name>
Founder, <App>

## Sender
From: <Your Name> <you@<sending-domain>>
Reply-to: moe@<reply-domain>
```

Reference: `Active/KOMPOZY-COLD-EMAIL/email-v1.md`.

---

## 4. subject-line-test.md template

A/B/C protocol — 2,500 recipients per variant, decide by **reply rate**, not opens.

```markdown
# Subject Line Test — <App> v1

## Test slice
- Total recipients in test: 7,500 (2,500 per variant)
- Sending window: <date> across <N> inboxes
- All other variables held constant (same body, same CTA, same landing page)

## Variants

| Variant | Subject | Preheader | Hypothesis |
|--|--|--|--|
| A | "<curiosity line>" | "<preheader>" | Curiosity beats specificity for cold |
| B | "I built this for you (<segment>)" | "<preheader>" | Direct segment naming beats curiosity |
| C | "<benefit-led line>" | "<preheader>" | Benefit-first beats both |

## Decision criteria (in priority order)

1. **Reply rate** — primary. >2% on cold is the "scale this" bar.
2. **Conversion rate** — secondary. Replies are leading indicator; conversions confirm.
3. **Click rate** — tertiary. Useful only if landing page is trusted (see landing-diagnosis.md).
4. **Open rate** — IGNORE for ranking. Apple MPP inflates 20-40%; tracking pixels are blocked by Gmail privacy on a lot of segments. Open rate is noise.

## Result (filled in after test)

Winner: <variant>. Reply rate <%> vs <%> vs <%>. Logged in [DECISIONS.md](./DECISIONS.md).
```

---

## 5. metrics-YYYY-MM-DD.md template (append-only)

**One file per day. Never overwrite.** User pastes screenshot or numbers from Instantly/Smartlead → new dated file → update README status block → append to DECISIONS.md.

```markdown
# Metrics — <App> Cold — 2026-05-19

## Send window
- Batches sent today: <N>
- Recipients today: <N>
- Cumulative recipients: <N>

## Numbers (from <Instantly/Smartlead> dashboard)

| Metric | Today | Cumulative | Rate |
|--|--|--|--|
| Sent | | | — |
| Delivered | | | <%> |
| Bounces (hard) | | | <%> |
| Bounces (soft) | | | <%> |
| Opens | | | <%> (unreliable) |
| Clicks | | | <%> |
| Replies | | | <%> ← primary |
| Conversions (trial) | | | <%> |
| Conversions (paid) | | | <%> |
| Unsubscribes | | | <%> |
| Spam complaints | | | <%> ← if >0.1% STOP |

## Reply breakdown (qualitative — leading indicator)

- Positive ("interested", "tell me more"): <N>
- Neutral ("what's the price", "how does it work"): <N>
- Negative ("unsubscribe", "wrong person"): <N>
- Hostile ("spam", "report"): <N> ← if >0 in a batch, pause and review

## Screenshot
![dashboard](./assets/2026-05-19-dashboard.png)

## Notes
<Anything weird: deliverability dip on one inbox, a specific subject variant lagging, etc.>
```

Reference: `Active/KOMPOZY-COLD-EMAIL/metrics-v1.md`.

---

## 6. DECISIONS.md template (rolling log)

The point of this file: correlate "I changed X on date Y → metrics moved Z" so optimization becomes empirical, not vibes-based. Every change goes in. Every change.

```markdown
# Decisions Log — <App> Cold Email

| Date | Change | Hypothesis | Result (filled in after 3 days of metrics) |
|--|--|--|--|
| 2026-05-15 | Initial send, email-v1, subject A, to 538 recipients | Baseline | 1.42% click, 0 conversions — see metrics-v1.md |
| 2026-05-18 | Filed landing-diagnosis.md — CTR healthy, conversion zero | Promise mismatch on landing | Pending /founders build |
| 2026-05-19 | Launched A/B/C subject test on 7,500 slice | Direct segment naming beats curiosity | Pending — read 2026-05-22 |
| 2026-05-22 | Variant B won (reply rate 2.3% vs 1.1% vs 0.8%) | — | Rolling B to remainder of list |
| 2026-05-23 | Re-pointed CTA from `/` to `/founders` | Promise match → conversions | Pending |
```

**Rule: never change two variables in the same row.** If you swap subject AND landing page on the same day, you can't attribute the lift. Stagger by at least 48h or run them as separate A/B slices.

---

## 7. List segmentation

Three temperatures. Pick one per campaign. Don't mix in the same send.

- **Cold** — no prior contact with anyone in the portfolio. Lowest reply rate (~0.5-1.5% on a good day), highest spam risk, requires the most warmup discipline. Used when launching to scraped/purchased lists.
- **Warm** — engaged with prior campaigns from a sibling app (BILT CRM's ~100k RE list is the canonical warm pool). Reply rates 2-4x cold; deliverability much friendlier because most addresses are still active. **Most marketing apps in the portfolio launch warm**, including Kompozy → BILT-RE.
- **Hot** — paying customers of a sibling app. Treat as transactional, not cold. Different inbox, different sender (founder-personal vs. brand), reply rates 8-15%.

For warm sends: still ramp. The list has been emailed before but not from your new sending domain — recipients haven't seen this `From:` address before, ESPs haven't built a reputation on it.

---

## 8. Ramp schedule

New sending stack ramps over **6+ weeks** before hitting full volume. Skipping this kills the domain — and there's no recovery, only buying a new one.

```
Week 0     : Buy domain. Set up SPF/DKIM/DMARC (see EMAIL-INFRASTRUCTURE-PLAYBOOK.md).
Week 1-2   : Instantly/Smartlead automated warmup. 14 days minimum, ZERO real sends.
Week 3 d1  : 20 emails/inbox/day to warm list only. Reply-rate check on day 3.
Week 3 d4+ : If reply rate >1% and spam <0.05%, hold 20/day for 7 days.
Week 4     : Ramp 10% week-over-week → ~22/inbox/day. Daily metrics check.
Week 5     : ~24/inbox/day. Watch bounce rate — hard bounces >2% means list quality issue, NOT volume issue.
Week 6+    : Continue 10%/week ramp until target throughput. Stop ramping if any week shows reply-rate decline or spam-complaint uptick.
```

**Inbox math:** 20/inbox/day × 10 inboxes = 200/day = 6,000/month. To send 100k in 30 days you need ~17 fully-warmed inboxes pulling 200/day each — plan inbox provisioning 6 weeks ahead of the campaign launch date.

---

## 9. Landing-page-mismatch diagnosis

**Trigger:** CTR is healthy (>5% among openers, or >1% of delivered) and conversion is zero or near-zero. The email is doing its job; the landing page isn't.

File `landing-diagnosis.md` with:

1. **Ground-truth mobile capture.** Open the destination URL on a fresh mobile profile (DevTools iPhone emulation works). Screenshot above-the-fold. Most cold-email clicks are mobile; the desktop view is irrelevant.
2. **List what the email promised.** Subject + opening line + the offer terms. Verbatim.
3. **List what the landing page shows.** Hero, sub, primary CTA, secondary CTA, what's NOT visible without scrolling.
4. **Map the mismatch.** Where does the page fail to deliver the email's promise? Common failures: generic SaaS pitch when email named a segment, free-trial CTA when email built urgency around a paid offer, $X price hidden 3 scrolls down behind "See Pricing".
5. **Prescribe the high-leverage fix.** Usually a dedicated route (`/founders`, `/<segment>`, `/<campaign-slug>`) with hero that mirrors the email subject + single CTA + the offer's price/expiry visible without scrolling.
6. **Re-point the email link, then resume sending.** Don't blast the rest of the list against a known-broken funnel.

The Kompozy v1 case: 6,912 sent → 98 clicks → 0 conversions. Diagnosis identified the homepage as the bottleneck, not the email. Stopped the blast at 7k instead of burning the remaining 93k against a broken funnel.

---

## 10. Pricing-promise hygiene

**Never promise pricing/features in cold email that don't exist in Stripe today.** The Kompozy v1 email mentioned a tiered ladder ($39 → $59 → $79 every 100 signups) that wasn't wired into Stripe products — replies asking "how do I lock in $39?" couldn't be answered with a working checkout link. Cost: ~3 days of campaign momentum while the Stripe products were built.

Before sending v1:

- Open Stripe dashboard. Confirm every tier/price/feature mentioned in the email has a live product.
- Confirm the signup link routes to a checkout that actually charges the promised price.
- Confirm any "founding member" / "early bird" / "locked for life" flag has a DB column or Stripe metadata key that survives renewal.

This is the single most expensive mistake to make in a cold campaign — every recipient who replied "interested" while the checkout was broken is a soft-burn on the list.

---

## 11. Reply handling

- **Per-app inbox.** Don't route replies to a personal address — they get lost in the noise and conversion attribution dies. Use `moe@<app-domain>` or `founders@<app-domain>` with a shared inbox tool.
- **Reply rate is the primary metric.** Track it daily in metrics files. Cold-RE benchmark: >2% is "scale this", 0.5-1.5% is "iterate copy", <0.5% is "rethink offer or audience."
- **Qualitative replies lead conversions by 2-3 weeks.** If reply tone shifts negative (more "unsubscribe" / "wrong person" / "this is spam"), expect conversion rates to drop in 2-3 weeks — pause and iterate now, don't wait for the numbers to confirm.
- **Conversion attribution.** UTM params on every CTA link (`utm_source=cold&utm_medium=email&utm_campaign=<app>-v<N>&utm_content=<variant>`). Signup form persists UTMs into `profiles.signup_source` so attribution survives even when the user comes back days later via direct URL.

---

## 12. Common pitfalls

- ❌ **Don't blast cold without warmup.** Fresh IP + fresh domain + cold list = inbox provider auto-spam in under 100 sends. 14-day warmup is non-negotiable.
- ❌ **Don't optimize on opens.** Apple MPP inflates open rates 20-40%; Gmail privacy strips the tracking pixel on a meaningful share of recipients. Opens are noise in 2026. Rank by reply rate.
- ❌ **Don't change multiple variables at once.** A/B/C tests must isolate ONE variable per round (subject only, OR body only, OR CTA only, OR landing only). Otherwise the result is unattributable.
- ❌ **Don't overwrite metrics files.** Append-only. Each day = new file. The timeline is the data.
- ❌ **Don't promise pricing/features that don't exist in Stripe.** See §10. Kompozy v1 paid for this mistake.
- ❌ **Don't ignore qualitative reply patterns.** They're a 2-3 week leading indicator before quant data confirms.
- ❌ **Don't scale through a broken funnel.** If conversion is zero after 5k+ sends with healthy CTR, file landing-diagnosis.md and STOP sending until the funnel is fixed.
- ❌ **Don't share an inbox between cold campaigns and personal mail.** Reply attribution breaks; deliverability reputation cross-contaminates.

---

## 13. Kickoff prompt

> Execute the Cold Email Campaign Playbook at `COLD-EMAIL-CAMPAIGN-PLAYBOOK.md` for `<APP-NAME>`. Mirror the Kompozy reference at `Active/KOMPOZY-COLD-EMAIL/`.
>
> Inputs:
> - App name + sending domain: [provided]
> - Audience: cold / warm / hot — [pick]
> - List source + estimated size: [provided]
> - Offer (tier name, price, expiry): [provided]
> - Stripe products live for this offer: [yes / building]
> - Sending stack (Instantly / Smartlead) + warmed inbox count: [provided]
>
> Step 1: Create `Active/<APP-NAME>-COLD-EMAIL/` with README.md, email-v1.md, subject-line-test.md, DECISIONS.md from the templates above.
> Step 2: Audit the offer against Stripe before drafting (§10). Halt if any promise lacks a live product.
> Step 3: Draft email-v1 + 3 subject variants. Pause for review before sending.

---

**Reference files in the Kompozy campaign:**
- `Active/KOMPOZY-COLD-EMAIL/README.md` — campaign goal + status
- `Active/KOMPOZY-COLD-EMAIL/email-v1.md` — current canonical copy
- `Active/KOMPOZY-COLD-EMAIL/subject-line-test.md` — A/B/C variant table
- `Active/KOMPOZY-COLD-EMAIL/metrics-v1.md` — first-batch baseline
- `Active/KOMPOZY-COLD-EMAIL/landing-diagnosis.md` — 98 clicks → 0 conversions postmortem
