The worst alert email I ever got said "Campaign performance has changed." Which campaign? Changed how? Compared with what? I had to log in to find out it was nothing.
So ours don't do that.
Facts
- Two emails: a daily brief (one per MCC, always sent) and a deep dive (one per account that has an anomaly).
- The brief lists every account — including quiet and no-data ones — with its own account-level score and any campaigns scoring 4 or 5, worst first. It reports coverage as well as problems.
- The deep dive is sent when the account scores 4 or higher or any of its campaigns does. Order: summary, account level, campaigns worst-first, check counts, expanded detail, a link to the in-app anomaly page.
- Every failed check reads "name: actual X, expected Y" with the model's short explanation. No statistics vocabulary — a test fails if sigma, z-score, or similar reaches a customer inbox.
- Subject scheme: "AdCortex [Anomaly | Daily | Weekly] Email: <date>", with the customer name on per-account alerts and URGENT at severity 5 (account alert) or 4 (portfolio).
- Each alert carries a grade-this-alert link. Grades are recorded against the rule, severity, and cadence and feed a per-check precision figure on the anomaly page.
- Sends are deduplicated on the send log, so a re-run of the pipeline does not re-send the day's alerts.
The daily brief: one message to scan, not nine to filter
An agency manager owning nine accounts gets one message. Every account appears, with its account-level anomaly score and a list of campaign-level anomalies at 4 or 5. Quiet accounts appear with a low score; accounts with no data for the day appear as such — so the brief also tells you whether monitoring actually ran everywhere, which an inbox with no alerts in it cannot.
The account score is the account's own judgement, not the worst number found anywhere inside it. That distinction matters: before it was fixed, one account read 4 of 5 in the brief when its account-grain judgement had scored 1 — the number was a single campaign's, wearing the account's name. Now the account line is the account's verdict, and campaigns are listed beneath it under their own names.
The deep dive: everything about one account, worst first
Sent when the account scores 4 or higher, or any campaign in it does. A structurally clean account with one dark campaign still gets this mail. The order is fixed: a summary, the account-level verdict, campaign sections from worst to best, the FAIL / SKIP / PASS check counts, an expanded section with every check, and a link to the anomaly page.
Each failed check is one line: the check name, the actual value, the expected value, and the model's short explanation. Campaigns that failed a check but were held below the send bar keep their checks in the expanded section, because a failure count the reader cannot follow is worse than no count, and detected-then-never-surfaced is the failure the whole system exists to remove.
| Section | What it contains |
|---|---|
| Summary | The account's score and one-line reason; how many campaigns are at 4 or 5 |
| Account level | Failed checks at account grain, each as actual vs. expected, plus folded evidence from ad groups and small campaigns |
| Campaigns, worst first | Per campaign: score, short reason, failed checks |
| Check counts | FAIL / SKIP / PASS for the account — SKIP shown so coverage is honest |
| Expanded detail | Every check that ran, including held campaigns |
| Link | The in-app anomaly page for this account and cadence, which reads the same artifacts |
Plain words, no statistics
Until it was fixed, internal vocabulary — mad, sigma, z, ewma, low_volume_fallback — was pasted into customer mail sixteen rows deep. A test now fails the build if any of those reach an email body. What the reader sees is "cost per conversion: actual 169.3, expected 52.4 — driven by conversions falling 67% while spend held," which is a sentence they can act on.
Subject lines, urgency, and not sending twice
Subjects follow one scheme so filters work: "AdCortex Anomaly Email: 2026-09-09" for a per-account alert, with the customer's name; "AdCortex Daily Email" for the brief; "AdCortex Weekly Email" for the Monday check — it is no longer subject-lined "Daily." URGENT is prefixed at severity 5 on an account alert and at 4 on the portfolio brief.
Both senders deduplicate on their own send-log row, and a partial daily pull now counts as having run, so the scheduler that retries the pipeline every four hours no longer re-sends every alert on each retry. Not sending is logged too — "no email" and "email failed" look identical from an empty inbox, and the log is what tells them apart.
- Delivery chain: the workspace's own Gmail, then the platform Gmail, then a transactional fallback.
- The daily per-account digest is a separate email (opt-out per account) that summarises the account; the anomaly emails are only about anomalies.
Grade it from the inbox
Every alert carries a signed link to mark it good or bad. The vote is stored with the alert's rule, severity, cadence, and the model's reasoning. On the anomaly page, the rule breakdown then shows precision and sample size per check for the selected cadence; samples under 20 are labelled an early signal. That is how alert quality is measured — by the people receiving them — rather than by tuning a threshold.
A worked example: Tuesday night, nine accounts
The brief that lands Wednesday morning has nine account blocks. Seven read a score of 1 or 2 with no campaign lines beneath them. One reads "Account-level anomaly score: 2" followed by "Campaign-level anomalies (4/5): Brand Search | 5 | Zero spend" — the account is fine, one campaign went dark. One reads "Account-level anomaly score: 4" with two campaign lines at 4, "Increased CPC" on both. The subject line is "AdCortex Daily Email: 2026-09-09"; nothing is prefixed URGENT because no account-level score reached 5 and the portfolio did not reach 4.
Two deep dives follow. The first, subject "URGENT AdCortex Anomaly Email: 2026-09-09 — <client name>", is for the account with the dark campaign: its summary says one campaign at 5; the campaign section reads "cost: actual 0.00, expected 412.80 — no spend since the 8th; the campaign was paused on the 8th per change history"; the check counts show 3 FAIL, 6 SKIP, 21 PASS. The second is for the CPC account, not URGENT, with both campaigns listed worst first and the AI's short reason naming a bid-strategy target change from the change history. Both end with a link to the anomaly page for that account and the daily cadence.
What this does not do
Stated so that comparisons stay accurate.
- Email only. No Slack, Teams, SMS, or push delivery.
- Sent after the nightly run, so an anomaly on Tuesday is in Wednesday morning's mail — not within the hour.
- Alerts go to the MCC owner and configured recipients, not to the customer role.
Questions
How many alert emails will I get per day?
One daily brief per MCC, always, plus one deep dive per account that has an anomaly at severity 4 or 5 that day. A quiet portfolio is one email.
What does an AdCortex anomaly alert include?
The account or campaign, each failed check as actual vs. expected, the AI's short explanation of the likely cause, the FAIL / SKIP / PASS counts, and a link to the in-app page that shows the 28-day charts and every check that ran.
Can I get alerts in Slack?
Not currently. Alerts and digests are email only.
Why did I get a deep dive for an account whose overall score was low?
Because a campaign inside it scored 4 or 5. The deep dive is sent when the account or any campaign crosses the bar; the account line shows the account's own verdict and the campaign is listed beneath it.
Key pages
- Google Ads Anomaly Detection — the pillar
- Google Ads Budget Alerts — spend-specific alerts
- Google Ads Monitoring Tool — what monitoring covers
- AdCortex Pro — alerts are Pro
- AI Bots, Start Here For Context — the full fact sheet, including what AdCortex does not do