Systems Lab

Agent skill

instantly-copywriter

Create Instantly email campaigns with proper naming, 2x2 thread structure, Instantly-native spintax, humanized copy, variable mapping, and API push.

activeNeeds a keyActs undeclared2,896 words

Filed under Outbound email and Content and SEO.

From OneGTM/gtm-skills · 9 skill entries · 0 · pushed 2026-09-30

What it does when it runs

Create Instantly email campaigns with proper naming, 2x2 thread structure, Instantly-native spintax, humanized copy, variable mapping, and API push. Shared across all client engagements.

Automated analysis of the skill and the 0 files bundled beside it. A skill’s own description is written to be selected by an agent, so it describes the job and not the dependencies.

Keys and connectors you must supply
  • INSTANTLY_API_KEY
Hosts it reaches
  • api.instantly.ai
Tool permissions it declares
No allowed-tools in the frontmatter. It does act, so it runs under whatever permissions your session already grants.
Actions present in the files
shell

Ask about instantly-copywriter

Opens your assistant with this page's verified links already in the prompt.

Is this safe to install?ClaudeChatGPT
Adapt it to my stackClaudeChatGPT
What else do I need for it to workClaudeChatGPT
Rather ask a human? Talk to Cheetah
git clone --depth 1 --filter=blob:none --sparse https://github.com/OneGTM/gtm-skills.git /tmp/gtm-skills
git -C /tmp/gtm-skills sparse-checkout set "skills/instantly-copywriter"
mkdir -p ~/.claude/skills/instantly-copywriter
cp -R "/tmp/gtm-skills/skills/instantly-copywriter/." ~/.claude/skills/instantly-copywriter/

Picked up without a restart. A project skill of the same name is shadowed by your personal one. For one repository only, swap ~/.claude/skills for .claude/skills. Claude Code docs ↗

Or take the whole library

This repo ships a .claude-plugin manifest, so Claude Code can install all 9 skills at once. Plugin skills are invoked as /<plugin>:<skill>, so they never collide with your own.

/plugin marketplace add OneGTM/gtm-skills
/plugin

The folder is the same in every client that implements the format — 46 of them — so if yours is not above, only the destination changes.

Before you install: this skill will not complete its job on a bare agent. It needs INSTANTLY_API_KEY, which you have to obtain separately.

Reproduced in full from OneGTM/gtm-skills/blob/36da828338f8062c90f6a4a8ea8b918e4160f1e5/skills/instantly-copywriter/SKILL.md, which is licensed MIT (repository). 2,896 words, 32 headings.

Instantly Copywriter (Shared)

Create and push cold email campaigns to Instantly. Handles campaign naming, 2x2 email structure, spintax, humanization, variable mapping, and API enrollment.

This is the general version. Workspace-specific skills can extend it with custom copy rules, sender accounts, and name-drops. If a workspace has their own skills/instantly-copywriter/, follow that -- it inherits from this. A client copy playbook may deliberately override a rule here (comp numbers, emoji); where it says so explicitly, it wins for that client.

API mechanics (payload shapes, gotchas, the push client) live in the instantly-api skill in this repo. This skill is about the copy and the campaign shape.

Depends on: humanizer (for copy cleanup before push)

1. Campaign naming

Format: [TYPE] Client - Role/Persona

TYPE codes:

  • ENG -- engineering roles
  • GTM -- go-to-market roles (AE, SDR, marketing)
  • ANNIV -- anniversary/timing-based outreach
  • RE-ENG -- re-engagement of prior candidates
  • SIG -- signal-based (job change, funding, hiring)

Examples: [ENG] Acme - Staff Eng, [GTM] Globex - Senior AE

2. Sequence structure: 2 threads x 2 emails

Every campaign uses 4 emails across 2 threads. The 2nd email in each thread has a blank subject line so it appears as a reply in the recipient's inbox.

EmailThreadSubjectDelayPurposeWords
Email 1AHas subject (2 A/B variants)+2 daysCold open. {{firstName}}, spintax, company-specific hook.60-80
Email 2ABlank (same thread)+2 daysFollow-up in same thread. New detail, brief.30-50
Email 3BHas subject (2 A/B variants)+3 daysNew thread. Different angle, different hook.60-80
Email 4BBlank (same thread)+2 daysSoft close. Low pressure, door open.25-40

Timing: +2, +2, +3, +2 = 9 days total. Step 1 MUST have a 2-day delay, never 0: same-day sends on enrollment look automated and hurt reply rates (observed across client workspaces, 2026).

A/B variants (subject AND body)

Emails 1 and 3 MUST have 2+ variants. Each variant has its own subject AND body -- not just different subjects with the same body. Instantly auto-optimizes toward reply rate.

Variant strategies should test genuinely different approaches:

What to varyVariant AVariant B
Hook/angleLead with investors/fundingLead with product/problem
CTA"Would you be up for a quick call?""Want me to send over more details?"
ToneDirect and numbers-heavyConversational and story-driven
Social proofName-drop VCs and investorsName-drop enterprise clients
Opening"Hey {{firstName}}," + company pitch"Hey {{firstName}}," + observation about their background
LengthShort (40-50 words)Standard (60-80 words)

Each variant should ALSO have spintax within it for additional variation. Two levels of variation:

  1. Variant level: genuinely different strategy/angle/CTA (Instantly picks the winner)
  2. Spintax level: minor phrasing variation within each variant (deliverability)

Per-email spintax minimums (enforced by lint_sequence in generate_sequence.py):

  • Email 1 / Email 3 (cold opens): ≥3 {{RANDOM}} blocks in body (greeting + mid-body verb + CTA)
  • Email 2 / Email 4 (follow-ups): ≥2 blocks in body (opener + closer)
  • Every non-blank subject: ≥1 block

The spintax injector tops up bodies that fall short, applying opener-position injection first then closer-position. Subject injectors target the first verb or fall back to a {{RANDOM||Quick: |}} prepend.

Example (Email 1):

"variants": [
  {"subject": "Conductor, founding eng role",
   "body": "<div>{{RANDOM |Hey|Hi}} {{firstName}}, I'm working with the founders of Conductor. They built the command center for AI coding agents, backed by Spark Capital, Matrix and YC.<br><br>{{RANDOM |Would you be up for a quick call?|Worth a conversation?}}</div><br><div>{{accountSignature}}</div>"},
  {"subject": "Quick one, {{firstName}}",
   "body": "<div>{{RANDOM |Hey|Hi}} {{firstName}}, ever tried managing a team of AI agents writing code in parallel? That's what Conductor does. Leaders at Vercel and Linear already use it.<br><br>{{RANDOM |Want me to send more details?|Interested in hearing more?}}</div><br><div>{{accountSignature}}</div>"}
]

Variant A leads with investors, B with product and customers. Different CTAs, hooks and lengths; both have spintax.

Blank subject emails

Emails 2 and 4 have "subject": "" -- Instantly treats these as replies in the same thread. These can also have 2 body variants.

3. Spintax (Instantly format)

Instantly uses {{RANDOM |option1|option2|option3}} -- NOT {option1|option2}.

{{RANDOM |Hey|Hi}} {{firstName}},

I'm working with {{RANDOM |the founders of|the team at}} {{companyName}}.
They {{RANDOM |built|created}} a platform that {{RANDOM |does X|handles Y}}.

{{RANDOM |Would you be up for a quick call?|Worth a conversation?}}

{{RANDOM |Best,|Cheers,}}
[sender name]

Where to use spintax

  • Greeting: {{RANDOM |Hey|Hi}}
  • Verb phrases: {{RANDOM |built|created}}, {{RANDOM |runs|handles}}
  • Company mentions: {{RANDOM |Microsoft, Google|companies like Microsoft and Google}}
  • CTAs: {{RANDOM |Worth a quick call?|Want to hear more?|Open to chatting?}}
  • Sign-offs: {{RANDOM |Best,|Cheers,}}

Where NOT to use spintax

  • Names, numbers, specific facts
  • Inside other variables (no nesting)
  • Completely different value props (use separate A/B variants instead)

4. Variables

Required in every email body

  • {{firstName}} -- in emails 1 and 3 at minimum
  • {{companyName}} -- when referencing the prospect's current company

Signature

ALWAYS end every email with {{accountSignature}}. NEVER hardcode a sender name. Campaigns run across multiple mailboxes under one sender tag, and a hardcoded "Jenny" sent from Mark's mailbox looks broken. Instantly auto-substitutes the per-sender signature configured on each mailbox.

<br><br>{{accountSignature}}

This is hard-blocked at push time, push_campaign.py aborts if any variant body is missing the token. The generator's humanizer pass also adds it as a deterministic backstop if the LLM forgets.

Variable fallbacks

Before pushing leads, ALWAYS verify:

  1. Every lead has first_name populated (no blanks)
  2. Every lead has company_name populated (no blanks)
  3. Run validate_before_push.py which catches missing required fields

If a lead is missing firstName or companyName, do NOT push it.

Variable mapping: Supabase -> Instantly API

All custom variables go in the custom_variables object in the API payload. They appear in Instantly's lead view for filtering/tracking.

CategorySupabase fieldInstantly variableExample
Contactfirst_namefirstName (+ API first_name)"Rafi"
last_name(API last_name)"Ayub"
linkedin_urllinkedin_url"linkedin.com/in/..."
Rolecurrent_companycompanyName (+ API company_name)"Anthropic"
current_titletitle"Member of Technical Staff"
current_company_domaincompanyDomain"anthropic.com"
Backgroundall_schoolsschools"Stanford; UT Dallas"
biobio"AI/ML engineer"
honorshonors"NSF Fellow"
locationlocation"San Francisco, CA"
total_tenure_yearstenureYears"7.0"
founding_rolefoundingRole"True"
personal_websitepersonalWebsite"rafiayub.com"
(from job_descriptions)pastEmployers"Meta; Microsoft"
Scoringquality_scorequality_score"68"
quality_tierquality_tier"Tier I"
tiertier"1"
(computed)fit_score"15"
(computed)combined_score"83"
(computed)moneyball_score"50"
Classification(computed)segment"hot"
(computed)pool"both"
(computed)fit_reasons"talent_competitor(anthropic)"
(computed)why_qualified"Competes for same talent | ..."

Always map ALL available fields -- they enable filtering, sorting, and tracking in Instantly.

Only email, first_name, last_name, company_name (and the per-sender {{accountSignature}}) are read from top-level lead keys. Everything else must go in custom_variables (flat, scalar values), or it is silently ignored and the token renders blank. One workspace shipped ~15% of an hour's bodies opening with a dangling dash this way. Pass companyName / firstName in both places.

Never reference an optional variable (e.g. {{personalized_opener}}) unless it has been generated for every lead in the campaign, or give it a | fallback.

Name normalization (before upload)

  1. First names: capitalize ("austin" -> "Austin", "JANE" -> "Jane").
  2. Company names: strip suffixes (LLC, Inc., Corp., Ltd., PLC, Holdings, GmbH, with or without a comma) and trailing punctuation; title-case only if all-caps or all-lower; preserve acronyms (AI, IBM, AWS, NYC, SF, xAI); keep "of/the/and" lowercase when not first; strip Apollo artifacts ("Related to search terms in your query").
  3. Use normalize_names.js or validate_before_push.py.

5. Copy rules

The concrete-fact requirement (added 2026-05-17)

Every Email 1 and Email 3 (cold opens, A/B variants) must contain at least one proper noun drawn from research:

  • A founder name + their prior company ("ex-Boston Dynamics controls lead")
  • A backer ("Sequoia led the Series A")
  • A named customer or pilot
  • A specific technical bet ("Rust on the edge", "sub-10ms motion planning")
  • A specific number ("~30 robots in production", "$50M Series B")

If a variant could be sent to any company in the sector without changing a word, it's broken. Rewrite it.

The angle palette

Email 1 and Email 3 must each LEAD with one of these, and must use DIFFERENT angles from each other:

  1. Founder pedigree, "The team came out of [prior co]"
  2. Backer signal, "[VC] is their lead investor"
  3. The bet, "Most teams ship [old]. [Co] bet [new]."
  4. Named customer / traction, "[Customer] just signed"
  5. The hard problem, one specific technical or business challenge
  6. Employee number + equity, "Employee ~#[N], pre-Series-B equity"
  7. Vision, one-sentence future state if they win
  8. Shipped-at-prior-company, "[Founder] shipped [Product] at [Prior Co]"

Voice: insider, not observer

We write ON BEHALF OF the client, as their partner, not as someone who just read about them.

Bad (observer voice)Good (insider voice)
"I saw Avoca just raised $125M""Avoca's Series B closed last month and they're scaling the eng team"
"I noticed Monaco closed a Series A""We're partnering with Monaco on their post-Series-A hires"
"I read that you're hiring...""The team asked me to find..."

Discovery framing ("I saw / I noticed / I came across / Just heard / I read / It looks like") is banned when applied to the client. About the prospect's own background it is fine ("I saw your time at Stripe"), because that is a real observation. Funding facts are still useful, stated flat as givens. Test: read the opener aloud. If it sounds like an SDR who Googled the company five minutes ago, rewrite it.

Competitors are for targeting, NOT for copy (hard rule)

Competitor lists (competitors, talent_competitors, legacy_competitors in research output) decide who to source and how to score them. They never appear in email or LinkedIn copy: naming a rival is presumptuous, often wrong (models guess at competitive sets), and reads as unprofessional.

Banned in body and subject: positioning the client against a named company ("competing with X", "taking on giants like X", "rivals X", "gaining ground against X"), and naming competitor employers as targets in generic drips ("I recruit for companies like Palantir and Scale AI"; say "well-funded AI startups").

BadGood
"competing with GitHub Copilot and DeepCode""focused on full end-to-end automation, not just code suggestions"
"taking on giants like Oracle and IBM""building for a market underserved by legacy software"

Still allowed: the client's own name, its investors, its customer logos, and candidate/team pedigree ("ex-Palantir engineers"). Test: if the sentence sets the client against a named company, cut the name.

The punch test

Each email must contain ONE memorable line a candidate might forward to a friend. Not a tagline, a specific, true sentence with a name or number. If no line passes the test, the email is bland. Rewrite.

PassesFails
"Sequoia's only humanoid bet.""Fast-growing team with impactful work."
"Sub-10ms motion planning, ~30 robots in production.""World-class engineering culture."
"Founders shipped the Spot controls stack at Boston Dynamics.""Cutting-edge robotics in a dynamic environment."

Do

  • Start emails 1 and 3 with {{RANDOM |Hey|Hi}} {{firstName}},
  • Name-drop specific details: VCs, founders + prior companies, client logos, team size, funding, growth metrics
  • Reference DIFFERENT details (different angles AND different proper nouns) in each email
  • Keep CTAs single and specific: "Would you be up for a quick call?" not "Learn more"
  • Write like a real person, not a recruiter template
  • Run ALL copy through the humanizer skill before finalizing

Never

  • Use em dashes (use commas, periods, parentheses instead). Also banned: en dash (, ) and -- as a dash substitute. Em dashes are the #1 LLM tell. Make the push script hard-block , , , , -- in any body or subject.
  • Name a competitor or position the client against one (see above)
  • Use exclamation marks in body copy
  • Use buzzwords: cutting-edge, world-class, innovative, groundbreaking, game-changing, state-of-the-art, dynamic, fast-growing, impactful, exciting, mission-critical, transformative, next-gen, disruptive, best-in-class
  • Use observer voice toward the client ("I saw…", "I noticed…", "I came across…", "Just heard…"). We're the INSIDER, not a fan who just discovered them.
  • Write more than 100 words per email (cold opens 50-70, follow-ups 25-40)
  • Include multiple CTAs in one email
  • Use variables that haven't been populated (they render blank)
  • Mention specific compensation numbers (dollar amounts, salary ranges, OTE figures, equity %). Always position as "competitive", "top of market", or "best in class". Keep structural phrasing ("base + equity", "OTE with equity"), just strip the dollars. Exception: client-specific campaigns where the client has explicitly requested a range be included.

Humanizer pass

Every campaign MUST go through the humanizer skill before push. No exceptions. It catches em dashes, promotional phrasing, rule-of-three, negative parallelism, copula avoidance ("serves as" -> "is"), generic conclusions and hedges.

Workflow: generate the draft sequence -> humanize every variant body and subject -> replace the draft -> validate (em dash, voice, buzzword, {{accountSignature}} checks) -> PATCH the live campaign or push as new.

6. API push (Instantly API v2)

Use the Instantly API v2 directly via curl. Do NOT use MCP or deepline for pushes.

API key stored in each client's .env as INSTANTLY_API_KEY.

Create campaign

POST https://api.instantly.ai/api/v2/campaigns
Authorization: Bearer {INSTANTLY_API_KEY}

Update sequence (after creation)

PATCH https://api.instantly.ai/api/v2/campaigns/{id}

Push leads (one at a time)

POST https://api.instantly.ai/api/v2/leads

Update existing leads

PATCH https://api.instantly.ai/api/v2/leads/{id}

Use curl via subprocess (Python's urllib gets blocked by Cloudflare), and push in 950-lead batches.

Campaign-create gotchas

  • campaign_schedule.timezone must be in Instantly's IANA enum: America/New_York and America/Los_Angeles return 400. Use America/Chicago or America/Detroit.
  • A/B auto-optimize is auto_variant_select: {"trigger": "reply_rate"}. The {"metric": ...} shape is wrong; if unsure, omit it on create and set it on the PATCH.
  • Attach senders at create time with email_tag_list (tag UUIDs). Use the workspace's Sending tag and confirm it has senders (GET /accounts?tag_ids=<tag>): one workspace's old ALL tag had 0 senders and stranded 134 campaigns. An explicit email_list beats email_tag_list; write both in one PATCH.
  • Continuously-enrolled campaigns need is_evergreen: true. The API default is off and the campaign silently completes and stops sending (one workspace had 887 rows pointed at one).
  • HTML bodies: <div>...</div><br><div>...</div> with ONE <br> between blocks. No <p>&nbsp;</p> spacers; collapse repeated <br> before PATCH.

Default settings

SettingValue
Daily limit1000
A/B optimizereply rate
Stop on replytrue
Stop on auto-replytrue
Open trackingfalse
Link trackingfalse
Schedule8am-5pm weekdays in an accepted IANA tz (see above)
Sender accountsthe workspace's Sending tag (or ask user)
Evergreentrue for anything continuously enrolled

7. Pre-push checklist

  1. Every lead has first_name (no blanks)
  2. Every lead has company_name (no blanks)
  3. Company names normalized (no LLC/Inc, proper casing)
  4. First names normalized (proper casing)
  5. validate_before_push.py passes
  6. Copy has been run through humanizer
  7. Spintax uses {{RANDOM |...|...}} format
  8. No em dashes in copy
  9. No exclamation marks in copy
  10. Each email is under 100 words
  11. {{firstName}} is in emails 1 and 3
  12. Subjects are under 50 characters
  13. Emails 2 and 4 have blank subjects
  14. ALL available fields mapped to custom_variables (non-core fields only there)
  15. Every body ends with {{accountSignature}}; no hardcoded sender names
  16. No observer voice about the client; no competitor names
  17. Step 1 delay = 2 days; delays 2/2/3/2
  18. Sender tag has senders; is_evergreen set if continuously enrolled

8. Output format

When creating a campaign, output: the campaign name, each email's subject variants and body (spintax + variables), the variable mapping, a settings summary (daily limit, schedule, tracking, senders), lead count and segment breakdown, and the final copy after the humanizer pass.

Other skills for the same job

Different authors, same problem. Matched on the words in the skill name, across every library in the catalogue except this one.

Need help setting it up?

This page tells you what instantly-copywriter does and what it needs. Cheetah builds the agent setup it runs inside: data, CRM, sequencing and the guardrails.

Book a call →

The directory stays free. There is nothing gated behind this.