← Windsor.aiCONTENT HISTORY
WHAT CHANGED
Update to Windsor.ai
Snapshot Nov 10, 2026 · 06:08 UTC · version 4.1.0
Collection source: downloaded plugin package.
First saved snapshot
No earlier snapshot is available to establish a change.
Save Windsor.ai and check future changes in Following.
Up to 3 plugins free · no card required.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"description": "Find ad spend that does not convert across Google Ads, Meta Ads, TikTok Ads, LinkedIn Ads, Microsoft Ads and Amazon Ads through Windsor.ai: zero-conversion campaigns and ad sets, conversions far above the account's average cost, and Google Ads search terms to add as negative keywords. Use when the user asks where budget is wasted, how to cut spend, how to improve ROAS, or to optimize campaigns. Proposes fixes and applies only the ones the user approves.",
"included_files": [
{
"relative_path": "agents/openai.yaml",
"size_in_bytes": 370
}
],
"name": "wasted-spend-audit",
"skill_md_contents": "---\nname: wasted-spend-audit\ndescription: \"Find ad spend that does not convert across Google Ads, Meta Ads, TikTok Ads, LinkedIn Ads, Microsoft Ads and Amazon Ads through Windsor.ai: zero-conversion campaigns and ad sets, conversions far above the account's average cost, and Google Ads search terms to add as negative keywords. Use when the user asks where budget is wasted, how to cut spend, how to improve ROAS, or to optimize campaigns. Proposes fixes and applies only the ones the user approves.\"\n---\n\nExplicit instructions from the user take priority over this skill.\n\n# Optimize ad spend\n\nFind spend that is not paying off, show the evidence, and apply the fixes the user approves.\n\n## 1. Scope\n- Call `get_connectors` and list the connected ad accounts. Ask which accounts to include if there are several; default to all.\n- Use the last 30 days unless the user gives a range. State the currency per account and never sum across currencies.\n\n## 2. Find waste, per platform\nFor each ad connector, read field ids from `get_fields` first, then call `get_data` with campaign (and ad set or ad group) name and id, spend, clicks, impressions and conversions. Conversions differ per platform: Microsoft Ads uses `conversions_qualified` (its `conversions` field is deprecated and returns 0); LinkedIn `conversions` counts post-view conversions only, so use `externalwebsiteconversions`, plus `oneclickleads` for Lead Gen forms; Meta has no plain `conversions` field, so pick the `actions_*` event that matches the campaign objective (for purchases, `actions_omni_purchase` with `action_values_omni_purchase`). Check get_fields before treating any campaign as zero-conversion.\n- **Zero-conversion spend:** items with spend above zero and no conversions, sorted by spend.\n- **Expensive conversions:** items whose cost per conversion (spend / conversions) is more than twice the account average; show both numbers.\n- **Search terms (Google Ads):** follow the recipe below for the negative keyword flow.\n- Ignore items with too little data to judge (for example under 1,000 impressions) and say so.\n\n## 3. Propose\nPresent one table per account: item, spend, conversions, cost per conversion, suggested action (pause, lower budget by an absolute amount, or add negatives), and the expected saving. Never propose touching brand campaigns or items the user marked as intentional.\n\n## 4. Apply only what is approved\n- Wait for an explicit yes to the exact list. Partial approval is fine.\n- Call `list_actions` for each connector and build params from the returned schema (`pause_campaign`, `pause_adset`, `set_campaign_budget` and similar; the live set is what `list_actions` returns). Budget units differ per platform: Google Ads takes micros (50.00 = 50000000), Meta takes cents (50.00 = 5000; whole units for currencies without cents such as JPY), LinkedIn, Microsoft Ads and Amazon Ads take the normal amount (50.00 = 50), and TikTok takes whole numbers (50). Convert the user's amount and show both values in the confirmation.\n- Call `execute_action` once per approved change and report what changed, before and after, and anything the platform rejected.\n- If write actions are not enabled for the account, send the user to https://onboard.windsor.ai/app/settings/account and retry after they confirm.\n\n## Hand-offs\n- Reporting only -> the marketing-performance-review skill\n- Creating replacement campaigns or ads -> the campaign-launch skill\n\n## Recipe: Google Ads search terms\n\nReview Google Ads search terms from the last 30 days, list the ones that spend without converting, and add the ones you approve as negative keywords.\n\n### Instructions\n\n1. Call `get_connectors` and pick the user's `google_ads` account. If there are several, ask which one.\n2. Call `get_data` on `google_ads` with `[\"campaign\", \"campaign_id\", \"ad_group_name\", \"ad_group_id\", \"search_term\", \"spend\", \"clicks\", \"impressions\", \"conversions\"]` and `date_preset: \"last_30d\"`. Google Ads cannot return `search_term` together with keyword fields, so read keywords in a separate call; compute cost per conversion as spend divided by conversions.\n3. In a separate call, get the account's keywords with `[\"campaign_id\", \"ad_group_id\", \"keyword_text\", \"keyword_match_type\", \"spend\"]` and the same date range.\n4. Skip rows with no search term (Performance Max campaigns return spend without search terms, and this action does not add negatives to them). Group the rest by search term and list the terms with spend above zero and zero conversions, sorted by spend descending. Show campaign, ad group, spend, clicks and impressions. Separately, list converting terms whose cost per conversion is more than twice the account average.\n5. Suggest which non-converting terms to add as negatives, and at which level (campaign or ad group). Do not suggest terms that contain the brand name, that match one of the keywords from step 3, or that are the target of a competitor or category ad group the user runs on purpose.\n6. Stop and wait for the user to approve the list. Never add negatives without an explicit yes.\n7. After approval, call `list_actions` for `google_ads` and read the schema of `push_negative_keywords`. Call `execute_action` once per target: `level: \"campaign\"` with `campaign_id`, or `level: \"ad_group\"` with `ad_group_id`, and set each term's match type (EXACT unless the user chose otherwise).\n8. Report what was added, where, and anything the API rejected.\n"
}SHA-256 of public snapshot: 5916e1a9cf6ac0e632d9e67656a9d65c56cecba3b91c1ce0ed61411565b8930d