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.
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 documentationRead 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/google-ads apply [draft-file]
Flow:
/google-ads apply review [draft-file]
/google-ads apply review --all
Flow:
No network calls, no API access needed. Pure local analysis.
/google-ads apply status
Shows at a glance:
/google-ads undo [reversal-id]
Flow:
workspace/ads/audit-trail/reversal-registry.json/google-ads undo-draft [draft-file]
Reverses ALL active actions from a specific draft. Same confirmation flow.
/google-ads apply log
Shows recent apply sessions from workspace/ads/audit-trail/_log.md.
| 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 |
If a draft contains actions outside the supported scope, the apply command will:
| 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 |
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:
## Apply Manifestmeta.budget_policy controls whether any net increase is alloweddrafts/templates/negative-draft.md — negative keyword additions + removals + narrowsdrafts/templates/pause-draft.md — keyword pauses + ad group pauses + impact summaryBefore showing the dry run, validate:
If validation fails, report which actions failed and why. Don't block the entire apply — allow valid actions to proceed.
═══════════════════════════════════════════════════════
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:
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 |
After every apply session, these files are updated:
workspace/ads/audit-trail/_log.md — append-only session summaryworkspace/ads/audit-trail/YYYY-MM-DD-apply-session.md — full action-by-action logworkspace/ads/audit-trail/reversal-registry.json — machine-readable undo recordsworkspace/ads/drafts/_index.md — moved to Applied sectionworkspace/ads/change-log.md — record of what changed| 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 |