Skip to main content

Overview

Write Meta ad copy that’s matched to the actual image creative — not generic copy pasted across every ad. Each image gets copy that reinforces its specific message, written in the brand’s voice, informed by what’s already working in the account. The critical rule: Copy reinforces the image, never repeats it. If the image shows “$17K/month”, the body tells the story behind that number. If the image shows a stressed host, the copy describes the chaos. Image and copy are two halves of one message.

Brand Memory Integration

This skill reads brand context to make every piece of copy sound like the brand, not a template.

Reads

workspace/brand/voice-profile.md
optional
Match tone, vocabulary, forbidden words. Show: “Voice loaded — [tone summary].”
workspace/brand/positioning.md
optional
Use angle as copy’s foundation. Show: “Positioning: [angle].”
workspace/brand/audience.md
optional
Know who we’re talking to, their awareness level, their language. Show: “Writing for [audience summary].”
If workspace/brand/ doesn’t exist, the skill works standalone. It will ask for ICP, voice notes, and forbidden words inline.

The Process

Step 1: Pull What’s Already Working

Before writing a single word, look at the account. What copy is converting?
# Top performers by CTR — last 30 days
curl -s "https://graph.facebook.com/v22.0/ACT_ID/insights?\
level=ad&fields=ad_name,impressions,clicks,ctr,cpc,cost_per_action_type\
&date_preset=last_30d&sort=ctr_descending&limit=20\
&access_token=$FACEBOOK_ACCESS_TOKEN"
Then pull copy from winners:
# Get creative ID from the ad
curl -s "https://graph.facebook.com/v22.0/AD_ID?fields=creative{id}&access_token=$FACEBOOK_ACCESS_TOKEN"

# Get the actual copy
curl -s "https://graph.facebook.com/v22.0/CREATIVE_ID?fields=asset_feed_spec&access_token=$FACEBOOK_ACCESS_TOKEN"
Extract patterns:
  • Headline length and structure (questions? numbers? commands?)
  • Body copy hooks (opens with pain? data? question? story?)
  • Social proof claims (X customers, Y% increase, Z revenue)
  • Reading level (short punchy vs. narrative)
  • CTA that converts (Learn More vs Get Started vs Book Demo)
Show the user what you found: “Your top 3 ads all open with a specific number and close with social proof. Average headline: 32 chars. I’ll match that pattern.”

Step 2: Load Brand Context

NeedWhereFallback
ICPaudience.md or CLAUDE.mdAsk: “Who’s this for?”
Voicevoice-profile.mdAsk: “Any words to avoid? Tone preference?”
Pain pointsaudience.mdExtract from top-performing copy
Key statsBrand brief / CLAUDE.mdAsk: “What proof points can I use?”
Forbidden wordsvoice-profile.mdDefault ban list (see below)
Default forbidden words (always banned unless brand explicitly uses them): revolutionary, game-changer, cutting-edge, innovative solution, transform your business, unlock your potential, unleash, harness the power, leverage (as verb), next-level, best-in-class, world-class, seamless, robust, synergy

Step 3: Analyze Each Image Creative

For every image that needs copy, use vision to identify:
  1. Visual format — Notes app, receipt, tweet, chart, kitchen photo, poster, review, slack, etc.
  2. On-image text — What copy is already baked into the image?
  3. Angle/hook — What psychology is the image pulling?
  4. Mood — Organic/found-footage, professional, urgent, casual, dark, bright
  5. Funnel stage — Awareness (problem), consideration (solution), conversion (offer)

Step 4: Write Copy Using Psychology

Every ad pulls at least one psychological lever. Rotate across variants so Meta can test.

The Four Horsemen (Kallaway Framework)

HorsemanTriggerOpening Pattern
MoneyRevenue loss, ROI, cost savings”Every [thing] you miss is worth $X.”
TimeWasted hours, efficiency, speed”Your [person] isn’t a [wrong role].”
StatusPeer comparison, industry moves”X+ [peers] already [did thing].”
FearLoss aversion, competitive threat”The [competitor] down the street already has this.”

Matching Psychology to Image Format

Image FormatBest PsychologyCopy Tone
Notes app / screenshotFear + MoneyCasual first-person discovery
Fake tweet / socialStatus + FearConversational, peer voice
Data visualizationMoney + StatusAuthority, numbers-forward
Receipt / POSMoney + TimeSpecific, transactional
Kitchen / still lifeFear + TimeEmotional, atmospheric
Review / testimonialStatus + MoneyThird-person proof
Slack / workplaceTime + StatusInsider, operational
News articleFear + StatusUrgent, newsjacking
Before/afterMoney + TimeContrast, transformation
Bold text / posterAnyDirect, declarative
Google reviewStatus + MoneySocial proof, customer voice
Whiteboard / letter boardTime + FearPhysical, tangible
Lock screen / notificationsFear + TimeUrgency, real-time

Step 5: Apply Copy Specs

These specs come from analyzing hundreds of winning Meta ads:
ElementSpecWhy
Headline length25-40 chars, never >50Truncation on mobile kills CTR
Body word count50-120 wordsShort enough to read, long enough to convince
Paragraphs2-3, each 1-2 sentencesMobile readability — walls of text = scroll past
Numbers required≥1 per variant ($, %, or count)Specificity = credibility
Social proofInclude where natural”X+ customers” is the highest-converting pattern
Opening linePain, stat, or bold claimNever brand name, never “Introducing”
Closing lineSocial proof + soft CTA”X+ already made the switch.”
Line breaksAfter every 1-2 sentencesScannability on mobile
No em dashesUse hyphens or restructureRendering inconsistency across placements
No emojis in bodyUnless brand voice uses themMost B2B/professional ads don’t

Step 6: Generate Degrees of Freedom Variants

Meta’s algorithm tests combinations. Give it options: Per creative:
  • 3-5 headline variants (different angles)
  • 3-5 body copy variants (different psychology)
  • 1-2 description variants (supporting stat or benefit)
Rules for variant quality:
  • Each variant MUST hit a different Horseman. V1=Fear, V2=Money, V3=Status, etc.
  • No two headlines should use the same structure. Mix questions, statements, numbers, commands.
  • No two body variants should open the same way.
  • At least one variant should feel like organic content (not an ad).

Step 7: Cross-Reference with Account Winners

Before finalizing, check your copy against Step 1 findings:
  • Does headline length match what’s working?
  • Are you using similar hook structures?
  • Does reading level match?
  • Are your social proof claims consistent with what the account already says?
Flag any mismatches: “Your top performers use question headlines. 2 of my 3 headlines are questions — aligned.”

Funnel Stage Adjustments

StageObjectiveCopy FocusCTAProof Level
ToFuAwareness / TrafficProblem awareness, curiosityLearn MoreLight — “X+ use this”
MoFuConsideration / LeadsSolution proof, specific featuresGet Free Audit / See DemoMedium — case study snippets
BoFuConversion / SalesUrgency, risk reversal, pricingStart Free Trial / Book DemoHeavy — ROI numbers, testimonials
Adjust body copy density:
  • ToFu: 50-80 words (fast read, curiosity-driven)
  • MoFu: 80-120 words (more detail, proof points)
  • BoFu: 60-100 words (tight, urgent, specific offer)

Output Format

For each creative:
## [Creative Name]
*Image: [brief description of what the image shows]*
*Format: [notes app / tweet / receipt / etc.]*
*Psychology: [primary Horseman] + [secondary]*
*Funnel: [ToFu / MoFu / BoFu]*
*Matched to image: [one line on how copy reinforces the visual]*

### Headlines
1. [headline] ([XX] chars) — [Horseman]
2. [headline] ([XX] chars) — [Horseman]
3. [headline] ([XX] chars) — [Horseman]

### Body Copy

**V1 — [Horseman]: [Angle name]**
[body text]
*[word count] words*

**V2 — [Horseman]: [Angle name]**
[body text]
*[word count] words*

**V3 — [Horseman]: [Angle name]**
[body text]
*[word count] words*

### Descriptions
1. [description]
2. [description]

asset_feed_spec Output

The skill outputs copy in Meta’s Graph API format, ready for upload:
{
  "bodies": [
    {"text": "Your V1 body copy here. Pain point opener, specific stat, soft CTA."},
    {"text": "Your V2 body copy here. Different angle, different psychology."},
    {"text": "Your V3 body copy here. Social proof heavy. Numbers up front."}
  ],
  "titles": [
    {"text": "Headline One - 35 Chars"},
    {"text": "Question Headline?"},
    {"text": "Stat-Driven: 3X Results"}
  ],
  "descriptions": [
    {"text": "Supporting benefit statement."},
    {"text": "Secondary proof point."}
  ]
}
This JSON format is required by Meta’s Graph API asset_feed_spec field when creating creatives. It enables Degrees of Freedom optimization where Meta tests all combinations.

Campaign File Output

After generation, save to campaign directory:
workspace/campaigns/{campaign-name}/ads/
  {creative-name}.md          <- Full copy document
  {creative-name}.json        <- asset_feed_spec JSON
Append to workspace/brand/assets.md:
| {creative-name} copy | ad-copy | YYYY-MM-DD | {campaign-name} | draft | 3 headlines, 3 bodies |

Graph API Integration

Fetch Top Performers

TOKEN="$FACEBOOK_ACCESS_TOKEN"
ACCOUNT="act_123456789"

# Get top ads by CTR
curl -s "https://graph.facebook.com/v22.0/$ACCOUNT/insights?\
level=ad\
&fields=ad_id,ad_name,impressions,clicks,ctr,cpc\
&date_preset=last_30d\
&sort=ctr_descending\
&limit=20\
&access_token=$TOKEN" | jq .

Extract Copy from Winning Ads

# Get creative from ad
AD_ID="23847111111111"
curl -s "https://graph.facebook.com/v22.0/$AD_ID?\
fields=creative{id,name,asset_feed_spec}\
&access_token=$TOKEN" | jq '.creative.asset_feed_spec'
Response:
{
  "bodies": [
    {"text": "Every reservation you miss is worth $47. That's the average..."},
    {"text": "Your host isn't a secretary. But they're spending 3 hours..."}
  ],
  "titles": [
    {"text": "$47 per missed call"},
    {"text": "Your host deserves better"}
  ]
}

Anti-Patterns

Avoid these common mistakes:
  • Reusing copy across creatives — Every image gets unique copy that matches IT
  • Same psychology across variants — V1/V2/V3 must hit different Horsemen
  • Starting with brand name — Nobody cares about your brand in ToFu
  • Generic SaaS language — See forbidden words list
  • Emoji bullet lists — Looks like every other ad on Meta
  • Ignoring account data — Always check what’s working first
  • Copy that repeats the image — Image says the number, copy tells the story
  • Long paragraphs — 3+ sentences = scroll past on mobile
  • Same headline structure — Mix questions, stats, commands, statements
  • Hard sell CTAs in ToFu — “Sign up now” in awareness = low CTR

Quick Mode

If the user provides images and says “write copy for these” without specifying a full workflow:
  1. Skip account data pull (or use cached if available)
  2. Load brand context if it exists
  3. Analyze images → write matched copy → output in standard format
  4. Still suggest checking against account data as a next step

Integration with Other Skills

Workflow

1. ad-creative-monitor → Detects "Notes App Hero" fatigued (CTR dropped 40%)
2. ad-copy-generator   → Write 3 new body variants matched to the same image
3. ad-upload           → Push fresh creative, attach to existing ad
4. meta-ads            → Monitor new creative performance next 7 days

Data Flow

# What's already working?
curl "https://graph.facebook.com/v22.0/$ACCOUNT/insights?..."
# → Extract: headline length, hook types, social proof format

Environment Variables

FACEBOOK_ACCESS_TOKEN
required
Facebook access token with ads_read scope for pulling account performance data
# Get token from social-cli config
export FACEBOOK_ACCESS_TOKEN=$(jq -r '.profiles.default.tokens.facebook' ~/.social-cli/config.json)

Next Steps

Build docs developers (and LLMs) love