| name | google-ads-apply |
| description | Execute approved draft actions in Google Ads accounts. Supports four safe mutation types plus bounded budget writes: add campaign-level negatives, add ad-group-level negatives, pause keywords, pause ad groups, and set campaign daily budgets through an Apply Manifest. Requires human confirmation via dry-run → approve → execute → verify → audit flow. All actions are logged in the audit trail and fully reversible.
|
| argument-hint | [draft-file] | review [draft-file|--all] | status | undo [action-id] | undo-draft [draft-file] | log |
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:
- 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_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:
- 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.