TrueAdvertize
All skills
07Copywriting

clay-alt-generate-copy

Personalized cold-email subject + body for one enriched lead.

Takes one enriched lead and a campaign copy template, returns personalized subject + body as strict JSON. Only fires after email verification, so no tokens burn on dead recipients. The Clay Alt Platform's per-lead copy step.

  • Personalized subject plus body per lead
  • Only fires after email verification
  • Drop-in for the Clay Alt Platform
Install
$ mkdir -p ~/.claude/skills/clay-alt-generate-copy && curl -sSL https://trueadvertize.com/skill-files/clay-alt-generate-copy/SKILL.md -o ~/.claude/skills/clay-alt-generate-copy/SKILL.md

Drops SKILL.md into ~/.claude/skills/clay-alt-generate-copy/. Reload Claude Code and the skill auto-activates on its triggers.

Source
SKILL.md
---
name: clay-alt-generate-copy
description: Generate personalized cold-email copy (subject + body) for one enriched lead, applying the 4-step outbound framework (personalization, who-am-I, offer, CTA). Input is a JSON lead record + a campaign config; output is strict JSON with `copy_subject` and `copy_body`. Use in the Clay Alt Platform after a lead reaches status='enriched' and has a valid email. Skips leads without a verified-email-or-strong-guess to save tokens.
allowed-tools: Bash
---

# clay-alt-generate-copy

Takes one enriched lead and a campaign config, returns personalized cold-email copy as strict JSON. Every email follows the same 4-step structure used in the sibling `email-copywriter` skill. Only called after the verify step — no token spend on leads that'll never send.

---

## When to use

- Lead status = `'enriched'` AND (`email_verified = true` OR campaign config allows unverified-email copy)
- Campaign config has at least the required framework fields (see below)

## When NOT to use

- Lead has no `email` or `email_guess` (no point personalizing for a null recipient)
- `copy_body` already populated (idempotent — skip unless explicitly regenerating)
- You're doing bulk A/B test variants — use a separate variant-generation skill
- Campaign config has no offer fields and no fallback template — emit `null` and skip

---

## Input contract

Caller passes a JSON object on stdin:

```json
{
  "lead": {
    "first_name": "Patrick",
    "company": "Stripe",
    "company_casual": "Stripe",
    "domain": "stripe.com",
    "title": "CEO",
    "location": "San Francisco",
    "location_casual": "SF",
    "enrichment": {
      "recent_news": "Stripe announced expansion into India payments",
      "linkedin_post_topic": "hiring an SDR team",
      "company_description": "...",
      "industry": "fintech",
      "headcount": 8500
    }
  },
  "template": {
    "subject": "{{ subject_line }}",
    "body": "Hey {{ first_name }},\n\n{{ personalization }}\n\n{{ who_am_i }}\n\n{{ offer }}\n\n{{ cta }}\n\nThanks,\n{{ sender_first_name }}"
  },
  "campaign": {
    "name": "ta-b2b-saas-founders",
    "audience": "B2B SaaS founders, 50 to 300 customers",
    "cold_read_angle": "scaling outbound past founder-led sales",
    "offer_outcome": "20 booked meetings",
    "offer_timeframe": "60 days",
    "offer_risk_reversal": "or you don't pay a cent",
    "proof_point": "11.2% reply rate and 18 meetings in the first 60 days for a B2B SaaS founder, around $412k in pipeline",
    "sender_first_name": "Sam",
    "goal": "booked_call"
  }
}
```

**Required campaign fields:**

- `audience` — the ICP, used to ground the cold read.
- `cold_read_angle` — the general pain or topic the opener will reference (must apply to 70%+ of the audience).
- `offer_outcome` — single quantified outcome (NOT a range). "$10–20k" is dead, pick one number.
- `offer_timeframe` — specific time window.
- `offer_risk_reversal` — what protects the prospect.
- `proof_point` — one specific named result with a specific number.
- `sender_first_name` — first name only, casual.
- `goal` — one of `reply`, `link_click`, `booked_call`. Drives CTA style.

**Optional campaign fields:**

- `cta_slots` — array of 2 specific time slots when `goal = booked_call` (e.g. `["3:30 p.m. today", "11 a.m. tomorrow"]`).
- `cta_phone` — phone number for the "I can give you a ring" CTA variant.
- `meet_link_alternative` — set true to offer "one-click Google Meet" as the friction-minimizer.
- `allow_emojis` — defaults to false. The framework does not use emojis.
- `lowercase_subject` — defaults to true (signals real person).

---

## Output contract

**Print exactly one JSON object to stdout.** Schema:

```json
{
  "copy_subject": "patrick, the SDR thing you posted about",
  "copy_body": "Hey Patrick,\n\nSaw your post about hiring an SDR team..."
}
```

No markdown fences, no preamble, no explanation. If you cannot personalize cleanly (e.g. no usable enrichment field for cold-read), emit:

```json
{"copy_subject": null, "copy_body": null, "notes": "no usable cold-read field on lead"}
```

…and exit 0 — the caller leaves the lead at `status='enriched'` and skips send.

---

## How — the 4-step framework, applied per lead

The output email MUST follow this exact structure. Each step answers a specific question the reader is asking sequentially:

| # | Step | Question it answers | Length |
|---|------|---------------------|--------|
| 1 | Personalization | "Is this a scammer?" | 1 sentence (2 max) |
| 2 | Who am I (+ proof) | "Who is this and why care?" | 1–2 sentences |
| 3 | Offer (outcome + time + risk reversal) | "What can you do for me?" | 2–3 sentences |
| 4 | CTA | "What's the next step?" | 1 line |

**Total target: 80–150 words.** Shorter wins.

### Step 1 — Subject line

Pick the strongest pattern given the lead data:

1. **Name + concrete hook** — `patrick, the SDR thing you posted about`
2. **Name + observation** — `patrick, your latest stripe expansion`
3. **Loss framing** — `patrick, you're wasting 2,300 per month` (only if you have a number to back it up)
4. **Tangential insider mention** — `your last linkedin post and a thought`

Subject rules:

- **Plausible deniability is everything.** The reader must NOT be able to tell this is a pitch from the subject alone.
- **30–50 characters.** Shorter > longer.
- **Lowercase** unless `lowercase_subject = false`.
- **No "quick"** as the hook word. Pattern-matches as low-effort template.
- **No selling.** No "Interested in...", no "Following up", no "Following up on...".
- **No empty subject.** Correlates with spam.
- **No acronym typos.** Writing "AI" when the industry says "A-I" signals outsider.

### Step 2 — Personalization (cold read)

Generate 1 sentence (2 max) that references `campaign.cold_read_angle` filtered through `lead.enrichment`.

Pattern:

> Hey {first_name}, {observation_about_cold_read_angle}. {soft_segue_to_pitch}.

Example with `cold_read_angle = "scaling outbound past founder-led sales"`:

> Hey Patrick, saw your post about hiring an SDR team. I've been in the same loop running outbound for B2B SaaS founders, and you're not wrong about agencies.

Rules:

- **1 sentence ideal, 2 max.** Long openers break the "real person" illusion.
- **Cold-read, don't AI-personalize.** BAD: "love how passionate you are about process optimization and aligning corporations with diversity outcomes at Beaver Corp." Nobody writes that.
- **Casualize scraped variables.** Use `lead.company_casual` if available (e.g. "Stripe", not "Stripe Inc."), `lead.location_casual` (e.g. "SF" not "San Francisco, CA").
- **Voluntary disclosure builds rapport.** Reveal something (real or plausible) about yourself when natural.
- **No greeting before "Hey"** — "Hey {first_name}" is the start. Never "Dear", never "Hello there".

### Step 3 — Who am I (1–2 sentences)

Use `campaign.proof_point` verbatim or near-verbatim. Match the reference group (if pitching SaaS founders, name a SaaS result; if pitching dentists, name a dental result).

Pattern:

> I currently work with {reference_group} to {do_thing}. {proof_point with specific numbers}.

Example with `proof_point = "11.2% reply rate and 18 meetings in the first 60 days for a B2B SaaS founder, around $412k in pipeline"`:

> I currently work with B2B SaaS founders 50 to 300 customers, building allbound GTM systems. Last build hit 11.2% reply rate and 18 meetings in the first 60 days, around $412k in pipeline.

Rules:

- **Specific numbers, not vague claims.** Use the exact numbers from `proof_point` — never round, never paraphrase upward.
- **Match reference group.** Don't paste SaaS proof into a dental pitch.
- **Use "I" not "we"** by default. Use "we" only when it puts you in the prospect's in-group.
- **Don't downplay.** Never write "small team" or "modest results" — undermines the proof.

### Step 4 — Offer (2–3 sentences)

Build from `campaign.offer_outcome` + `offer_timeframe` + `offer_risk_reversal`.

Pattern:

> {observation about prospect's situation, drawn from cold_read_angle}. I'll {offer_outcome} in {offer_timeframe} {offer_risk_reversal}. [Optional: friction-minimizer like "takes 15 minutes of your time, once."]

Example:

> There's a 4-week build between you and a pipeline that runs without you in every email. I'll book 20 meetings in 60 days or you don't pay a cent. It'll take just 15 minutes of your time over a brief call once at the beginning.

Rules:

- **One number, never a range.** "$20k", never "$10–20k".
- **Time-bound.** Without a deadline, the guarantee is meaningless.
- **Risk-reverse onto self.** The prospect risks nothing.
- **Scale to prospect tier.** If `lead.enrichment.headcount` or revenue tier suggests the prospect is small, scale the outcome down so it's mathematically achievable.
- **Casual language.** "I think you're leaking money" beats "Our proprietary methodology has identified inefficiencies."
- **No corporate signals.** Kill "leveraging synergies", "hope this finds you well", signature blocks with credentials.

### Step 5 — CTA

Pick the variant matching `campaign.goal`:

**Goal = `booked_call`:**

> Would you be open to a 15-minute chat? If so, I can give you a ring at {cta_slots[0]} or {cta_slots[1]}. [If meet_link_alternative: Or send you a one-click Google Meet invite. Just let me know.]

**Goal = `reply`:**

> [Ask one specific question that requires a yes/no or short answer. No calendar link.] Example: "Worth a 10-minute chat? If yes I'll send you a one-click invite."

**Goal = `link_click`:**

> [One specific link with a clear benefit framing.] Example: "Want me to send over a 2-min Loom showing exactly how it'd look for {company_casual}?"

Rules:

- **Soft proposal, hard specifics.** "Would you be open to" + concrete time/mechanism.
- **Never end with "let me know your thoughts."** Trash.
- **Never use multi-step booking dances.** Every back-and-forth = ~5% lost. Collapse to 2 steps.
- **Sign off casually.** "Thanks, {sender_first_name}." Never "Best regards" + credentials block.

---

## Step 6 — Validate before emit

Before printing JSON, confirm:

- Every `{{ var }}` in the template has been replaced (no literal `{{` in output).
- Total body length is 80–150 words.
- Subject is 30–50 chars, lowercase if `lowercase_subject` true, no "quick", no "AI" typo.
- Body has all 4 framework steps in order.
- Outcome is ONE number, time-bound, with risk reversal language.
- No "we" where "I" would work better.
- No corporate filler ("leveraging", "synergies", "circle back", "touch base").
- No emojis unless `allow_emojis = true`.
- No clickable links in the body (deliverability — except as explicit `link_click` goal).
- Body does NOT contain the lead's email, the campaign name, or any internal identifiers.

If any check fails, regenerate before emitting.

---

## Step 7 — Emit

One JSON object, stdout, done. No extra text.

---

## Token cost

~600 input + ~250 output tokens per lead on Sonnet ≈ $0.005 per lead. 100 leads/day ≈ $15/month.

---

## Recommended campaign template

```json
{
  "template": {
    "subject": "{{ subject_line }}",
    "body": "Hey {{ first_name }},\n\n{{ personalization }}\n\n{{ who_am_i }}\n\n{{ offer }}\n\n{{ cta }}\n\nThanks,\n{{ sender_first_name }}"
  }
}
```

The skill fills `subject_line`, `personalization`, `who_am_i`, `offer`, `cta` per the framework above. Backward compatibility: legacy slot names (`hook_line`, `value_prop`) still work but map to `personalization` + `offer` respectively. New campaigns should use the framework-shaped slot names.

---

## Anti-patterns

- **Letting AI hyper-personalize with granular specifics no human would notice.** "Hey Stacy, love how passionate you are about process optimization and aligning corporations with diversity outcomes at Beaver Corp" — dead.
- **Range outcomes.** "$10–20k" hedges. Pick one number.
- **Time-unbound outcomes.** "I'll grow your revenue" without a deadline = meaningless.
- **Multi-step booking dances.** Every back-and-forth leaks ~5% conversion.
- **Newsletter-style follow-ups.** "While analyzing your funnel I noticed..." reads like marketing. Follow-ups are short human pings.
- **Empty subject lines.** Spam pattern.
- **Putting typos at the start.** Reader will judge and bounce. Imperfections go near the end after trust is built.
- **Inventing facts.** If `lead.enrichment.recent_news` is null, fall back to `cold_read_angle` — never fabricate news, numbers, or quotes.
- **Calendar links in `goal=reply` campaigns.** The goal is a reply, not a click. Mixing modes tanks both.
- **Stats not in enrichment.** Never write "30% increase" or "top 1%" unless it appears verbatim in the lead or campaign data.

---

## Scope

Per-lead automation of the cold-outbound framework inside the Clay Alt Platform's outbound pipeline. The sibling skill `email-copywriter` documents the framework in detail for interactive use; this skill automates it across a campaign list. For warmed-list / newsletter / value-email work, use a different skill — the rules differ.
Trigger phrases

Claude Code auto-activates this skill when prompts contain phrases like:

generate copy for these leadspersonalized cold email per leadsubject and body for the enriched listper-lead outbound copy