Ad Copy skill for Codex
Variants that differ in idea, not in synonyms. Codex builds ad variants from distinct angles such as outcome, pain, proof and offer, keeps search ads tied to their keyword theme and to the landing page hero, and writes RSA headlines that read in any order. It checks Meta personal-attribute and special category rules, validates character limits in code, and produces an import-ready CSV without launching or spending anything.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- ad-copy Installs to ~/.agents/skills/ad-copy/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install ad-copy The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project ad-copy Installs to .agents/skills/ad-copy/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/ad-copy.zipunzip -o ad-copy.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) ad-copy PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $ad-copy, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Writes paid ad copy and structured variant sets for Google Ads responsive search ads, Microsoft Ads, Meta (Facebook and Instagram), LinkedIn, Reddit and TikTok - headlines and descriptions within each platform's character limits, angle-based variants, keyword-to-ad relevance, landing page message match and policy-safe claims - and exports them as CSV for bulk upload. Use when asked to write Google Ads headlines, create Facebook ad variations, refresh fatigued creative, or build an ad testing matrix.
Use it when
- A search campaign needs responsive search ad headlines and descriptions
- Social ads need fresh variants after CTR drops and frequency climbs
- You need an angle-based testing matrix for paid campaigns
- Ad text keeps getting disapproved and you suspect claims or policy wording
Use something else when
- You are writing the landing page the ads point to: use landing-page-copy
- You need tracking parameters and attribution for the campaigns: use utm-attribution
How to check the result
Every field passes the scripted length check, five hand-assembled RSA combinations read naturally, and each ad's offer matches its landing page.
The mistake it prevents
Writing fifteen near-identical headlines, which drops Ad Strength and leaves the system nothing different to test.
Ad copy is done well when each ad speaks to one audience with one angle, the headline echoes what the searcher typed or the scroller cares about, the landing page repeats the promise, every field fits its limit, and variants differ in idea rather than in synonyms.
Inputs
| Input | Why |
|---|---|
| Product, offer and the single conversion action | Copy sells one action |
| Audience segment and stage (search intent, retargeting, cold) | Changes the angle |
| Landing page URL and its hero copy | Message match; mismatch costs Quality Score and conversions |
| Proof: numbers, reviews, awards, customer names cleared for ads | Specific claims beat adjectives |
| Keywords and ad groups (search), or audience definitions (social) | Relevance per group |
| Past performance export | Which angles already won or fatigued |
| Brand voice and legal constraints | Required disclaimers, banned claims |
Platform limits (current as of 2025-2026; confirm in the platform if critical)
| Platform / format | Field limits |
|---|---|
| Google Ads RSA | Up to 15 headlines x 30 chars; up to 4 descriptions x 90 chars; 2 display paths x 15 chars. Min 3 headlines, 2 descriptions |
| Microsoft Ads RSA | Same structure as Google; imports from Google Ads |
| Google Performance Max asset group | Headlines 30, long headlines 90, descriptions 90 (one at 60 or less), business name 25 |
| Meta (Facebook/Instagram) | Primary text: no hard cap but ~125 chars show before “more”; headline ~40 shown; description ~30 shown |
| LinkedIn single image | Intro text 600 max (~150 shown); headline 200 max (~70 shown) |
| Headline up to 300 chars; keep the hook in the first line | |
| TikTok | Ad text up to ~100 chars; the video carries the message |
Double-width characters (CJK) count as two in Google Ads.
Angles: vary the idea, not the wording
Write variants across distinct angles, then pick 3-5 angles per ad group:
| Angle | Example (project management tool) |
|---|---|
| Outcome | “Ship Projects 2 Weeks Sooner” |
| Pain | “Tired of Status Meetings?” |
| Proof | “Used by 4,000 Agencies” |
| Specific feature tied to benefit | “Client Approvals in One Click” |
| Offer / risk reducer | “Free for Teams Under 10” |
| Comparison (no competitor trademarks unless permitted) | “The Spreadsheet Replacement” |
| Audience callout | “Built for Creative Agencies” |
Google Ads RSA workflow
- [ ] 1. One ad group = one tight keyword theme; copy written per group
- [ ] 2. 3-4 headlines containing the core keyword or close variant
- [ ] 3. 3-4 headlines on benefits/outcomes, 2-3 on proof, 2-3 on offer/CTA
- [ ] 4. 4 descriptions: benefit + proof + CTA combos, each readable alone
- [ ] 5. Check every headline makes sense next to every other (any order, any pair)
- [ ] 6. Pin only what legally or logically must be in position 1 (brand, disclaimer)
- [ ] 7. Paths use keywords: /project-management/agencies
- [ ] 8. Validate lengths; export CSV for Google Ads Editor
- 15 headlines with low overlap gives the system room; near-duplicates lower Ad Strength.
- Pinning reduces combinations; pin a set of 2-3 headlines to a slot rather than one.
- Use assets (sitelinks, callouts, structured snippets, images) to carry secondary messages instead of cramming them into headlines.
- Keyword insertion (
{KeyWord:Default text}) only where every keyword in the group reads correctly in the sentence.
Meta, LinkedIn and other social
- The creative (image or video) does most of the work; copy supports it. Write the copy for a specific creative concept, not in isolation.
- First line of primary text is the hook: a specific problem, result or question.
- Headline states the offer or outcome; CTA button matches the landing action.
- Write for the placement: Stories/Reels text sits on 9:16 creative, keep overlay text out of the top and bottom ~14% safe zones.
- LinkedIn: job-title targeting allows sharper callouts (“For RevOps leaders…”).
- Meta Advantage+ creative may vary text automatically; review what it is allowed to change.
Claims and policy
- Superlatives and numbers need substantiation the advertiser can produce.
- Never imply knowledge of personal attributes on Meta (“Are you diabetic?”, “Other single parents…”): Meta rejects copy asserting or implying health, financial status, sexual orientation and similar attributes.
- Special Ad Categories on Meta (credit, employment, housing, social issues, elections, politics) restrict targeting and require declaration.
- Google restricted categories (healthcare, financial services, gambling, alcohol) need certification or carry extra rules.
- Trademarks: do not use competitor brand names in ad text unless the platform policy and local law allow it; bidding on them is a separate question.
- Prices and offers must match the landing page exactly, including currency and tax display.
Flag uncertain claims as [VERIFY] and anything in a regulated category for the user’s
legal or compliance review.
Testing and refresh
- Test angles, not punctuation. One variable per experiment when you need a clean answer.
- Google: compare asset performance labels and conversions by asset combination over several weeks; replace “Low” assets, keep “Best”.
- Social: frequency above ~3-4 with falling CTR signals creative fatigue; rotate in a new angle, not a reworded one.
- Judge by cost per conversion (or ROAS) from the landing page, not by CTR alone; a
clickbait ad wins CTR and loses money. Experiment design belongs to
ab-testing.
Export formats
Generate a CSV and validate lengths before handing it over:
import csv
ads = [
{"Campaign": "Search - PM Agencies", "Ad group": "agency project management",
"Headlines": ["Agency Project Management", "Client Approvals in One Click",
"Used by 4,000 Agencies", "Free for Teams Under 10"],
"Descriptions": ["Plan, approve and bill client work in one place. Start free today.",
"Replace status meetings with live project timelines your clients can see."],
"Path 1": "agencies", "Path 2": "project-mgmt",
"Final URL": "https://example.com/agencies"},
]
problems = []
rows = []
for ad in ads:
row = {k: v for k, v in ad.items() if k not in ("Headlines", "Descriptions")}
for i, h in enumerate(ad["Headlines"], 1):
if len(h) > 30: problems.append(f"headline {i} {len(h)}/30: {h}")
row[f"Headline {i}"] = h
for i, d in enumerate(ad["Descriptions"], 1):
if len(d) > 90: problems.append(f"description {i} {len(d)}/90: {d}")
row[f"Description {i}"] = d
for p in ("Path 1", "Path 2"):
if len(row[p]) > 15: problems.append(f"{p} {len(row[p])}/15")
rows.append(row)
if problems:
raise SystemExit("\n".join(problems))
with open("rsa_import.csv", "w", newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=list(dict.fromkeys(k for r in rows for k in r)))
w.writeheader(); w.writerows(rows)
print(f"wrote {len(rows)} ads")
Column names for bulk import differ by tool and version (Google Ads Editor, Microsoft Advertising Editor, Meta bulk import): export a template from the user’s account and map to its exact headers. Uploading or enabling ads spends money: hand over the file and let the user import and launch, or get explicit approval for budget and launch first.
Gotchas
- Headlines that only make sense in order (“Fast Setup.” + “And Easy Billing.”). RSA combines them in any order.
- Same headline with trivial changes across 15 slots; gives the system nothing to learn.
- Ad promises a free trial; landing page shows “Book a demo”. Conversions crash.
- Writing one ad and running it everywhere; search intent and social scroll are different moments.
- Counting characters by eye; run the check.
Verification
- Length check passes for every field.
- Each RSA reads sensibly for 5 random headline combinations you assemble by hand.
- Every ad’s offer and wording matches its final URL’s hero.
- Claims have sources; regulated-category ads flagged for review.
Output template
## Ad set: <campaign> / <ad group or audience>
**Audience and intent:** ... **Landing page:** <url> (hero: "...")
**Angles:** outcome, proof, offer
| # | Field | Text | Chars | Angle | Pin |
**Assets:** sitelinks / callouts
**Claims to verify:** ...
**Files:** rsa_import.csv (not uploaded; awaiting approval) What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "Ad Copy"
short_description: "Variants that differ in idea, not in synonyms"
default_prompt: "$ad-copy Write 15 headlines and 4 descriptions for our Google Ads agency project management ad group" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.