MGID Creative Fatigue Detector

Use when MGID teasers show fatigue — e.g. "creative fatigue", "CTR dropped", "pause underperforming ads", "which teasers to refresh", "tired creatives".

MGID Creative Fatigue Detector

Detects teaser fatigue via period-over-period stats. Rules live here.

Tools: tool reference
Assumptions: threshold assumptions


Required inputs

Input Required Default
Campaign ID(s) Yes simulator scope list, user ids, or named campaigns when run standalone
Current period Yes last 7 days
Baseline period Yes prior 7 days (days 8–14)
Target CPA No from campaign stats or user

Campaign type gate

Only content, product and push campaigns have teasers. Skip search_feed and newsletter and say so in the output — see the campaign-type table in tool reference. On push, widget-level signals are absent, so rely on teaser-level CTR/CPA only.

Refresh recommendations name what to replace, never the replacement copy. New headlines and images come from MGID's own creative generation, not from the agent.


MCP tools

Tool Purpose
run_custom_report Primary — all teasers + metrics in one call per campaign (requires customReports grant)
list_teasers Active teaser count + status guardrail (no metrics)
get_teaser Config, moderation status
get_teaser_statistics Fallback — per teaser when custom report unavailable
get_campaign_statistics Campaign avg CTR/CPA for signal #2 and #5

Bulk stats via custom report

One run_custom_report per campaign beats N× get_teaser_statistics. Use dimension teaserId, filter by filters.campaigns, and the same date windows as other signal skills.

ads-mcp implementation: run_custom_report calls the v2 statistics reports endpoint, which accepts filters.dateRange only — no dateRangePrevious. There is no comparison mode through ads-mcp today.

Period-over-period: run two reports per campaign — current window and baseline window — and join rows on teaserId. Still 2 calls/campaign, not one per teaser.

Example params (current 7d, top spenders first):

{
  "dimensions": ["teaserId"],
  "metrics": ["clicks", "spent", "ctr", "conversionsBuy", "conversionsCostBuy"],
  "filters": {
    "campaigns": [12061865],
    "dateRange": {
      "dateFrom": "2026-06-30T00:00:00.000+03:00",
      "dateTo": "2026-07-06T23:59:59.999+03:00"
    }
  },
  "orders": { "spent": "desc" },
  "limit": 100
}

Repeat with dateRange set to the prior 7d for baseline metrics.

Paginate with offset when meta.total > limit. Skip teasers with zero current spend unless list_teasers shows them as active and recently launched.

Grant missing: fall back to get_teaser_statistics per teaser; cap at 20 teasers (prior-spend order from a single-period custom report is unavailable — use list_teasers + campaign stats to pick candidates).


Minimum data (ASSUMPTION)

Per teaser, in current period:

  • clicks ≥ 100, OR
  • impressions ≥ 1,000 (if returned by statistics tool)

Skip non-active teasers. Count active teasers — never pause if only one remains.


Fatigue signals (ASSUMPTION)

Compare current 7d vs prior 7d only (no day-over-day — too noisy on native):

  1. CTR drop ≥ 30% with min clicks
  2. CPA increase ≥ 50% vs campaign avg with ≥ 5 conversions
  3. CVR drop ≥ 35% vs baseline with ≥ 200 clicks
  4. Spend ≥ $30, conversionsBuy = 0, clicks ≥ 150
  5. Teaser CPA > 2× best active teaser (both ≥ 5 conversions)

Recommendations

  • pause_teaser when fatigue + ≥ 2 other active teasers
  • launch_teaser_variant — guidance only; no create/moderation MCP tools yet
  • Rejected teasers: explain via get_teaser; user fixes in UI — no send_to_moderation tool

Impact (ASSUMPTION)

spend_removed = teaser_spend_7d / 7
recovery_rate = 0.3 to 0.7  (sibling absorption)
net_spend_delta = -spend_removed × (1 - recovery_rate)

Apply

set_teaser_status (write scope).


Output

Chat: table of teasers to pause/monitor.

Return: Recommendation[].