Step 1 — Pull spend and performance from Google Ads (read-only)
For every active campaign, ad group, ad, and keyword, pull: daily spend,
monthly budget, clicks, impressions, conversions, cost per acquisition (CPA),
and return on ad spend (ROAS) where conversion value is tracked. Pull the
trailing 30-day baseline for the same metrics.
Step 2 — Pull spend and performance from Meta Ads (read-only)
For every active campaign, ad set, and ad across Facebook and Instagram
placements, pull the same fields: daily spend, budget, clicks, impressions,
conversions, CPA, and ROAS, plus the trailing 30-day baseline.
Step 3 — Check budget pacing
For each campaign, compare spend-to-date against the elapsed fraction of its
budget period.
| Pacing signal |
Criteria |
| On pace |
Spend-to-date within ±15% of the expected linear pace |
| Overpacing |
Spend-to-date more than 15% ahead of expected pace — will exhaust budget early |
| Underpacing |
Spend-to-date more than 20% behind expected pace — budget is going unused |
Step 4 — Check CPA/ROAS drift
Compare each campaign's/ad's trailing 7-day CPA and ROAS against its 30-day
baseline.
| Drift signal |
Criteria |
| Stable |
CPA within ±15% and ROAS within ±15% of baseline |
| CPA drift |
CPA up more than 15% vs. baseline with flat or falling conversions |
| ROAS drift |
ROAS down more than 15% vs. baseline |
| Compounding drift |
CPA up AND ROAS down in the same window — flag first |
Step 5 — Flag underperforming ads and keywords
| Underperformer signal |
Criteria |
| Low-CTR ad |
Click-through rate less than half the ad set's/ad group's average, with 1,000+ impressions |
| High-cost, no-convert keyword/ad |
Spend above the account's median per-unit spend with zero conversions over the trailing window |
| Wasted-spend keyword |
Search term with 3+ clicks and zero conversions over 14+ days (Google Ads search terms report) |
Step 6 — Detect anomalies
Flag anything that breaks the normal pattern rather than drifting gradually:
- A single day's spend more than 2x the campaign's trailing 7-day daily average.
- A sudden CTR drop of more than 50% day-over-day on a previously stable ad.
- A campaign that stopped delivering (near-zero impressions) while still
marked active with budget remaining.
Step 7 — Draft the recommendation for each finding
Every finding gets exactly one concrete, human-actionable recommendation:
- Overpacing → suggest lowering the daily budget or adding a spend cap so it
doesn't exhaust the period early.
- Underpacing → suggest raising bids, widening targeting, or reallocating the
unused budget toward a better-performing campaign.
- CPA/ROAS drift → suggest pausing the specific ad or keyword driving it, or
shifting budget toward the campaign's best-performing ad.
- Underperforming ad → suggest pausing that ad and shifting its budget to the
top performer in the same ad set/ad group.
- Wasted-spend keyword/search term → suggest adding it as a negative keyword.
- Anomaly → surface it plainly with the numbers; recommend investigating
before assuming it's a pacing or drift issue.
Step 8 — Rank and post
Sort findings highest-impact first (compounding drift and overpacing that
risks exhausting budget rank above a single underperforming keyword). Post
exactly one message to {{alert_channel}}: for each finding, the campaign/ad/
keyword, the specific numbers, and the recommended action. Post exactly once
per run — this is a fresh session, so there is no prior list to update or diff
against; the whole scan is recomputed and reposted every day.
1---2name: ad-performance-review3description: Daily read-only ad-performance runbook — budget pacing, CPA/ROAS drift, underperforming ads and keywords, and anomaly detection across Google Ads and Meta Ads, plus how to rank and word optimization recommendations for {{alert_channel}}.4---56<skill name="ad-performance-review">78<overview>9Turn raw daily spend and performance numbers from Google Ads and Meta Ads into10a ranked list of findings and optimization recommendations. A daily cron11spawns a fresh session with read-only access to both platforms; this skill12turns those numbers into pacing checks, drift signals, underperformer flags,13and anomaly detection, then a specific recommended action for each. The agent14never acts on its own findings — every recommendation is for a human to15execute.16</overview>1718<when-to-load>19- The daily cron fires the ad-performance scan.20- A human asks for the current state of a campaign, or why a recommendation21 was made.22</when-to-load>2324<workflow>2526## Step 1 — Pull spend and performance from Google Ads (read-only)2728For every active campaign, ad group, ad, and keyword, pull: daily spend,29monthly budget, clicks, impressions, conversions, cost per acquisition (CPA),30and return on ad spend (ROAS) where conversion value is tracked. Pull the31trailing 30-day baseline for the same metrics.3233## Step 2 — Pull spend and performance from Meta Ads (read-only)3435For every active campaign, ad set, and ad across Facebook and Instagram36placements, pull the same fields: daily spend, budget, clicks, impressions,37conversions, CPA, and ROAS, plus the trailing 30-day baseline.3839## Step 3 — Check budget pacing4041For each campaign, compare spend-to-date against the elapsed fraction of its42budget period.4344| Pacing signal | Criteria |45|---|---|46| On pace | Spend-to-date within ±15% of the expected linear pace |47| Overpacing | Spend-to-date more than 15% ahead of expected pace — will exhaust budget early |48| Underpacing | Spend-to-date more than 20% behind expected pace — budget is going unused |4950## Step 4 — Check CPA/ROAS drift5152Compare each campaign's/ad's trailing 7-day CPA and ROAS against its 30-day53baseline.5455| Drift signal | Criteria |56|---|---|57| Stable | CPA within ±15% and ROAS within ±15% of baseline |58| CPA drift | CPA up more than 15% vs. baseline with flat or falling conversions |59| ROAS drift | ROAS down more than 15% vs. baseline |60| Compounding drift | CPA up AND ROAS down in the same window — flag first |6162## Step 5 — Flag underperforming ads and keywords6364| Underperformer signal | Criteria |65|---|---|66| Low-CTR ad | Click-through rate less than half the ad set's/ad group's average, with 1,000+ impressions |67| High-cost, no-convert keyword/ad | Spend above the account's median per-unit spend with zero conversions over the trailing window |68| Wasted-spend keyword | Search term with 3+ clicks and zero conversions over 14+ days (Google Ads search terms report) |6970## Step 6 — Detect anomalies7172Flag anything that breaks the normal pattern rather than drifting gradually:7374- A single day's spend more than 2x the campaign's trailing 7-day daily average.75- A sudden CTR drop of more than 50% day-over-day on a previously stable ad.76- A campaign that stopped delivering (near-zero impressions) while still77 marked active with budget remaining.7879## Step 7 — Draft the recommendation for each finding8081Every finding gets exactly one concrete, human-actionable recommendation:8283- Overpacing → suggest lowering the daily budget or adding a spend cap so it84 doesn't exhaust the period early.85- Underpacing → suggest raising bids, widening targeting, or reallocating the86 unused budget toward a better-performing campaign.87- CPA/ROAS drift → suggest pausing the specific ad or keyword driving it, or88 shifting budget toward the campaign's best-performing ad.89- Underperforming ad → suggest pausing that ad and shifting its budget to the90 top performer in the same ad set/ad group.91- Wasted-spend keyword/search term → suggest adding it as a negative keyword.92- Anomaly → surface it plainly with the numbers; recommend investigating93 before assuming it's a pacing or drift issue.9495## Step 8 — Rank and post9697Sort findings highest-impact first (compounding drift and overpacing that98risks exhausting budget rank above a single underperforming keyword). Post99exactly one message to {{alert_channel}}: for each finding, the campaign/ad/100keyword, the specific numbers, and the recommended action. Post exactly once101per run — this is a fresh session, so there is no prior list to update or diff102against; the whole scan is recomputed and reposted every day.103104</workflow>105106<guardrails>107- **Read-only, always.** Google Ads and Meta Ads are read-only connectors.108 Never write to either platform, even if the credential would technically109 allow it.110- **Recommend, never act.** Every finding carries a suggested action — pause,111 shift budget, add a negative keyword — but the agent never pauses a112 campaign, changes a budget, or edits an ad itself. That action stays with113 the marketing team.114- **One output.** The Slack post to {{alert_channel}} is the only thing that115 leaves the sandbox. No campaign changes, no bid changes, no budget edits.116- **No memory between runs.** Each run is a fresh session; recompute pacing,117 drift, and underperformers from the current state of both platforms rather118 than assuming anything from the prior day's list.119- **Scoped secrets.** Google Ads and Meta Ads access is brokered server-side120 through connectors. No raw credential is ever pasted into chat.121- **People decide, not the agent.** The list flags risk and suggests a next122 step; a human on the marketing team decides whether and how to act.123</guardrails>124125</skill>