HIP Creative · Paid Media

AI Quality Checks Spec

Every recurring check bucketed onto a cadence spine, split by team, now with a build-readiness overlay: a Today? column (can we run it as things stand) and Viktor's notes on data access and gaps. Each check is also tagged Binary (automated pass/fail) or Recommendation (human judgment → task).

Version Draft v3 · +build-readiness
Date Jul 21, 2026
Design ION (light)
Feasibility per Viktor (Slack)
Binary Auto pass/fail.
Recommendation Human judgment.
Yes Buildable today.
Partial Some accounts / a proxy only.
No Blocked on access.

Today? reflects current data access: Meta API (20 connected accounts) + pb_data BigQuery (~240 Meta / all Google accounts), Google Ads API (full MCC, 1,098 accounts), ClickUp, and browser. No access yet: GHL / PracticeBeacon. Values in [brackets] are thresholds to confirm.

Viktor's read — ~85–90% buildable today. The Google Ads API connection covers the whole MCC (all 1,098 accounts, proven on the suspension check) and pb_data BQ has deep detail (RSA copy, search terms, lost IS, conversions). Jobs 1–3 + wrapper are essentially all green. The gaps: the GHL/PracticeBeacon dependency (lead recon, and the starts/schedules/ROAS behind most of Jobs 4–5), plus a few API blind spots (auction insights, ad extensions/PMax copy). #1 unlock: PracticeBeacon / GHL access.

The cadence spine — 5 jobs + wrapper

Daily
1 · Delivery & Money Sweep
Binary · Delivery rotation
2× / week
2 · Creative & Content QA
Mixed · Account specialist
Weekly
3 · Config & Targeting Integrity
Binary · Specialist + Sawyer
Weekly
4 · Performance → Sawyer
Recommendation · Team-lowest
Biweekly
5 · Specialist "Unders" Prep
Recommendation · Specialist
Every cycle
⊚ Coverage Wrapper
Binary · System guardrail
1

Delivery & Money Sweep

Daily
Type: Binary → morning digest
Owner: Delivery rotation
Data: Google Ads API · pb_data BQ · ClickUp · GHL / PracticeBeacon
Money & delivery and measurement integrity run in one daily pass. Section A is fully buildable now; the lead-recon rows in Section B wait on PracticeBeacon/GHL.
CheckTypeToday?Fail conditionData sourceOn failViktor's notes
Section A — Money & delivery
Payment method downBinYesFail if account billing status = declined / card failure.Ads API — billing setupAlert + taskProven yesterday — billing flags across both MCCs.
Account suspended / limitedBinYesFail if serving status = suspended / under review / limited.Ads API — customer statusAlert + taskProven yesterday (suspension check).
Budget pacing off-trackBinYesFail if projected month-end spend deviates from plan by [±10%].MTD cost vs. MX budgetAlert + auto taskBQ MTD cost vs. ClickUp MX budget.
Pacing about to overspendBinYesFail if run-rate projects ≥100% of budget before period end.Run-rate vs. remainingAlert; task if not pausedFlag-only; references your existing auto-pause automation.
Not spending 2+ daysBinYesFail if daily cost = $0 for ≥2 consecutive days on a live campaign.Ads API / BQ — daily costAlert + taskBQ daily spend.
HIP / active naming spend ruleBinYesFail if spend > $0 in a campaign named HIP while the account name isn't active.Campaign + account name + costAlert + taskName + cost logic on BQ / API.
Spend anomalyBinYesFail if today's spend deviates from trailing [7–14d] average by [±50%].Daily cost vs. baselineAlertBQ trailing baseline.
Zero-conversion spendBinYesFail if spent ≥ [$X] over [N] days with 0 conversions.Cost + conversionsAlert + taskBQ cost + conversions.
Zero serving adsBinYesFail if a live ad group has 0 eligible/serving ads.Ads API — ad_group_ad statusAlert + taskGAQL ad_group_ad status.
Section B — Measurement integrity
Conversion heartbeatBinYesFail if a primary conversion action shows no recent conversions > [N] days.Ads API / BQ — conversion_actionAlert + taskConversion actions present in API + BQ.
Conversion firing (absence)BinYesFail if today's conversions = 0 where trailing average > 0.Conversions vs. baselineAlertBQ conversions vs. baseline.
Lead reconciliationBinNoFail if Google-reported conversions ≠ GHL/PB leads beyond [±tol].Ads API vs. GHL / PBAlert + taskBlocked — no GHL/PracticeBeacon access today. #1 unlock.
MLs + $0 opportunitiesBinNoFail if manual leads / opps are logged $0 where value is expected.PracticeBeacon / GHLAlertBlocked — PracticeBeacon/GHL access needed.
2

Creative & Content QA

2× / week
Type: Mixed → per-account QA report
Owner: Account specialist
Data: RSA text · LP HTML · ClickUp · website
All green — same pipeline as the July RSA sweep. The one disclosed blind spot: no data source for ad extensions/assets (sitelinks, callouts, promos) or PMax asset copy — where the Vibrant typo hid.
CheckTypeToday?Fail / flag conditionData sourceOn failViktor's notes
RSA spellingBinYesFail on spelling error in a live headline/description.RSA assetsTaskProven (July sweep). Blind spot: ad extensions/assets + PMax asset copy have no data source.
LP spelling (desktop + mobile)BinYesFail on spelling error on the rendered LP, both viewports.LP HTML — dual renderTaskBrowser render, both viewports.
TruncationBinYesFail if headline > 30 / description > 90 chars, or visibly cut.RSA asset lengthsTaskChar-length rules on RSA copy.
Broken final URLBinYesFail on non-200 / redirect / soft-404.HTTP check on live URLsTaskHTTP checks on final URLs.
Offer / theme matchBinYesFail if live offer in copy/LP ≠ current offer subtask / notes.Ad + LP vs. ClickUpTaskCopy/LP vs. ClickUp offer subtask + notes.
Address / location accuracyBinYesFail if # addresses in copy/LP ≠ # locations in ClickUp / assets.Copy + ClickUp + websiteTaskCopy + ClickUp + website counts.
Images match offerRecYesFlag if image assets don't reflect the current offer/theme.Image assets vs. offerReview flagFull-size images reviewed visually.
LP mobile vs. desktop parityRecYesFlag if key content differs between mobile and desktop.Dual render diffReview flagDual render diff.
GrammarRecYesFlag likely grammar issues in live copy/LP.Ad + LPReview flagSame copy pipeline as spelling.
3

Config & Targeting Integrity

Weekly
Type: Binary → reconciliation report
Owner: Specialist, spot-checked by Sawyer
Data: Ads API settings + change history vs. MX · recommendations
Live setup vs. declared plan (MX). Nearly all green; two rows carry API caveats (30-day change-history window, and some auto-asset toggles not API-exposed).
CheckTypeToday?Fail conditionData sourceOn failViktor's notes
Geo presence settingBinYesFail if location option = "Presence or interest" (should be Presence only).geo_target_type_settingTaskgeo_target_type_setting.
Geo / radius matchBinYesFail if targeted geo/radius ≠ MX service area.Location criteria vs. MXTaskLocation criteria vs. ClickUp MX.
Network settingsBinYesFail if Search campaign has Display expansion / Search Partners on when MX says off.campaign.network_settingsTaskDirectly queryable.
Bid strategy matchBinPartialFail if bid strategy / tCPA / tROAS ≠ MX or changed from baseline.Bidding config + change historyTaskConfig match ✅; change-from-baseline limited to Google's hard 30-day change-history API lookback.
Missing negative listBinYesFail if the required shared negative list isn't attached.Campaign shared setsTaskCampaign shared sets.
Conflicting negativesBinYesFail if conflicting-negatives % > 0%.Recommendations / computedAuto-remediateCompute conflicts directly (negatives vs. serving terms); auto-remediation = draft → your approval.
Conversion setup integrityBinYesFail if > 1 primary risks double-count, or include-in-Conversions misconfigured.conversion_action configTaskPrimary / include flags.
Automation guardrailBinPartialFail if auto-apply recs on, auto-created assets on (unwanted), or a new auto-feature switched on.Account settings + change historyTask; auto-off where safeAuto-apply recs subscription queryable; some auto-created-asset toggles aren't API-exposed.
Naming ↔ config reconciliationBinYesParse name tokens; validate each against actual settings.Names vs. live settingsTaskFull umbrella supported.
4

Performance Flags → Sawyer Tasks

Weekly
Type: Recommendation → tasks (team-lowest)
Owner: System → tasks to Sawyer
Data: Ads metrics · PracticeBeacon · ClickUp
Ads-side triggers are green; the starts/schedules/ROAS triggers depend on PracticeBeacon (AF starts is a usable proxy).
CheckTypeToday?Flag conditionExclusionsOn flagViktor's notes
No starts L90RecPartialFlag accounts with 0 starts in the last 90 days.ROAS exc. + AGL within 30Task → SawyerAF has starts — usable proxy; PB is the spec's source of truth.
No schedules L30RecPartialFlag accounts with 0 schedules in the last 30 days.(MX seriousness layer)Task → SawyerSame — AF proxy only.
Search CPL > $200 L60RecYesFlag accounts with search CPL > $200 over 60 days.Exclude AGL < 60Task → SawyerBQ.
Budget-limited / Lost IS (budget)RecYesFlag strong campaigns capped by budget.search_budget_lost_impression_shareTask / reviewBQ has the column.
Lost IS (rank)RecYesFlag high lost impression share due to rank.search_rank_lost_impression_shareReviewBQ.
Ad strength poorRecYesFlag ad groups with "poor" ad strength.Ads API — ad_strengthReviewBQ RSA table.
Search-term wasted spendRecYesFlag spend on junk queries above [$X]; suggest negatives.Ads API — search_term_viewReview + negativesBQ search terms w/ cost+conv — strongest check in the job.
Brand drift + brand defenseRecPartialFlag brand spend-share drift, or competitor on brand terms.Brand campaigns + auction insightsReviewSpend-share drift ✅; competitor-on-brand needs auction insights — not in API or BQ, UI-only.
5

Specialist "Unders" Prep

Biweekly
Type: Recommendation → specialist task → Sawyer subtask
Owner: Specialist, reviewed by Sawyer
Data: Ads metrics · PracticeBeacon
Underpacers is green; schedule-rate and ROAS measures all sit behind PracticeBeacon revenue/schedules data.
CheckTypeToday?Flag conditionData sourceOn flagViktor's notes
UnderpacersRecYesAccounts pacing below plan for the period.Cost vs. MX budgetPrep itemBQ cost vs. MX.
Low schedule rateRecNoAccounts below [schedule-rate threshold].Schedules / leads (PB)Prep itemPracticeBeacon schedules data.
Low ROASRecNoAccounts below [ROAS threshold].Revenue / cost (PB)Prep itemPracticeBeacon revenue.
0 ROASRecNoAccounts returning zero over the window.Revenue / cost (PB)Prep itemPracticeBeacon revenue.

Coverage Wrapper

Every cycle
Type: Binary → system guardrail
Owner: System
Data: ClickUp active list vs. run manifest
CheckTypeToday?Fail conditionData sourceOn failViktor's notes
Account coverageBinYesFail if any account marked "active" in ClickUp is absent from the checked set.ClickUp active list vs. run manifestAlert to leadStraightforward — ClickUp active list vs. run manifest.

Deliberately NOT new jobs — defer to existing alerts

These already fire in real time. The system references them; it does not rebuild them.
Existing signalWhere it livesNote
Verifications neededSlack alertIf yes, it already names them.
URLs down (hard down)Existing alertReal-time catches hard-downs; Creative QA adds the deeper "wrong URL that resolves" check.
Pacing overage → auto-pauseExisting automationJob 1 references it; only tasks when it didn't act.

Glossary & thresholds to confirm

MX — the media plan / declared spec each check reconciles against.

AGL — account go-live date; drives the "new account" exclusions.

PB (PracticeBeacon) — source of truth for leads, starts, schedules, ROAS. No access yet.

AF — Agency Framework; has starts as a usable proxy while PB is unavailable.

BQ (pb_data) — BigQuery warehouse: cost, conversions, RSA copy, search terms, lost IS.

Thresholds to lock: pacing ±%, anomaly ±%, zero-conv $/days, CPL ceiling, ROAS floors.

Viktor's read — ~80% buildable today. Meta API (your connection) covers 20 accounts (ION + 19 partners) with full settings depth; pb_data BQ covers ~240 Meta accounts for daily spend/results/creative copy/targeting (how the typo sweep hit every account). All of Job 1's spend/pacing, most of Job 2, Job 3 config reconciliation, and the wrapper — yes. Blockers: GHL/PracticeBeacon lead reconciliation (no access) and instant-form contents + pixel stats (Meta token scope — needs pages_manage_ads + pixel permissions).

Meta is the simpler build — 3 jobs + wrapper. No search terms, negatives, network settings, or impression share; no team-lowest / specialist-unders layer; pixel checks scoped to rare web/LP campaigns; disclaimer and Advantage+ guardrail intentionally out.

The cadence spine — 3 jobs + wrapper

Daily
1 · Delivery & Money Sweep
Binary · Delivery rotation
2× / week
2 · Creative & Content QA
Mixed · Account specialist
Weekly
3 · Config & Targeting Integrity
Mixed · Specialist + lead
Every cycle
⊚ Coverage Wrapper
Binary · System guardrail
1

Delivery & Money Sweep

Daily
Type: Binary → morning digest
Owner: Paid Social delivery rotation
Data: Meta API (20 accts) · pb_data BQ (~240) · ClickUp · GHL
Spend/pacing all green fleet-wide via BQ. Disapprovals + delivery are API-only (20 accounts) until the token is broadened; lead recon waits on PracticeBeacon/GHL. Measurement is scoped to web/LP campaigns only.
CheckTypeToday?Fail conditionData sourceOn failViktor's notes
Section A — Money & delivery
Payment method downBinPartialFail if the ad account funding source is declined / billing failed.Meta API — fundingAlert + taskaccount_status (disabled/unsettled) via API + BQ list = yes; granular funding-source detail sometimes permission-blocked.
Ad account restricted / in reviewBinYesFail if account_status = disabled / unsettled / in-review or restricted.Meta API — account_statusAlert + taskaccount_status / disable_reason.
Budget pacing off-trackBinYesFail if projected spend deviates from plan by [±10%].MTD spend vs. MX budgetAlert + auto taskMTD spend (BQ, all accounts) vs. MX budget on ClickUp — same plumbing as the weekly MX draft.
Not spending 2+ daysBinYesFail if spend = $0 for ≥2 consecutive days on a live ad set/campaign.Meta API / BQ — daily spendAlert + taskBQ daily spend, all accounts.
Spend anomalyBinYesFail if today's spend deviates from trailing [7–14d] average by [±50%].Daily spend vs. baselineAlertBQ trailing baseline.
Zero-result spendBinYesFail if spent ≥ [$X] over [N] days with 0 results.Spend + resultsAlert + taskBQ spend + lead actions.
Ad / creative disapprovalsBinPartialFail if any ad effective_status = DISAPPROVED / WITH_ISSUES.Meta API — ad reviewAlerteffective_status via API on the 20 connected accounts; BQ has no review status — full-fleet needs a broader token.
Zero delivering adsBinPartialFail if a live ad set has 0 delivering ads.Meta API — effective_statusAlert + taskSame — API-only, 20 accounts.
Section B — Measurement integrity · web / LP campaigns only
Pixel / CAPI heartbeatBinPartialFail if a web/LP campaign's pixel/CAPI shows no events > [N] days. Skipped for instant-form-only accounts.Meta pixel / dataset statsAlert + taskPixel stats endpoint is permission-blocked on the current token; can proxy via "tracked conversions went to 0" from BQ.
Conversion firing (absence)BinPartialFail if a web/LP campaign's tracked conversions = 0 where trailing average > 0.Conversions vs. baselineAlertSame as pixel heartbeat — BQ proxy works; native pixel stats permission-blocked.
Lead reconciliationBinNoFail if Meta-reported leads ≠ GHL leads beyond [±tol]. Applies to instant forms too.Meta leads vs. GHLAlert + taskBlocked — no GHL/PracticeBeacon access today. #1 unlock.
MLs + $0 opportunitiesBinNoFail if manual leads / opps are logged $0 where value is expected.PracticeBeacon / GHLAlertBlocked — PracticeBeacon/GHL access needed.
2

Creative & Content QA

2× / week
Type: Mixed → per-account QA report
Owner: Account specialist
Data: Ad copy + creative · LP HTML or instant-form def · ClickUp · website
Copy QA proven at fleet scale (July sweep). The instant-form pieces (form contents, form privacy links) need the pages_manage_ads scope; everything LP- and copy-based is green.
CheckTypeToday?Fail / flag conditionData sourceOn failViktor's notes
Ad copy spellingBinYesFail on spelling error in live primary text, headline, or description.Ad creativeTaskProven — the July sweep, incl. DC variants via creative breakdown tables.
LP / instant-form spellingBinPartialFail on spelling error on the LP (both viewports) for web ads, or across the instant form for lead ads.LP HTML / form definitionTaskLP spelling ✅ (dual-viewport render); instant-form spelling ❌ — list_lead_forms needs pages_manage_ads.
TruncationBinPartialFail if primary text or headline is cut off in-feed on key placements.Ad creative + placement previewTaskChar-length rules yes; true in-feed placement previews no.
Broken linkBinYesFail on non-200/redirect final URL; for lead ads, broken form privacy or post-submit link.HTTP checkTaskHTTP checks on final URLs (form privacy links blocked by the same form-read gap).
Offer / theme matchBinYesFail if live offer in copy / LP / form ≠ current offer subtask / notes.Ad + LP/form vs. ClickUpTaskCopy/LP vs. offer subtask + notes.
Address / location accuracyBinYesFail if # addresses in copy ≠ # locations in ClickUp / website.Copy + ClickUp + websiteTaskCopy + ClickUp + website.
Creative matches offerRecYesFlag if the image/video doesn't reflect the current offer/theme.Creative vs. offerReview flagFull-size images pull from BQ URLs; reviewed visually.
LP mobile vs. desktop parityRecYesFlag if key content differs between mobile and desktop (web ads only).Dual render diffReview flagDual render diff.
GrammarRecYesFlag likely grammar issues in live copy / LP / form.Ad + LP/formReview flagSame copy pipeline as spelling.
3

Config & Targeting Integrity

Weekly
Type: Mixed → reconciliation + health flags
Owner: Specialist, spot-checked by lead
Data: Meta API settings (20 accts) · BQ targeting (fleet) vs. MX
Full depth on the 20 API-connected accounts. Fleet-wide, BQ's targeting table covers geo / placements / optimization, but not objective, budget type, or destination — so those read Partial until settings depth extends beyond the 20.
CheckTypeToday?Fail / flag conditionData sourceOn failViktor's notes
Section A — Config reconciliation (name ↔ setup)
Objective matches nameBinPartialFail if campaign objective ≠ the name token.campaign.objectiveTask✅ on the 20 API-connected accounts; objective isn't in the fleet BQ targeting table.
Budget type (CBO / ABO)BinPartialFail if Advantage campaign budget on/off ≠ what the name declares.campaign budget configTask✅ on 20 accts; budget type not in fleet BQ.
Geo / radius matchBinYesFail if ad-set geo is whole-US or ≠ MX service area.ad set — geo_locationsTaskAPI on 20; BQ FBADS_MOPS_TARGETING covers geo fleet-wide.
Optimization eventBinYesFail if optimization_goal ≠ intended.ad set.optimization_goalTaskAPI on 20; targeting fields in fleet BQ.
Placements as intendedBinYesFail if Advantage+ vs. manual placements ≠ plan.ad set — placementsTaskAPI on 20; placements in fleet BQ.
Special ad categoryBinPartialFail if special_ad_categories isn't set as intended.campaign.special_ad_categoriesTask✅ on 20 accts; campaign-level, not in the fleet targeting BQ.
Destination correctBinPartialFail if the ad routes to the wrong form / LP.ad creative — destinationTask✅ on 20 accts; destination not in fleet BQ.
Naming ↔ config reconciliationBinPartialParse name tokens; validate each against the actual setting.Names vs. live settingsTask✅ on 20 accts; fleet limited to the targeting fields BQ carries.
Section B — Delivery health (recommendation)
Frequency too highRecYesFlag ad sets with frequency > [threshold] (audience fatigue).Meta API — insightsReview flagEverywhere (BQ insights).

Coverage Wrapper

Every cycle
Type: Binary → system guardrail
Owner: System
Data: ClickUp active list vs. run manifest
CheckTypeToday?Fail conditionData sourceOn failViktor's notes
Account coverageBinYesFail if any account marked "active" in ClickUp is absent from the checked set.ClickUp active list vs. run manifestAlert to leadStraightforward.

What's intentionally out (and why)

DroppedReason
Pixel/CAPI as an always-on checkScoped to web/LP campaigns only — instant-form-only accounts have no pixel to expect.
Health-claim / disclaimer checkSo much runs through instant forms that a standalone disclaimer gate isn't worth the noise.
Advantage+ automation guardrailYou sometimes want these features on, so an auto-flag would fight the strategy.
Audience overlap & learning-phase flagsRemoved per request; kept the delivery-health section to Frequency only.
Performance / accountability layer (Jobs 4 & 5)The team-lowest + specialist-unders system is a Paid Search / Sawyer structure; not run for social.
HIP / active naming spend ruleGoogle-only.

The unlocks, in order of impact

UnlockWhat it opens
1 · GHL / PracticeBeacon accessLead reconciliation + $0 opportunities (both platforms). Needs an API key/integration or an AF-side proxy.
2 · Meta token scopepages_manage_ads (instant forms) + pixel stats. A re-auth with those scopes fixes both.
3 · Fleet-wide settings depthConfig audits beyond the 20 accounts need system-user API access to the BM or new BQ transfers (objective/budget columns).

Glossary

MX — media plan / declared spec each check reconciles against.

Instant form — Meta's native lead form; the QA target when an ad has no LP. Needs pages_manage_ads to read.

CAPI — Conversions API; checked only on web/LP campaigns.

BQ (pb_data) — BigQuery: ~240 Meta accounts, daily spend/results/copy/targeting.

20 accounts — the Meta API connection depth (ION + 19 partners) for full settings audits.

PB / GHL — lead source-of-truth systems; no access yet.