Claude Cursor Skill

meta-ads-audit

Meta Ads (Facebook + Instagram) account audit and business context setup. Run this first — it gathers business information, analyzes account health, and saves context that all other Meta ads skills reuse. Trigger on "audit my Meta ads", "audit my Facebook ads", "Meta ads audit",

LLM Mart · 0 points · 21 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download nowork-studio-notfair-plugin-meta-ads_audit-daf87d3.zip · 15 KB
Part of nowork-studio/notfair-plugin — 88 skills

Install

skills CLI npx skills add https://github.com/nowork-studio/notfair-plugin/tree/main/meta-ads/audit
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install nowork-studio-notfair-plugin@llmmart
Git git clone https://github.com/nowork-studio/notfair-plugin.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole nowork-studio/notfair-plugin collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Meta Ads Audit

Diagnose Meta (Facebook + Instagram) account health and persist business context for downstream skills (/meta-ads). Read-only — never mutates the account. The user runs /meta-ads to execute fixes you recommend.

Setup

Follow ../shared/preamble.md — MCP detection, OAuth, ad account selection.

Filesystem contract (MUST persist)

Artifact Path When
Business context {data_dir}/meta/business-context.json First full audit, or refresh when audit_date is >90 days old. Skip on scoped audits if file is fresh.
Personas {data_dir}/meta/personas/{accountId}.json Every full audit.

These are the handoff to /meta-ads — write them even if the report itself is short. Otherwise downstream skills operate without business context and produce generic output.

If a {data_dir}/business-context.json exists from /google-ads-audit (no meta/ subdir), read it as a starting point — most fields (services, brand voice, differentiators, locations, seasonality) are platform-agnostic. Then write the Meta-specific version to {data_dir}/meta/business-context.json with any Meta-specific overrides (different creative angles, different audiences, different funnel events).

business-context.json schema (shared with Google Ads where fields apply): business_name, industry, website, services[], locations[], target_audience, brand_voice{tone, words_to_use[], words_to_avoid[]}, differentiators[], competitors[], seasonality{peak_months[], slow_months[], seasonal_hooks[]}, social_proof[], offers_or_promotions[], landing_pages{}, unit_economics{aov_usd, profit_margin, ltv_usd, source}, notes, audit_date, account_id.

Meta-specific extensions: meta_funnel_events{top_of_funnel, mid_of_funnel, conversion}, creative_inventory{concepts[], formats[], aspect_ratios[]}, custom_audiences{purchasers, abandoners, engagers, list_uploads[]}, pixel_health{pixel_id, capi_enabled, emq_score, last_event_at}.

personas JSON schema: {account_id, saved_at, personas: [{name, demographics, primary_goal, pain_points[], decision_trigger, value, meta_creative_angles[], visual_cues[]}]}. The Meta version adds meta_creative_angles (e.g. "before/after demonstration", "founder-led explainer", "UGC review") and visual_cues (objects, settings, emotions that resonate with this persona). See references/persona-discovery.md.

Policy freshness check (run first)

Read ../shared/policy-registry.json. For each entry where last_verified + stale_after_days < today:

  • High-volatility → WebSearch the area for recent Meta Ads changes; compare to assumption. If drift, banner the report and suggest registry update.
  • Moderate-volatility → one-line "may warrant a check" note.
  • Stable → skip silently.

The Meta platform changes faster than Google Ads (Advantage+, attribution, learning behaviors) — check high-volatility entries every audit.

Phase 1 — Pull the audit dataset

Choose available read capabilities for the requested audit scope. Batch related reads where useful and supported; the rubric below describes evidence to consider, not a fixed call sequence.

A complete audit needs at minimum:

  • Ad account info (/{accountId}) — currency, timezone, business id, spend cap, account status, balance.
  • Pixel health (/{accountId}/customconversions + /{accountId}/adspixels) — pixel id, last activity, CAPI status, Event Match Quality (EMQ) score.
  • Campaigns (/{accountId}/campaigns) — id, name, objective, status, daily/lifetime budget, special_ad_categories, buying_type, bid_strategy, created_time. Last 90 days.
  • Ad sets (/{accountId}/adsets) — id, name, status, campaign_id, optimization_goal, billing_event, bid_strategy, daily_budget, lifetime_budget, attribution_spec, targeting (summary), promoted_object, learning_stage_info.
  • Ads (/{accountId}/ads) — id, name, status, ad set, creative summary (image/video, primary text, headline, description, CTA), effective_status.
  • Insights at campaign level — spend, impressions, reach, frequency, cpm, link CTR, link clicks, purchases (or other primary action), purchase value, ROAS, CPA.
  • Insights at ad set level — same fields, last 30 days.
  • Insights at ad level — top 50 ads by spend; same fields plus video metrics (3-sec views, ThruPlays) for video creatives.
  • Insights with breakdowns — placement (publisher_platform,platform_position), age/gender, device. Use these to spot placement losers and audience composition.
  • Recent edit activity — when available via /{adsetId} last_modified or /{adsetId} change history.

Compute aggregates in the script, return summarized JSON. Don't return all rows — rank, slice, summarize. The agent narrates the result; the script does the math.

Use available platform recommendations as an optional cross-check when they would help the analysis.

If a read fails, follow actionable recovery guidance. Clearly report missing evidence; continue independent findings only when the available data supports them.

Skip scoring entirely if totalSpend == 0 or activeCampaigns == 0. Go straight to business context.

Phase 2 — Scope handling

If the user narrows the audit ("focus on one campaign", "campaign X", "just check creative fatigue"):

  • Match campaign names by case-insensitive substring. If no match, list available campaigns and ask.
  • Filter the in-memory dataset before scoring — no extra API calls.
  • Account-level dimensions (Pixel health, attribution defaults) stay account-wide. Note "Scoped to: X" in the report.
  • Skip Phase 4 (business context refresh) on scoped audits if business-context.json is fresh.

Phase 3 — Score

Score each of the 7 dimensions 0–5 using references/account-health-scoring.md. Overall = round(sum × 100 / 35).

Score Label Meaning
0 Critical Broken or missing — actively losing money
1 Poor Major waste or missed opportunity
2 Needs Work Several clear issues
3 Acceptable Functional, room to improve
4 Good Well-managed, minor opportunities
5 Excellent Best-practice

Scope-aware: campaign-level dimensions reflect in-scope data; account-level dimensions (Pixel + CAPI, attribution setup) score account-wide with a note on scope impact.

Encoded heuristics — apply these, they aren't obvious

  • Pixel + CAPI is upstream of everything. EMQ < 7.0 means Meta can't match events well — Smart Bidding starves regardless of how good the creative is. STOP-condition input.
  • Reported ROAS systematically overstates true ROAS. Cross-check Meta-reported numbers against Shopify / GA4 / MMM where possible. The gap is the modeled-conversion premium and is typically 20–40% in ecom.
  • Frequency × CPM trend = creative diagnosis. Frequency > 3.0 with CPM rising ≥ 30% w/w is fatigue — recommend creative refresh, not budget cuts.
  • One ad set carrying > 70% of a campaign is fragility, not concentration. When it fatigues, the campaign collapses.
  • Audience overlap > 50% between sibling ad sets fragments signal. Consolidate; don't try to "fix" with bid caps.
  • Special Ad Category misclassification is a takedown risk, not just a policy nit. Surface as Critical regardless of current performance.
  • Manual placements without evidence is a sign of inherited-from-2018 thinking. Default should be Advantage+ Placements; deviations need data.

Pixel + Tracking Diagnosis Matrix

EMQ < 5 EMQ 5–6.9 EMQ 7.0+
CAPI off Critical — flying blind Critical — most events lost High — leaving 15–25% of events on the table
CAPI on, dedup off Critical — duplicated and weak signal High — duplicate counting risk Medium — match quality improves with dedup
CAPI on, dedup on High — match quality is the bottleneck Medium — improve event_id coverage Healthy

Phase 4 — Business context

Derive what you can from the data already pulled:

Field Source
business_name Ad account name (/{accountId} name field)
services Top campaigns by spend, ad set names, top-converting ad creatives
locations Targeting geo summary (countries / regions in active ad sets)
brand_voice Top-performing ad copy (primary text + headline)
creative_inventory.formats Mix of image / video / carousel observed in active ads
creative_inventory.aspect_ratios Aspect ratios across active ads (1:1, 4:5, 9:16)
meta_funnel_events.conversion Most common optimization event on top-spending ad sets
custom_audiences Custom audiences referenced in active ad set targeting
pixel_health From the Pixel detail call
website Apex domain from active ad final URLs

Then crawl the website (homepage + about + 1–2 top landing pages, parallel WebFetch) and merge into the schema. See references/business-context.md for the full crawl procedure.

Always ask the user: differentiators, competitors, seasonality, AOV + profit margin (essential for ROAS-aware scoring). Ask for everything else only if data + crawl can't answer it.

Phase 5 — Personas

Discover 2–3 personas from creative performance (which angles convert), top-spending audiences, and landing-page content — all from the dataset already in memory. Persist to {data_dir}/meta/personas/{accountId}.json. Each persona must be grounded in observable evidence (a converting ad set, a converting creative angle, a landing-page section) — no inventing. See references/persona-discovery.md.

Phase 6 — Report

Lead with the verdict, then the top 3 actions (with dollar impact when possible), then the scorecard, then evidence for dimensions scoring 0–2 only. Cite specific campaigns, ad sets, ads, and dollar amounts. Cap at ~80 lines.

State where any audit artifacts were actually saved. Do not claim hosted audit history unless a live result confirms it.

Guardrails

  1. Read-only skill. Diagnose; don't mutate. Every fix routes through /meta-ads. End the report with one handoff tied to the #1 action.
  2. STOP condition — if Pixel health scores 0–1 (EMQ < 5 or CAPI off in an ecom account), recommend pausing scaling decisions until tracking is fixed before recommending anything else. Everything downstream is unreliable.
  3. Always persist meta/business-context.json and meta/personas/{accountId}.json even if the report itself is short — downstream skills depend on them.
  4. Name names. Every finding cites specific campaigns, ad sets, ad creatives, and dollar amounts. "Some ad sets are underperforming" is not a finding.
  5. Never report Meta-reported ROAS without footnoting the modeled-conversion premium. "ROAS 3.2× (Meta-reported, 7DC1DV — typically overstates Shopify-attributed ROAS by 20–40%)" is honest. "ROAS 3.2×" is misleading.
Files (notfair-plugin)
  • references
    • account-health-scoring.md 9.7 KB
      # Meta Ads Account Health — Scoring Rubric
      
      The 7 dimensions scored 0–5 in `/meta-ads-audit`. Each dimension represents a distinct question the audit needs to answer. Overall score = `round(sum × 100 / 35)`.
      
      The dimensions are ordered from upstream (signal infrastructure) to downstream (scaling) — fix in this order, because fixes downstream of broken upstream are worthless.
      
      ---
      
      ## Dimension 1 — Pixel + CAPI Health
      
      *"Can Meta see what's happening?"*
      
      If signals are broken, every decision downstream is built on lies — and Smart Bidding can't optimize what it can't measure.
      
      ### Scoring
      
      | Score | Criteria |
      |---|---|
      | 5 | Pixel + CAPI deployed, EMQ ≥ 8.0, deduplicated by event_id, all key events firing (View, AddToCart, InitiateCheckout, Purchase) |
      | 4 | Pixel + CAPI deployed, EMQ 7.0–7.9, dedup correct, key events firing |
      | 3 | Pixel + CAPI deployed, EMQ 6.0–6.9, dedup correct |
      | 2 | Pixel only OR CAPI without dedup OR EMQ 5.0–5.9 |
      | 1 | Pixel only with EMQ < 5.0, OR CAPI broken, OR key events missing (e.g. Purchase event not firing) |
      | 0 | Pixel not firing OR no purchases recorded over 30 days despite spend ≥ 5× account CPA |
      
      ### Red Flags
      
      - Reported conversions ≥ 50% of clicks (tag firing on page load, not real conversions)
      - Conversion attribution mismatched between Meta and Shopify by > 50% (Shopify is ground truth; Meta should be within ±30%)
      - iOS share of conversions disproportionately low vs. impression share (ATT decline + missing CAPI)
      - All conversions show as `offsite_conversion.fb_pixel_custom` instead of named events (Pixel deployed without standard event mapping)
      
      ---
      
      ## Dimension 2 — Attribution & Measurement Setup
      
      *"Are we measuring success the right way?"*
      
      ### Scoring
      
      | Score | Criteria |
      |---|---|
      | 5 | Attribution window matches sales cycle, off-platform truth source connected (Shopify, GA4, MMM), holdout test or incrementality study run within last 12 months |
      | 4 | Attribution matches sales cycle; off-platform truth source loosely reconciled monthly |
      | 3 | Default attribution (7DC1DV) used without explicit consideration; some off-platform reconciliation |
      | 2 | No off-platform truth source consulted; Meta-reported numbers used directly for budget decisions |
      | 1 | Attribution window mismatched to sales cycle (e.g. 1-day click on a high-consideration purchase) |
      | 0 | Last-touch attribution used as the only measure; no awareness of modeled conversion premium |
      
      ### Red Flags
      
      - Reported ROAS used to scale into unprofitability because of modeled-conversion inflation
      - Attribution window changed mid-campaign without re-baselining (creates apples-to-oranges comparisons)
      - Multi-touch journey ignored (Meta gets full credit for the conversion when Google or organic was the real driver)
      
      ---
      
      ## Dimension 3 — Campaign Structure
      
      *"Is the account organized for the bidding LLM to learn?"*
      
      ### Scoring
      
      | Score | Criteria |
      |---|---|
      | 5 | 2–4 campaigns, prospecting/retargeting cleanly split, CBO default, no audience overlap > 25%, ASC running and outperforming |
      | 4 | Clean structure; minor overlap or one fragmentation issue |
      | 3 | Functional; some fragmentation or anti-patterns |
      | 2 | Multiple anti-patterns: campaigns split by creative/placement, > 5 campaigns, single-ad-set dependency |
      | 1 | Heavy fragmentation: 8+ campaigns, ad sets with > 50% mutual overlap, no funnel logic |
      | 0 | Special Ad Category misclassified, or campaign-level structure prevents Learning Phase exit on the majority of ad sets |
      
      ### Red Flags
      
      - > 50% audience overlap between active ad sets in the same campaign
      - One ad set > 70% of campaign spend (fragility, not concentration)
      - Branded campaigns running on Meta (almost always wasted spend — Meta is discovery, not search)
      - Campaign split by creative type or placement (anti-pattern; consolidate)
      - Special Ad Category vertical without proper classification
      
      ---
      
      ## Dimension 4 — Creative Health
      
      *"Is the creative giving the bidding LLM enough to optimize over?"*
      
      ### Scoring
      
      | Score | Criteria |
      |---|---|
      | 5 | 4–6 distinct concepts per active ad set, all aspect ratios uploaded, weekly creative refresh cadence, top concept's hook rate > 30% |
      | 4 | Adequate diversity; refresh cadence appropriate to spend velocity |
      | 3 | Functional creative, occasional refresh; some fatigue patterns |
      | 2 | Limited concept diversity (1–2 concepts per ad set), infrequent refresh, hook rate 15–25% |
      | 1 | Single concept across most ad sets, no refresh in 6+ weeks, hook rate < 15%, frequency > 4.0 with declining CTR |
      | 0 | Creative pool exhausted, declining performance for 4+ weeks, no replacement creative in pipeline |
      
      ### Red Flags
      
      - Frequency > 3.0 with CPM rising ≥ 30% w/w and CTR declining ≥ 30% w/w (active fatigue)
      - All ads on one ratio (1:1) when 4:5 + 9:16 are missing — placements unlocked by ratios are not being used
      - Hook rate (3-sec views / impressions) < 15% on video ads — viewers skip before any message lands
      - Same hero ad running for > 8 weeks at > $200/day (typical fatigue threshold passed)
      - "We don't have time to ship new creative" — usually the bottleneck for further scaling
      
      ---
      
      ## Dimension 5 — Audience Strategy
      
      *"Are we targeting in a way that lets the bidding LLM win?"*
      
      ### Scoring
      
      | Score | Criteria |
      |---|---|
      | 5 | Broad-targeting prospecting, lookalikes from quality seeds (purchasers / high-LTV), retargeting with proper exclusions, < 25% overlap |
      | 4 | Mostly broad with some targeted lookalikes; clean exclusions |
      | 3 | Functional targeting; some legacy interest stacks |
      | 2 | Heavy reliance on detailed-targeting interests, no purchasers exclusion in prospecting, audience overlap 25–50% |
      | 1 | Narrow interest stacks across all prospecting, multiple lookalike layers stacked, > 50% overlap |
      | 0 | No retargeting layer at all, or interest stacks contradict Meta's published guidance with no testing data to justify |
      
      ### Red Flags
      
      - Prospecting ad sets do not exclude past purchasers — paying to retarget converted customers
      - Lookalike seed > 90 days old (drift from current customer profile)
      - Stacked lookalikes (LAL 1–3% + LAL 1–5% in the same ad set) — pure overlap
      - No retargeting on cart abandoners despite > $1k/month spend
      - Account spending > $5k/month with only narrow-interest targeting (likely leaving Advantage+ Audience / broad gains on the table)
      
      ---
      
      ## Dimension 6 — Spend Efficiency
      
      *"Are we getting profitable returns on every dollar?"*
      
      ### Scoring
      
      | Score | Criteria |
      |---|---|
      | 5 | Account ROAS ≥ 1.5× Break-Even ROAS, < 5% spend on losing ad sets, MER ≥ industry-typical, healthy unit economics confirmed |
      | 4 | Account profitable; minor waste pockets |
      | 3 | Account roughly at break-even; meaningful waste in 1–2 ad sets |
      | 2 | Significant waste (≥ 15% of spend on ad sets below break-even), some campaigns unprofitable |
      | 1 | Most spend below break-even; reported ROAS papering over real losses |
      | 0 | Account-level losing money even on Meta-reported ROAS; structural unprofitability |
      
      ### Red Flags
      
      - > 20% of spend on ad sets with ROAS < 0.7 × Break-Even (waste pockets that survive on inertia)
      - Heavy reliance on Meta-reported ROAS for scaling decisions (no MER / Shopify cross-check)
      - Branded brand-defense campaigns running (almost always waste on Meta)
      - Audience Network not excluded for direct-response brands (often underperforms by 50%+)
      
      ---
      
      ## Dimension 7 — Scaling Readiness
      
      *"Can we 2× spend without breaking the account?"*
      
      ### Scoring
      
      | Score | Criteria |
      |---|---|
      | 5 | Strong ROAS headroom, fresh creative pipeline, multiple winning ad sets, audience saturation < 30%, ASC tested and working |
      | 4 | Headroom present; one constraint to address before aggressive scaling |
      | 3 | Some headroom; structural debt would slow scaling |
      | 2 | Account near saturation; doubling spend likely doubles CPA |
      | 1 | Account at the wall: frequency too high, audience overlap too high, no creative pipeline |
      | 0 | Account already losing money — scaling is moot until structural fixes land |
      
      ### Red Flags
      
      - Single dominant ad set carrying the account (fragility)
      - All winning audiences at > 50% saturation
      - No creative pipeline (no new concepts in production / approval queue)
      - Pixel + CAPI weak — scaling will starve faster than expected because match quality drops with volume
      
      ---
      
      ## Impression Share / Reach Saturation Matrix
      
      Meta doesn't publish "impression share" the way Google does. The proxies are **reach saturation** (Reach / Estimated Audience Size) and **frequency trend**:
      
      | | Saturation < 30% | Saturation 30–60% | Saturation > 60% |
      |---|---|---|---|
      | **Frequency < 2.0** | Healthy; clear scaling room | Healthy; some headroom | Audience nearly maxed; broaden |
      | **Frequency 2.0–3.5** | Warming up; monitor | Mature; refresh creative soon | Near saturation; rotate audience |
      | **Frequency > 3.5** | Anomalous — check tracking | Late-stage; refresh creative | Saturated; new audience required |
      
      Use this matrix to translate raw frequency / saturation numbers into action recommendations.
      
      ## Composite Score Mapping
      
      | Score | Verdict | Default Recommendation |
      |---|---|---|
      | 90+ | Excellent | Continue cadence; scale within 20% rule; test new creative concepts |
      | 75–89 | Good | Address 1–2 dimensions scoring < 4; otherwise hold steady |
      | 60–74 | Acceptable | Prioritized fix list across 2–3 dimensions; expect meaningful lift |
      | 40–59 | Needs Work | Structural fixes required before scaling; budget freeze advisable |
      | < 40 | Critical | STOP further spend until Pixel + CAPI + structure fixed; reset baseline |
      
      A score below 50 on Dimension 1 (Pixel + CAPI) overrides the composite — recommend the user stop scaling decisions until tracking is fixed regardless of how the rest of the account scores. Tracking is upstream of everything.
      
    • business-context.md 7.7 KB
      # Business Context — Crawl, Bootstrap, and Schema
      
      The single source of truth for gathering business context for Meta Ads. Used by `/meta-ads-audit` to write `{data_dir}/meta/business-context.json`, and read by `/meta-ads` on every invocation.
      
      If `{data_dir}/business-context.json` already exists from `/google-ads-audit`, **read it first as a starting point** — most fields (services, brand voice, differentiators, locations, seasonality, unit economics) are platform-agnostic. Then write a Meta-specific copy to `{data_dir}/meta/business-context.json` with overrides only where Meta-specific (creative inventory, custom audiences, Pixel health, funnel events).
      
      ## When to crawl
      
      - First audit on a new account.
      - Refresh when `audit_date` is > 90 days old.
      - Refresh when the user reports a positioning change, new product line, or seasonal pivot.
      - Always crawl on a full audit (Phase 4); crawl optionally on scoped audits if the file is fresh.
      
      ## What to crawl
      
      In one parallel `WebFetch` batch:
      
      1. **Homepage** — for value prop, hero copy, primary CTA, brand voice
      2. **About page** (if exists) — for founder story, team size, year founded, location
      3. **Top-spending ad's landing page** — message-match baseline; the page that's converting (or not)
      4. **One additional landing page** if a different campaign points elsewhere (e.g. category vs. PDP for ecom)
      
      For ecom specifically: also fetch the homepage and one PDP. The PDP tells you AOV-typical price point; the homepage tells you the brand promise.
      
      Keep the crawl lean — 4 fetches in parallel is fast. Don't crawl the full site; you don't need it.
      
      ## Schema — fields and how to populate
      
      ### Identity
      
      ```json
      {
        "business_name": "Brand Name",
        "industry": "ecommerce | saas | local_service | b2b_lead_gen | mobile_app | other",
        "website": "https://example.com",
        "account_id": "1234567890",
        "audit_date": "2026-05-01"
      }
      ```
      
      - `business_name` — from the Meta ad account name; cross-check against the homepage `<title>`.
      - `industry` — pick from the enum; if the user doesn't fit cleanly, use `other` and add a `notes` line.
      
      ### Offering
      
      ```json
      {
        "services": ["Premium skincare", "Subscription replenishment"],
        "differentiators": ["Founder-formulated", "Free shipping over $50", "60-day money-back"],
        "competitors": ["Competitor1", "Competitor2"],
        "locations": ["US", "CA"],
        "social_proof": ["10,000+ reviews", "Featured in Vogue", "Founder TEDx"],
        "offers_or_promotions": ["First-order 15% off", "Subscribe & save 20%"]
      }
      ```
      
      - Derive `services` from top-spending campaigns + landing page H1s.
      - Derive `differentiators` from homepage value-prop sections; **always confirm with the user** (homepage copy lies more than any other source).
      - `locations` are countries / regions where active ad sets target.
      - `competitors` is almost always a user input — Meta data doesn't reveal them directly.
      
      ### Brand voice
      
      ```json
      {
        "brand_voice": {
          "tone": "warm, plain-spoken, slightly cheeky",
          "words_to_use": ["clean", "real", "founder-led"],
          "words_to_avoid": ["cheap", "luxury", "ground-breaking"]
        }
      }
      ```
      
      Derive from top-performing ad copy (high link CTR + high CVR ads). If the user has a brand book, prefer it over inferred voice.
      
      ### Seasonality
      
      ```json
      {
        "seasonality": {
          "peak_months": [11, 12],
          "slow_months": [1, 2, 7],
          "seasonal_hooks": ["Mother's Day gifting", "Black Friday bundle", "New Year reset"]
        }
      }
      ```
      
      For ecom, Q4 (Nov-Dec) is universal. Industry specifics: floral peaks Feb (Valentine's) + May (Mother's Day); fitness peaks January; school supplies peak July-Aug. Ask the user for unique-to-them spikes.
      
      ### Unit economics (essential — request if missing)
      
      ```json
      {
        "unit_economics": {
          "aov_usd": 68.50,
          "profit_margin": 0.55,
          "ltv_usd": 220.00,
          "source": "user_provided"
        }
      }
      ```
      
      Without these, ROAS-aware scoring is impossible. **Always ask** if not in the file. `source` values:
      
      - `user_provided` — user told us directly
      - `inferred_from_template` — industry default (e.g. ecom apparel typical AOV $60, margin 55%) — flag as uncertain
      - `derived_from_data` — computed from Shopify or Stripe data the user provided
      
      Use `inferred_from_template` only as a temporary placeholder — never make a scaling recommendation without confirming.
      
      ### Landing pages
      
      ```json
      {
        "landing_pages": {
          "https://example.com/skincare-bundle": {
            "h1": "The clean skincare bundle that actually works",
            "primary_cta": "Get the bundle — $48",
            "form_fields": 0,
            "notes": "Direct PDP; no email gate"
          }
        }
      }
      ```
      
      For each ad-traffic landing page, capture H1, primary CTA, form-field count (or "no form"), and any notes that affect message match. This is what `/meta-ads-audit` references when scoring creative-to-page coherence.
      
      ### Meta-specific extensions
      
      ```json
      {
        "meta_funnel_events": {
          "top_of_funnel": "ViewContent",
          "mid_of_funnel": "AddToCart",
          "conversion": "Purchase"
        },
        "creative_inventory": {
          "concepts": ["UGC testimonial", "Founder explainer", "Product demo", "Lifestyle"],
          "formats": ["video_ugc", "video_studio", "static_lifestyle", "static_product"],
          "aspect_ratios": ["1:1", "4:5", "9:16"]
        },
        "custom_audiences": {
          "purchasers_180d": "audience_id_xxx",
          "abandoners_14d": "audience_id_yyy",
          "engagers_90d": "audience_id_zzz",
          "list_uploads": [
            { "name": "Email subscribers", "size": 28400, "uploaded": "2026-04-15" }
          ]
        },
        "pixel_health": {
          "pixel_id": "1234567890",
          "capi_enabled": true,
          "emq_score": 7.6,
          "last_event_at": "2026-04-30T19:45:00Z"
        }
      }
      ```
      
      These come from the audit data pulled in Phase 1 — fill them automatically. The user shouldn't need to provide pixel_health or custom_audiences manually; the API has them.
      
      ### Notes
      
      ```json
      {
        "notes": "Founder-led brand, prefers UGC over polished studio work. Avoid the word 'natural' (FTC trouble in 2024). Q4 is 50% of annual revenue — protect Black Friday creative pipeline."
      }
      ```
      
      Free-form. Capture anything that doesn't fit elsewhere but a future audit needs to know.
      
      ## Crawl bootstrapping flow
      
      1. **Read existing files in order:**
         - `{data_dir}/meta/business-context.json` (Meta-specific)
         - `{data_dir}/business-context.json` (Google Ads or shared)
         If either has content, use as starting point.
      
      2. **Pull data-derivable fields** from the Phase 1 audit dataset (services from campaign names, locations from targeting, voice from top-CTR ads, pixel_health from API, etc.).
      
      3. **Run the parallel WebFetch** for homepage + about + top-spending landing page + one additional. Merge findings.
      
      4. **Ask the user (one short batch of questions)** for fields that data + crawl can't answer:
         - Differentiators — what makes you the choice over competitors?
         - Competitors — top 2–3 you compete with
         - Seasonality specifics — your spikes vs. industry typical
         - **AOV + profit margin** (essential — block on this before any scaling recommendation)
         - LTV (if subscription / repeat business)
         - Anything weird I should know? (ad policy issues, recent rebrand, audience pivot)
      
         Ask in one block, not iteratively — it's faster for the user.
      
      5. **Write `{data_dir}/meta/business-context.json`** with `audit_date` set to today.
      
      ## What NOT to do
      
      - **Don't invent competitors.** If you don't know, ask. Inventing them and using them in differentiation copy is worse than skipping it.
      - **Don't infer unit economics from random web data.** Industry templates are explicit fallbacks; ad-hoc estimates from a homepage price are not.
      - **Don't crawl 20 pages.** 4 is the sweet spot; more adds noise without information.
      - **Don't write the file with empty `unit_economics`.** Either populate (even with `inferred_from_template`) or block on the user — but never ship it as `null` because downstream skills will break silently.
      
    • persona-discovery.md 6 KB
      # Persona Discovery — Meta Ads
      
      Discovery procedure for `{data_dir}/meta/personas/{accountId}.json`. Used by `/meta-ads-audit` (creates the file) and conceptually informs creative direction in `/meta-ads`.
      
      Personas on Meta are different from Search-engine personas. On Search, you discover personas from the **search terms** — the actual phrases people type. On Meta, you discover personas from **what creative resonates** — which angles, formats, and messaging convert. The data source is different, the framework is the same.
      
      ## Sources of evidence (in priority order)
      
      1. **Top-performing ad creatives** — the angle, format, and messaging of ads with highest ROAS / lowest CPA. The persona is the cohort the ad spoke to successfully.
      2. **Top-performing audiences** — which custom audiences and lookalike seeds convert best.
      3. **Landing page content** — which pages convert best from Meta traffic; the person on the page when conversion happens.
      4. **Demographic + placement breakdowns** — use available reporting capabilities for age, gender, and publisher-platform breakdowns. Gives a coarse demographic skew.
      5. **Customer email list / Shopify data** — if the user has it, real customer data > inferred from ads.
      
      The agent should *triangulate* across at least 3 of these — a persona supported only by demographic skew is too weak.
      
      ## How many personas
      
      2–3 is the right number for most accounts. More than 3 fragments creative production. One is usually too coarse — even single-product brands typically have a primary buyer and a gift-giver / influencer cohort.
      
      ## What goes in a persona
      
      ```json
      {
        "name": "Skincare Switcher Sarah",
        "demographics": "Women 28–42, urban / suburban US, income $60k–$110k",
        "primary_goal": "Find a skincare routine that works for sensitive adult skin without the marketing nonsense",
        "pain_points": [
          "Tried 5+ brands, breakouts continue",
          "Distrusts overly-polished beauty marketing",
          "Wants ingredient transparency"
        ],
        "decision_trigger": "Founder-led explainer video where the founder talks about their own skin journey",
        "value": "AOV $68, repeat-purchase 2.4× per year (LTV ~$220)",
        "meta_creative_angles": [
          "UGC review with visible before/after",
          "Founder-led explainer (1 min)",
          "Ingredient deep-dive carousel"
        ],
        "visual_cues": [
          "Bathroom counter setting (relatable, not aspirational)",
          "Natural lighting",
          "Real-skin texture (not retouched)"
        ]
      }
      ```
      
      ### Field-by-field
      
      - **`name`** — memorable, mnemonic. "Skincare Switcher Sarah" beats "Persona 1".
      - **`demographics`** — coarse; from the breakdown report and the audience definitions of best-performing ad sets.
      - **`primary_goal`** — what they're trying to accomplish. Phrase as the customer would say it.
      - **`pain_points`** — the frustrations the product solves. Pull from converting ad copy ("tired of breakouts that won't quit") and customer reviews if you have them.
      - **`decision_trigger`** — what specifically tipped them into buying. On Meta, this is often a creative angle or social-proof signal.
      - **`value`** — AOV + repeat behavior + estimated LTV. Tells you how much you can spend to acquire them.
      - **`meta_creative_angles`** — concrete creative approaches that work for this persona (3–5).
      - **`visual_cues`** — settings, lighting, casting that resonate. Distinct from `meta_creative_angles` — angles are message; cues are visual language.
      
      ## Process
      
      1. **Pull the data already in memory.** Top-spending campaigns + ad sets, top-converting ads with creative summaries, demographic breakdowns, audience definitions of best-performing ad sets.
      
      2. **Cluster.** Group ads with similar angles + demographic profiles. Look for distinct cohorts — e.g. "founder-led + ingredient-detail" wins on broad audience, "UGC + before-after" wins on lookalike-purchasers. Two distinct cohorts → two distinct personas.
      
      3. **Validate against landing pages.** Pull the landing page copy via `WebFetch`. Does the page speak to the persona's pain points and decision trigger? If yes, the persona is grounded. If no, either the persona is wrong, the page is wrong, or there's a creative-to-page mismatch worth surfacing.
      
      4. **Write the file.** Persist to `{data_dir}/meta/personas/{accountId}.json`:
      
      ```json
      {
        "account_id": "1234567890",
        "saved_at": "2026-05-01T14:00:00Z",
        "personas": [
          { ... },
          { ... }
        ]
      }
      ```
      
      ## Drop a persona if…
      
      - You can't name 3+ distinct converting ad creatives that speak to it (no evidence)
      - The demographic profile is identical to another persona (just rename and merge — they're the same cohort with different framing)
      - The user reads it and says "no, that's not us" (user knowledge > inferred persona; respect it and update)
      
      ## Avoid these failure modes
      
      - **Marketing-deck personas.** "Aspirational Andrea, age 35, lives in a coastal city, Pinterest-obsessed, drinks oat-milk lattes" sounds great in a deck and tells you nothing about how to make a Meta ad. Always pair every persona with concrete `meta_creative_angles` that have data behind them.
      - **Persona inflation.** Having 5+ personas means you're slicing demographics, not finding distinct cohorts. The test: can you describe one ad creative each persona would respond to, and one ad creative they wouldn't? If not, they're the same persona.
      - **Persona over-fitting.** A persona built on one converting ad set is fragile — when that ad set fatigues, the persona evaporates. Require 3+ converging signals (creative, audience, landing page).
      - **Stereotyping.** Personas based on demographics alone (age + gender + income) are stereotypes. The behavioral signal — what they bought, what creative they responded to, what page they read — is what matters.
      
      ## Update cadence
      
      Refresh personas when:
      
      - A new product line launches (new persona may be entering the account)
      - Quarterly during business-context refresh
      - When a creative concept that worked stops working (the persona may have moved on or the cohort has converted-out)
      
      Don't refresh continuously — personas are slower-moving than ad creative. Quarterly is appropriate.
      
  • SKILL.md 11.3 KB
    ---
    name: meta-ads-audit
    description: Meta Ads (Facebook + Instagram) account audit and business context setup. Use for account-health audits and business-context setup. Trigger on "audit my Meta ads", "audit my Facebook ads", "Meta ads audit", "set up my Meta ads", "onboard Meta", "Meta account overview", "how's my Meta account", "Meta health check", "what should I fix in my Facebook ads", or when the user is new to NotFair Meta and hasn't run an audit before.
    argument-hint: "<account name or 'audit my Meta ads'>"
    ---
    
    # Meta Ads Audit
    
    Diagnose Meta (Facebook + Instagram) account health and persist business context for downstream skills (`/meta-ads`). **Read-only** — never mutates the account. The user runs `/meta-ads` to execute fixes you recommend.
    
    ## Setup
    
    Follow `../shared/preamble.md` — MCP detection, OAuth, ad account selection.
    
    ## Filesystem contract (MUST persist)
    
    | Artifact | Path | When |
    |---|---|---|
    | Business context | `{data_dir}/meta/business-context.json` | First full audit, or refresh when `audit_date` is >90 days old. Skip on scoped audits if file is fresh. |
    | Personas | `{data_dir}/meta/personas/{accountId}.json` | Every full audit. |
    
    These are the handoff to `/meta-ads` — write them even if the report itself is short. Otherwise downstream skills operate without business context and produce generic output.
    
    If a `{data_dir}/business-context.json` exists from `/google-ads-audit` (no `meta/` subdir), read it as a starting point — most fields (services, brand voice, differentiators, locations, seasonality) are platform-agnostic. Then write the Meta-specific version to `{data_dir}/meta/business-context.json` with any Meta-specific overrides (different creative angles, different audiences, different funnel events).
    
    **business-context.json schema (shared with Google Ads where fields apply):**
    `business_name, industry, website, services[], locations[], target_audience, brand_voice{tone, words_to_use[], words_to_avoid[]}, differentiators[], competitors[], seasonality{peak_months[], slow_months[], seasonal_hooks[]}, social_proof[], offers_or_promotions[], landing_pages{}, unit_economics{aov_usd, profit_margin, ltv_usd, source}, notes, audit_date, account_id`.
    
    **Meta-specific extensions:**
    `meta_funnel_events{top_of_funnel, mid_of_funnel, conversion}, creative_inventory{concepts[], formats[], aspect_ratios[]}, custom_audiences{purchasers, abandoners, engagers, list_uploads[]}, pixel_health{pixel_id, capi_enabled, emq_score, last_event_at}`.
    
    **personas JSON schema:** `{account_id, saved_at, personas: [{name, demographics, primary_goal, pain_points[], decision_trigger, value, meta_creative_angles[], visual_cues[]}]}`. The Meta version adds `meta_creative_angles` (e.g. "before/after demonstration", "founder-led explainer", "UGC review") and `visual_cues` (objects, settings, emotions that resonate with this persona). See `references/persona-discovery.md`.
    
    ## Policy freshness check (run first)
    
    Read `../shared/policy-registry.json`. For each entry where `last_verified + stale_after_days < today`:
    - **High-volatility** → WebSearch the `area` for recent Meta Ads changes; compare to `assumption`. If drift, banner the report and suggest registry update.
    - **Moderate-volatility** → one-line "may warrant a check" note.
    - **Stable** → skip silently.
    
    The Meta platform changes faster than Google Ads (Advantage+, attribution, learning behaviors) — check high-volatility entries every audit.
    
    ## Phase 1 — Pull the audit dataset
    
    Choose available read capabilities for the requested audit scope. Batch related reads where useful and supported; the rubric below describes evidence to consider, not a fixed call sequence.
    
    A complete audit needs at minimum:
    
    - **Ad account info** (`/{accountId}`) — currency, timezone, business id, spend cap, account status, balance.
    - **Pixel health** (`/{accountId}/customconversions` + `/{accountId}/adspixels`) — pixel id, last activity, CAPI status, Event Match Quality (EMQ) score.
    - **Campaigns** (`/{accountId}/campaigns`) — id, name, objective, status, daily/lifetime budget, special_ad_categories, buying_type, bid_strategy, created_time. Last 90 days.
    - **Ad sets** (`/{accountId}/adsets`) — id, name, status, campaign_id, optimization_goal, billing_event, bid_strategy, daily_budget, lifetime_budget, attribution_spec, targeting (summary), promoted_object, learning_stage_info.
    - **Ads** (`/{accountId}/ads`) — id, name, status, ad set, creative summary (image/video, primary text, headline, description, CTA), effective_status.
    - **Insights at campaign level** — spend, impressions, reach, frequency, cpm, link CTR, link clicks, purchases (or other primary action), purchase value, ROAS, CPA.
    - **Insights at ad set level** — same fields, last 30 days.
    - **Insights at ad level** — top 50 ads by spend; same fields plus video metrics (3-sec views, ThruPlays) for video creatives.
    - **Insights with breakdowns** — placement (`publisher_platform,platform_position`), age/gender, device. Use these to spot placement losers and audience composition.
    - **Recent edit activity** — when available via `/{adsetId}` last_modified or `/{adsetId}` change history.
    
    Compute aggregates **in the script**, return summarized JSON. Don't return all rows — rank, slice, summarize. The agent narrates the result; the script does the math.
    
    Use available platform recommendations as an optional cross-check when they would help the analysis.
    
    If a read fails, follow actionable recovery guidance. Clearly report missing evidence; continue independent findings only when the available data supports them.
    
    **Skip scoring entirely if** `totalSpend == 0` or `activeCampaigns == 0`. Go straight to business context.
    
    ## Phase 2 — Scope handling
    
    If the user narrows the audit ("focus on one campaign", "campaign X", "just check creative fatigue"):
    
    - Match campaign names by case-insensitive substring. If no match, list available campaigns and ask.
    - Filter the in-memory dataset before scoring — no extra API calls.
    - Account-level dimensions (Pixel health, attribution defaults) stay account-wide. Note "Scoped to: X" in the report.
    - Skip Phase 4 (business context refresh) on scoped audits if `business-context.json` is fresh.
    
    ## Phase 3 — Score
    
    Score each of the 7 dimensions 0–5 using `references/account-health-scoring.md`. Overall = `round(sum × 100 / 35)`.
    
    | Score | Label | Meaning |
    |---|---|---|
    | 0 | Critical | Broken or missing — actively losing money |
    | 1 | Poor | Major waste or missed opportunity |
    | 2 | Needs Work | Several clear issues |
    | 3 | Acceptable | Functional, room to improve |
    | 4 | Good | Well-managed, minor opportunities |
    | 5 | Excellent | Best-practice |
    
    Scope-aware: campaign-level dimensions reflect in-scope data; account-level dimensions (Pixel + CAPI, attribution setup) score account-wide with a note on scope impact.
    
    ### Encoded heuristics — apply these, they aren't obvious
    
    - **Pixel + CAPI is upstream of everything.** EMQ < 7.0 means Meta can't match events well — Smart Bidding starves regardless of how good the creative is. STOP-condition input.
    - **Reported ROAS systematically overstates true ROAS.** Cross-check Meta-reported numbers against Shopify / GA4 / MMM where possible. The gap is the modeled-conversion premium and is typically 20–40% in ecom.
    - **Frequency × CPM trend = creative diagnosis.** Frequency > 3.0 with CPM rising ≥ 30% w/w is fatigue — recommend creative refresh, not budget cuts.
    - **One ad set carrying > 70% of a campaign is fragility, not concentration.** When it fatigues, the campaign collapses.
    - **Audience overlap > 50% between sibling ad sets fragments signal.** Consolidate; don't try to "fix" with bid caps.
    - **Special Ad Category misclassification is a takedown risk, not just a policy nit.** Surface as Critical regardless of current performance.
    - **Manual placements without evidence is a sign of inherited-from-2018 thinking.** Default should be Advantage+ Placements; deviations need data.
    
    ### Pixel + Tracking Diagnosis Matrix
    
    | | EMQ < 5 | EMQ 5–6.9 | EMQ 7.0+ |
    |---|---|---|---|
    | **CAPI off** | Critical — flying blind | Critical — most events lost | High — leaving 15–25% of events on the table |
    | **CAPI on, dedup off** | Critical — duplicated and weak signal | High — duplicate counting risk | Medium — match quality improves with dedup |
    | **CAPI on, dedup on** | High — match quality is the bottleneck | Medium — improve event_id coverage | Healthy |
    
    ## Phase 4 — Business context
    
    Derive what you can from the data already pulled:
    
    | Field | Source |
    |---|---|
    | `business_name` | Ad account name (`/{accountId}` `name` field) |
    | `services` | Top campaigns by spend, ad set names, top-converting ad creatives |
    | `locations` | Targeting geo summary (countries / regions in active ad sets) |
    | `brand_voice` | Top-performing ad copy (primary text + headline) |
    | `creative_inventory.formats` | Mix of image / video / carousel observed in active ads |
    | `creative_inventory.aspect_ratios` | Aspect ratios across active ads (1:1, 4:5, 9:16) |
    | `meta_funnel_events.conversion` | Most common optimization event on top-spending ad sets |
    | `custom_audiences` | Custom audiences referenced in active ad set targeting |
    | `pixel_health` | From the Pixel detail call |
    | `website` | Apex domain from active ad final URLs |
    
    Then crawl the website (homepage + about + 1–2 top landing pages, parallel `WebFetch`) and merge into the schema. See `references/business-context.md` for the full crawl procedure.
    
    Always ask the user: differentiators, competitors, seasonality, **AOV + profit margin** (essential for ROAS-aware scoring). Ask for everything else only if data + crawl can't answer it.
    
    ## Phase 5 — Personas
    
    Discover 2–3 personas from creative performance (which angles convert), top-spending audiences, and landing-page content — all from the dataset already in memory. Persist to `{data_dir}/meta/personas/{accountId}.json`. Each persona must be grounded in **observable evidence** (a converting ad set, a converting creative angle, a landing-page section) — no inventing. See `references/persona-discovery.md`.
    
    ## Phase 6 — Report
    
    Lead with the verdict, then the top 3 actions (with dollar impact when possible), then the scorecard, then evidence for dimensions scoring 0–2 only. Cite specific campaigns, ad sets, ads, and dollar amounts. Cap at ~80 lines.
    
    State where any audit artifacts were actually saved. Do not claim hosted audit history unless a live result confirms it.
    
    ## Guardrails
    
    1. **Read-only skill.** Diagnose; don't mutate. Every fix routes through `/meta-ads`. End the report with one handoff tied to the #1 action.
    2. **STOP condition** — if Pixel health scores 0–1 (EMQ < 5 or CAPI off in an ecom account), recommend pausing scaling decisions until tracking is fixed before recommending anything else. Everything downstream is unreliable.
    3. **Always persist** `meta/business-context.json` and `meta/personas/{accountId}.json` even if the report itself is short — downstream skills depend on them.
    4. **Name names.** Every finding cites specific campaigns, ad sets, ad creatives, and dollar amounts. "Some ad sets are underperforming" is not a finding.
    5. **Never report Meta-reported ROAS without footnoting the modeled-conversion premium.** "ROAS 3.2× (Meta-reported, 7DC1DV — typically overstates Shopify-attributed ROAS by 20–40%)" is honest. "ROAS 3.2×" is misleading.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related