MGID Performance Diagnostics

Use when performance moved and nobody knows why — e.g. "why did CPA jump yesterday", "spend dropped", "conversions fell off a cliff", "what changed", "CTR is down since Friday".

MGID Performance Diagnostics

Explains a metric move. Retrospective and read-only: it answers what changed and why, then hands off. It does not decide what to do — that is the simulator's and the signal skills' job.

Tools: tool reference Assumptions: threshold assumptions


Required inputs

Input Required Default
Symptom metric Yes ask: CPA, spend, conversions, CTR, or "something feels off"
Scope No whole account
Break window No last 3 days vs the 7 days before

The user rarely knows the exact day. Find it rather than asking twice.


Order of elimination

Work top-down and stop at the first layer that explains the move. Each layer is cheaper and more common than the one below it.

# Layer Question Evidence
0 Tracking Did conversions stop being recorded? mgid-tracking-health-monitor
1 Own changes Did we change something? ask the user; changes are not readable through the tools
2 Composition Did the mix shift between campaigns? get_portfolio_statistics, two windows
3 Delivery Did one campaign's volume or price move? get_campaign_statistics daily rows
4 Placement Did the traffic mix inside a campaign move? get_selective_bidding_statistics
5 Creative Did a teaser die or a new one take over? run_custom_report / get_teaser_statistics
6 Unexplained None of the above say so plainly

Layer 0 first, always. A broken postback looks exactly like a CPA catastrophe, and every action taken on it makes things worse.

Layer 1 costs a question, not a call. No tool reads back what was changed, so ask the user what moved recently — bids, filters, budgets, creatives — and correlate the answer with when the metric broke. Changes made in the MGID dashboard are equally invisible, so never rule a change out: if the break is real and nobody remembers touching anything, say a dashboard change cannot be excluded.


Workflow

Phase 0 — Gate

Run mgid-tracking-health-monitor on the scope. On fail: report the tracking break, its start, the spend at risk, and stop. Do not diagnose CPA on untrustworthy conversion data.

Phase 1 — Locate the break

get_portfolio_statistics(dateInterval: interval, startDate, endDate)  // symptom window
get_portfolio_statistics(dateInterval: interval, startDate, endDate)  // baseline window

Two calls for the whole account. Compute per campaign: Δspend, Δclicks, Δavcpc, Δconversions, ΔCPA, Δprofit — absolute and as a share of the account delta.

Rank campaigns by contribution to the account-level move, not by their own percentage change: a 300% CPA rise on $4 of spend is noise, a 20% rise on $4,000 is the story.

Phase 2 — Attribute

For the top contributors (cap at 5), pull daily rows:

get_campaign_statistics(campaignId, dateFrom, dateTo)   // symptom + baseline in one range

Decompose the CPA move — this is the part a dashboard cannot do:

CPA = spend / conversions = (clicks × avgCPC) / (clicks × CVR) = avgCPC / CVR

so

  • avgCPC up, CVR flat → an auction/price story → Layer 4
  • CVR down, avgCPC flat → a traffic-quality or landing-page story → Layer 4/5
  • both moved → report both contributions, largest first
  • conversions flat, spend up → a pacing or limit change → Layer 1/3
  • clicks collapsed, rates flat → delivery: budget exhausted, campaign paused, moderation rejection, or lost auction share

State the split numerically ("CPA +38%: +31pp from CPC, +7pp from CVR"), never as an adjective.

Phase 3 — Localise inside the campaign

get_selective_bidding_statistics(campaignId, entity, symptom window)
get_selective_bidding_statistics(campaignId, entity, baseline window)

Entity per the campaign-type table. Look for new entities carrying material spend in the symptom window and absent from the baseline — a newly opened source is the single most common cause of an overnight CPA jump.

Creative layer only if placement does not explain it: a teaser that stopped serving (moderation rejection, exhausted, paused) or a new teaser that took the share. list_teasers carries the rejection reason.

Phase 4 — Report


Output

Chat, in this order:

  1. One-sentence verdict. "CPA rose 38% because source X took 22% of spend at 4× the account CPA, starting Tuesday."
  2. The break date, with the numbers on either side of it.
  3. Contribution table — campaign, Δ, share of the account move.
  4. The decomposition — CPC vs CVR, in points.
  5. What was ruled out, explicitly: tracking, our own changes, composition.
  6. Confidence, plus what would raise it (a longer window, the customReports grant, a dashboard change history the API cannot see).

Then hand off: "mgid-source-auditor would price or block the sources involved" — do not do it here.


Guardrails

  • Read-only skill. It recommends no writes and calls no write tool.
  • Never report a cause without the numbers that support it.
  • Never attribute to a change in the audit log without matching the timestamp to the break; correlation must at least be in the right order.
  • Fewer than 50 conversions in either window (ASSUMPTION) → label the CPA move as possibly statistical noise and say so before anything else.
  • Same-day and yesterday-only windows are unreliable — stats settle. Prefer complete days, and name the window in the output.
  • If nothing explains it, say "unexplained" and list what was checked. An invented cause is worse than an open question.