Installation
$npx skills add TheMattBerman/google-ads-copilot --skill google-ads-applySummary
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>SeeAPPLY-LAYER.mdfor design details andscripts/apply-layer/README.mdfor the public CLI implementation guide. SeeOPERATOR-PLAYBOOK.mdfor 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 handlingdrafts/DRAFTS.md— draft system documentation
Read workspace:
workspace/ads/drafts/_index.md— current queueworkspace/ads/drafts/_summary.md— prioritized summaryworkspace/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:
- Read the draft file
- Parse all proposed actions (manifest-first for budget drafts; legacy parsing for v1 negatives/pauses)
- Validate each action (correct account, valid targets, within live apply scope)
- Resolve human-readable names to Google Ads resource IDs via GAQL
- Display dry run with every mutation listed, action type summary, and risk assessment
- Wait for operator confirmation ("confirm" or "cancel")
- Execute each action via Google Ads REST API v20
- Verify changes via GAQL re-query
- Write audit trail (session log, master log, reversal registry)
- Update draft status to "applied"
- Show post-apply summary with undo instructions
Review a Draft (No Apply)
/google-ads apply review [draft-file]
/google-ads apply review --all
Flow:
- Parse the draft into structured actions
- Show action breakdown with counts by type and risk levels
- Show confidence and dependencies
- Provide a proceed/defer recommendation
- 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:
- Look up reversal record in
workspace/ads/audit-trail/reversal-registry.json - Display what will be reversed
- Warn if change has been live >7 days
- Wait for confirmation
- Execute reversal via Google Ads API
- Verify via GAQL
- 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
| Action | Target | Scope | Risk |
|---|---|---|---|
| Add negative keyword | Campaign criterion | Campaign-level negative | Low |
| Add negative keyword | Ad group criterion | Ad group-level negative | Low |
| Pause keyword | Ad group criterion | Set status to PAUSED | Low |
| Pause ad group | Ad group | Set status to PAUSED | Medium |
| Set campaign daily budget | Campaign budget | Update amount_micros | Medium |
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:
- List the out-of-scope actions
- Explain which are out of scope and why
- Offer to apply ONLY the in-scope actions
- Never execute out-of-scope actions regardless of confirmation
| Action | Version | Why Blocked |
|---|---|---|
| Bid strategy changes | v2 | Can disrupt Smart Bidding learning |
| Campaign creation | v3 | Large blast radius |
| RSA modifications | v4 | Learning period impact |
| Enable paused entities | Never in v1 | Higher risk than pausing |
| Delete anything | Never | Pause instead |
Draft Parsing
The apply layer parses these sections from draft markdown files:
| Section | Action Type | Template |
|---|---|---|
| Section A: Negatives to ADD | ADD_NEGATIVE | negative-draft.md |
| Section D: CRITICAL KEYWORD-LEVEL RECOMMENDATION | PAUSE_KEYWORD | negative-draft.md |
| Section A: Keywords to PAUSE | PAUSE_KEYWORD | pause-draft.md |
| Section B: Ad Groups to PAUSE | PAUSE_ADGROUP | pause-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_policycontrols whether any net increase is allowed
Draft Templates
drafts/templates/negative-draft.md— negative keyword additions + removals + narrowsdrafts/templates/pause-draft.md— keyword pauses + ad group pauses + impact summary
Validation Checks (Before Dry Run)
Before showing the dry run, validate:
- Account match: Draft account ID matches connected account
- Campaign exists: Campaign referenced in draft is still present (GAQL lookup)
- Ad group exists: For ad-group-scoped actions (GAQL lookup)
- No duplicates: Negative keyword doesn't already exist at the same scope
- Match type valid: PHRASE, EXACT, or BROAD only
- Target exists: Keyword being paused still exists and is currently ENABLED
- Ad group status: For ad group pause, target is currently ENABLED
- 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:
| Action | Verification Query | What's Confirmed |
|---|---|---|
| Add campaign negative | campaign_criterion WHERE negative=TRUE | Keyword exists in campaign |
| Add ad group negative | ad_group_criterion WHERE negative=TRUE | Keyword exists in ad group |
| Pause keyword | keyword_view + status check | Status is PAUSED |
| Pause ad group | ad_group + status check | Status is PAUSED |
Audit Trail
After every apply session, these files are updated:
- Master log:
workspace/ads/audit-trail/_log.md— append-only session summary - Session detail:
workspace/ads/audit-trail/YYYY-MM-DD-apply-session.md— full action-by-action log - Reversal registry:
workspace/ads/audit-trail/reversal-registry.json— machine-readable undo records - Draft file: Updated with applied date and reversal IDs
- Draft index:
workspace/ads/drafts/_index.md— moved to Applied section - Change log:
workspace/ads/change-log.md— record of what changed
CLI Scripts
| Script | Purpose |
|---|---|
gads-apply.sh | Full apply flow: parse → validate → dry-run → confirm → execute → verify → audit |
gads-apply.sh --dry-run | Parse and show dry run only |
gads-apply.sh --parse-only | Parse draft to JSON (debugging) |
gads-review.sh | Parse and show operator-facing review (no API calls) |
gads-review.sh --all | Review 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 --list | List all active reversals |
gads-status.sh | Full operator status overview |
gads-status.sh --applied | Show only applied actions |
gads-status.sh --pending | Show only pending drafts |
gads-smoke-test.sh | End-to-end write cycle test |
Safety Rules
- Never apply without dry run. The operator must see exactly what will change.
- Never auto-approve. The operator must explicitly type "confirm."
- Never apply out-of-scope actions. Hard reject, clear explanation.
- Never delete anything. Pause is the maximum severity in v1.
- Log everything. Every action, every error, every reversal.
- Fail gracefully. If one action fails, continue with the rest. Report the failure.
- Verify after apply. Re-query to confirm changes took effect.
- Make undo easy. Every action has a stored reversal instruction.
- Rate limit. Max 50 mutations per apply session. 500ms delay between mutations.
- No batch apply across accounts. One account per apply session.