google-ads-apply
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.