Agent skill
instantly-copywriter
Create Instantly email campaigns with proper naming, 2x2 thread structure, Instantly-native spintax, humanized copy, variable mapping, and API push.
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-toolsin the frontmatter. It does act, so it runs under whatever permissions your session already grants. - Actions present in the files
- shell
Install it
View source on GitHub ↗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.
The skill
Source on GitHub ↗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 rolesGTM-- go-to-market roles (AE, SDR, marketing)ANNIV-- anniversary/timing-based outreachRE-ENG-- re-engagement of prior candidatesSIG-- 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.
| Thread | Subject | Delay | Purpose | Words | |
|---|---|---|---|---|---|
| Email 1 | A | Has subject (2 A/B variants) | +2 days | Cold open. {{firstName}}, spintax, company-specific hook. | 60-80 |
| Email 2 | A | Blank (same thread) | +2 days | Follow-up in same thread. New detail, brief. | 30-50 |
| Email 3 | B | Has subject (2 A/B variants) | +3 days | New thread. Different angle, different hook. | 60-80 |
| Email 4 | B | Blank (same thread) | +2 days | Soft 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 vary | Variant A | Variant B |
|---|---|---|
| Hook/angle | Lead with investors/funding | Lead with product/problem |
| CTA | "Would you be up for a quick call?" | "Want me to send over more details?" |
| Tone | Direct and numbers-heavy | Conversational and story-driven |
| Social proof | Name-drop VCs and investors | Name-drop enterprise clients |
| Opening | "Hey {{firstName}}," + company pitch | "Hey {{firstName}}," + observation about their background |
| Length | Short (40-50 words) | Standard (60-80 words) |
Each variant should ALSO have spintax within it for additional variation. Two levels of variation:
- Variant level: genuinely different strategy/angle/CTA (Instantly picks the winner)
- 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:
- Every lead has
first_namepopulated (no blanks) - Every lead has
company_namepopulated (no blanks) - Run
validate_before_push.pywhich 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.
| Category | Supabase field | Instantly variable | Example |
|---|---|---|---|
| Contact | first_name | firstName (+ API first_name) | "Rafi" |
last_name | (API last_name) | "Ayub" | |
linkedin_url | linkedin_url | "linkedin.com/in/..." | |
| Role | current_company | companyName (+ API company_name) | "Anthropic" |
current_title | title | "Member of Technical Staff" | |
current_company_domain | companyDomain | "anthropic.com" | |
| Background | all_schools | schools | "Stanford; UT Dallas" |
bio | bio | "AI/ML engineer" | |
honors | honors | "NSF Fellow" | |
location | location | "San Francisco, CA" | |
total_tenure_years | tenureYears | "7.0" | |
founding_role | foundingRole | "True" | |
personal_website | personalWebsite | "rafiayub.com" | |
(from job_descriptions) | pastEmployers | "Meta; Microsoft" | |
| Scoring | quality_score | quality_score | "68" |
quality_tier | quality_tier | "Tier I" | |
tier | tier | "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)
- First names: capitalize ("austin" -> "Austin", "JANE" -> "Jane").
- 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").
- Use
normalize_names.jsorvalidate_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:
- Founder pedigree, "The team came out of [prior co]"
- Backer signal, "[VC] is their lead investor"
- The bet, "Most teams ship [old]. [Co] bet [new]."
- Named customer / traction, "[Customer] just signed"
- The hard problem, one specific technical or business challenge
- Employee number + equity, "Employee ~#[N], pre-Series-B equity"
- Vision, one-sentence future state if they win
- 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").
| Bad | Good |
|---|---|
| "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.
| Passes | Fails |
|---|---|
| "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
humanizerskill 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.timezonemust be in Instantly's IANA enum:America/New_YorkandAmerica/Los_Angelesreturn 400. UseAmerica/ChicagoorAmerica/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 oldALLtag had 0 senders and stranded 134 campaigns. An explicitemail_listbeatsemail_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> </p>spacers; collapse repeated<br>before PATCH.
Default settings
| Setting | Value |
|---|---|
| Daily limit | 1000 |
| A/B optimize | reply rate |
| Stop on reply | true |
| Stop on auto-reply | true |
| Open tracking | false |
| Link tracking | false |
| Schedule | 8am-5pm weekdays in an accepted IANA tz (see above) |
| Sender accounts | the workspace's Sending tag (or ask user) |
| Evergreen | true for anything continuously enrolled |
7. Pre-push checklist
- Every lead has
first_name(no blanks) - Every lead has
company_name(no blanks) - Company names normalized (no LLC/Inc, proper casing)
- First names normalized (proper casing)
-
validate_before_push.pypasses - Copy has been run through humanizer
- Spintax uses
{{RANDOM |...|...}}format - No em dashes in copy
- No exclamation marks in copy
- Each email is under 100 words
-
{{firstName}}is in emails 1 and 3 - Subjects are under 50 characters
- Emails 2 and 4 have blank subjects
- ALL available fields mapped to custom_variables (non-core fields only there)
- Every body ends with
{{accountSignature}}; no hardcoded sender names - No observer voice about the client; no competitor names
- Step 1 delay = 2 days; delays 2/2/3/2
- Sender tag has senders;
is_evergreenset 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.
- cold-email-copywriter by markster · 50
- website-copywriter by markster · 50
- cold-email-copywriter by composio-community · 2
- email-copywriter by ekatasingh1107 · 2
- fivos-copywriter by fivosaresti · 1
- instantly-api by Cold-IQ · 1
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.