google-ads-apply

Installation

$npx skills add TheMattBerman/google-ads-copilot --skill google-ads-apply

Summary

Execute pre-approved mutations in Google Ads (negative keywords, keyword pauses, ad group pauses, budget adjustments) via a confirmed dry-run flow with full audit logging and reversal capability. The agent applies manifests to live accounts, verifies changes via GAQL, and tracks all actions for undo.

SKILL.MD

Google Ads Apply

Status: LIVE — Write Access Confirmed

API v20 confirmed working (2026-03-15). v18 is sunset (404), v19 unstable (500). Full write cycle proven: add campaign negative → GAQL verify → remove → verify removal. Keyword pauses and ad group pauses fully scaffolded and hardened. CLI scripts at scripts/apply-layer/. Smoke test: scripts/apply-layer/gads-smoke-test.sh <customer_id> See APPLY-LAYER.md for design details and scripts/apply-layer/README.md for the public CLI implementation guide. See OPERATOR-PLAYBOOK.md for the full operator workflow loop.

Read first:

  • OPERATOR-PLAYBOOK.md — full operator workflow (connect → select → review → apply → verify → undo)
  • APPLY-LAYER.md — design document, API specs, error handling
  • drafts/DRAFTS.md — draft system documentation

Read workspace:

  • workspace/ads/drafts/_index.md — current queue
  • workspace/ads/drafts/_summary.md — prioritized summary
  • workspace/ads/audit-trail/_log.md — previous apply sessions (if any)
  • workspace/ads/audit-trail/reversal-registry.json — active reversals

Commands

Apply a Draft

/google-ads apply [draft-file]

Flow:

  1. Read the draft file
  2. Parse all proposed actions (manifest-first for budget drafts; legacy parsing for v1 negatives/pauses)
  3. Validate each action (correct account, valid targets, within live apply scope)
  4. Resolve human-readable names to Google Ads resource IDs via GAQL
  5. Display dry run with every mutation listed, action type summary, and risk assessment
  6. Wait for operator confirmation ("confirm" or "cancel")
  7. Execute each action via Google Ads REST API v20
  8. Verify changes via GAQL re-query
  9. Write audit trail (session log, master log, reversal registry)
  10. Update draft status to "applied"
  11. Show post-apply summary with undo instructions

Review a Draft (No Apply)

/google-ads apply review [draft-file]
/google-ads apply review --all

Flow:

  1. Parse the draft into structured actions
  2. Show action breakdown with counts by type and risk levels
  3. Show confidence and dependencies
  4. Provide a proceed/defer recommendation
  5. Show next-step commands (dry-run or full apply)

No network calls, no API access needed. Pure local analysis.

Check Operator Status

/google-ads apply status

Shows at a glance:

  • Connection state (account, API version, credentials)
  • Pending drafts with status
  • Active reversals grouped by draft
  • Recent apply sessions
  • Suggested next step

Undo a Single Action

/google-ads undo [reversal-id]

Flow:

  1. Look up reversal record in workspace/ads/audit-trail/reversal-registry.json
  2. Display what will be reversed
  3. Warn if change has been live >7 days
  4. Wait for confirmation
  5. Execute reversal via Google Ads API
  6. Verify via GAQL
  7. Update reversal registry (status: "undone")

Undo an Entire Draft

/google-ads undo-draft [draft-file]

Reverses ALL active actions from a specific draft. Same confirmation flow.

View Apply Log

/google-ads apply log

Shows recent apply sessions from workspace/ads/audit-trail/_log.md.


Scope Guardrails (v1 + v2 budgets)

Allowed Actions

ActionTargetScopeRisk
Add negative keywordCampaign criterionCampaign-level negativeLow
Add negative keywordAd group criterionAd group-level negativeLow
Pause keywordAd group criterionSet status to PAUSEDLow
Pause ad groupAd groupSet status to PAUSEDMedium
Set campaign daily budgetCampaign budgetUpdate amount_microsMedium

Why These Are Safe

  • Negative keywords can only REDUCE traffic. They cannot increase spend, break ads, or corrupt tracking.
  • Pausing keywords stops traffic to one keyword. All history, quality scores, and configuration remain intact.
  • Pausing ad groups stops traffic to a set of keywords. Same preservation guarantees. Broader blast radius than keyword pause, hence Medium risk.
  • All are instantly reversible: remove the negative, or set status back to ENABLED.

Blocked Actions (Hard Reject)

If a draft contains actions outside the supported scope, the apply command will:

  1. List the out-of-scope actions
  2. Explain which are out of scope and why
  3. Offer to apply ONLY the in-scope actions
  4. Never execute out-of-scope actions regardless of confirmation
ActionVersionWhy Blocked
Bid strategy changesv2Can disrupt Smart Bidding learning
Campaign creationv3Large blast radius
RSA modificationsv4Learning period impact
Enable paused entitiesNever in v1Higher risk than pausing
Delete anythingNeverPause instead

Draft Parsing

The apply layer parses these sections from draft markdown files:

SectionAction TypeTemplate
Section A: Negatives to ADDADD_NEGATIVEnegative-draft.md
Section D: CRITICAL KEYWORD-LEVEL RECOMMENDATIONPAUSE_KEYWORDnegative-draft.md
Section A: Keywords to PAUSEPAUSE_KEYWORDpause-draft.md
Section B: Ad Groups to PAUSEPAUSE_ADGROUPpause-draft.md

Both negative-draft.md and pause-draft.md templates are supported. Mixed-type drafts (negatives + pauses in one file) are fully supported — the parser extracts from all applicable sections and deduplicates.

Budget drafts are different:

  • They MUST include ## Apply Manifest
  • The manifest is authoritative when present
  • Draft-level meta.budget_policy controls whether any net increase is allowed

Draft Templates

  • drafts/templates/negative-draft.md — negative keyword additions + removals + narrows
  • drafts/templates/pause-draft.md — keyword pauses + ad group pauses + impact summary

Validation Checks (Before Dry Run)

Before showing the dry run, validate:

  1. Account match: Draft account ID matches connected account
  2. Campaign exists: Campaign referenced in draft is still present (GAQL lookup)
  3. Ad group exists: For ad-group-scoped actions (GAQL lookup)
  4. No duplicates: Negative keyword doesn't already exist at the same scope
  5. Match type valid: PHRASE, EXACT, or BROAD only
  6. Target exists: Keyword being paused still exists and is currently ENABLED
  7. Ad group status: For ad group pause, target is currently ENABLED
  8. Within scope: Supported actions are add negative, pause keyword, pause ad group, and manifest-backed campaign daily budget changes

If validation fails, report which actions failed and why. Don't block the entire apply — allow valid actions to proceed.


Dry Run Display

═══════════════════════════════════════════════════════
 DRY RUN: [draft-file]
═══════════════════════════════════════════════════════

Account: [Name] (CID: [ID])
Actions: [N] valid / [M] total

#    Action           Target                       Detail                      Risk
---  ---------------  ---------------------------  --------------------------  ------
1    ADD NEG          Campaign: [name]             "near me" [PHRASE]          Low
2    ADD NEG          Campaign: [name]             "debris chute" [PHRASE]     Low
...
13   PAUSE KW         AG: [name]                   "waste mgmt" [EXACT]        Low
14   PAUSE AG         Campaign: [name]             [ad group name]             Medium

Summary:
  • 12 negative keyword addition(s)
  • 1 keyword pause(s)
  • 1 ad group pause(s)
  • Reversibility: All actions reversible

⚠️  This will make REAL changes to account [CID].
Type 'confirm' to proceed, or 'cancel' to abort:

Post-Apply Verification

After execution, run verification queries:

ActionVerification QueryWhat's Confirmed
Add campaign negativecampaign_criterion WHERE negative=TRUEKeyword exists in campaign
Add ad group negativead_group_criterion WHERE negative=TRUEKeyword exists in ad group
Pause keywordkeyword_view + status checkStatus is PAUSED
Pause ad groupad_group + status checkStatus is PAUSED

Audit Trail

After every apply session, these files are updated:

  1. Master log: workspace/ads/audit-trail/_log.md — append-only session summary
  2. Session detail: workspace/ads/audit-trail/YYYY-MM-DD-apply-session.md — full action-by-action log
  3. Reversal registry: workspace/ads/audit-trail/reversal-registry.json — machine-readable undo records
  4. Draft file: Updated with applied date and reversal IDs
  5. Draft index: workspace/ads/drafts/_index.md — moved to Applied section
  6. Change log: workspace/ads/change-log.md — record of what changed

CLI Scripts

ScriptPurpose
gads-apply.shFull apply flow: parse → validate → dry-run → confirm → execute → verify → audit
gads-apply.sh --dry-runParse and show dry run only
gads-apply.sh --parse-onlyParse draft to JSON (debugging)
gads-review.shParse and show operator-facing review (no API calls)
gads-review.sh --allReview all pending drafts
gads-undo.sh <rev-id>Undo a single action
gads-undo.sh --draft <file>Undo all actions from a draft
gads-undo.sh --listList all active reversals
gads-status.shFull operator status overview
gads-status.sh --appliedShow only applied actions
gads-status.sh --pendingShow only pending drafts
gads-smoke-test.shEnd-to-end write cycle test

Safety Rules

  1. Never apply without dry run. The operator must see exactly what will change.
  2. Never auto-approve. The operator must explicitly type "confirm."
  3. Never apply out-of-scope actions. Hard reject, clear explanation.
  4. Never delete anything. Pause is the maximum severity in v1.
  5. Log everything. Every action, every error, every reversal.
  6. Fail gracefully. If one action fails, continue with the rest. Report the failure.
  7. Verify after apply. Re-query to confirm changes took effect.
  8. Make undo easy. Every action has a stored reversal instruction.
  9. Rate limit. Max 50 mutations per apply session. 500ms delay between mutations.
  10. No batch apply across accounts. One account per apply session.