We never edit a live ad. The only path to changing your ad copy is a new ad, created paused next to the ones already running. Almost everything else follows the same shape: prove it works on a reversible test before it ever touches your budget. Two actions are the exception, and they're marked clearly - in the product, a live-spend button is always orange with a two-step confirm; every other button is black.

A/B experiments

Runs on a copy of the campaign, split-tested against what you're doing today. Nothing about your live campaign changes until you review the result and choose to conclude it. Every experiment lever below shares the same three-call scaffold before it does anything lever-specific: create the experiment, split it into a control and treatment arm, then schedule it once the lever-specific write (shown per row) lands on the treatment copy.

Show the shared experiment scaffold
POST customers/1234567890/experiments:mutate
{
  "operations": [{
    "create": {
      "resourceName": "customers/1234567890/experiments/-1",
      "name": "Target ROAS test - Search - Brand",
      "type": "SEARCH_CUSTOM",
      "status": "SETUP"
    }
  }]
}

POST customers/1234567890/experimentArms:mutate
{
  "operations": [
    { "create": { "experiment": "customers/1234567890/experiments/998",
                   "control": true, "trafficSplit": 50 } },
    { "create": { "experiment": "customers/1234567890/experiments/998",
                   "control": false, "trafficSplit": 50 } }
  ]
}

POST customers/1234567890/experiments/998:scheduleExperiment
ActionWhat happens in your accountWhere it comes fromAPINoteStatus
Bidding-target testRuns a target-CPA or target-ROAS test on a copy of the campaign, split against your current target.Recommendations feed & Chat
experiments:mutate → experimentArms:mutate → campaigns:mutate → experiments/{id}:scheduleExperimentPreview: POST /api/insights/preview · Apply: POST /api/insights/apply
Show request & response
POST customers/1234567890/campaigns:mutate
{
  "operations": [{
    "update": {
      "resourceName": "customers/1234567890/campaigns/9876543210",
      "targetRoas": { "targetRoas": 3.2 }
    },
    "updateMask": "target_roas.target_roas"
  }]
}
{ "results": [{ "resourceName": "customers/1234567890/campaigns/9876543210" }] }
Search campaigns only.Ready
Ad-group bid-target testThe same bidding-target test, scoped to a single ad group.Recommendations feed
experiments:mutate → experimentArms:mutate → adGroups:mutate → experiments/{id}:scheduleExperimentApply: POST /api/insights/apply
Show request & response
POST customers/1234567890/adGroups:mutate
{
  "operations": [{
    "update": {
      "resourceName": "customers/1234567890/adGroups/222111",
      "targetRoas": 3.2
    },
    "updateMask": "target_roas"
  }]
}
{ "results": [{ "resourceName": "customers/1234567890/adGroups/222111" }] }
Not yet available in Chat.Ready
Location bid testTests a bid adjustment for a specific location.Recommendations feed
experiments:mutate → experimentArms:mutate → campaignCriteria:mutate → experiments/{id}:scheduleExperimentApply: POST /api/insights/apply
Show request & response
POST customers/1234567890/campaignCriteria:mutate
{
  "operations": [{
    "update": {
      "resourceName": "customers/1234567890/campaignCriteria/9876543210~1007735",
      "bidModifier": 1.2
    },
    "updateMask": "bid_modifier"
  }]
}
{ "results": [{ "resourceName": "customers/1234567890/campaignCriteria/9876543210~1007735" }] }
Not yet available in Chat.Ready
Ad-group split testSplits a mixed-intent ad group into focused ones and tests the split.Recommendations feed & Chat
experiments:mutate → experimentArms:mutate → googleAds:mutate (adGroup + adGroupCriterion + adGroupAd, one batch) → experiments/{id}:scheduleExperimentApply: POST /api/insights/apply
Show request & response
POST customers/1234567890/googleAds:mutate
{
  "mutateOperations": [
    { "adGroupOperation": { "create": {
        "resourceName": "customers/1234567890/adGroups/-1",
        "name": "Same-day delivery - Business",
        "campaign": "customers/1234567890/campaigns/9876543210",
        "status": "ENABLED" } } },
    { "adGroupCriterionOperation": { "create": {
        "adGroup": "customers/1234567890/adGroups/-1",
        "status": "ENABLED",
        "keyword": { "text": "business courier account", "matchType": "PHRASE" } } } },
    { "adGroupAdOperation": { "create": {
        "adGroup": "customers/1234567890/adGroups/-1",
        "status": "ENABLED",
        "ad": { "responsiveSearchAd": {
          "headlines": [{ "text": "Business courier accounts" }],
          "descriptions": [{ "text": "Dedicated support for recurring business shipments." }] } } } } }
  ]
}
{
  "mutateOperationResponses": [
    { "adGroupResult": { "resourceName": "customers/1234567890/adGroups/222222" } },
    { "adGroupCriterionResult": { "resourceName": "customers/1234567890/adGroupCriteria/222222~333" } },
    { "adGroupAdResult": { "resourceName": "customers/1234567890/adGroupAds/222222~444" } }
  ]
}
Multiple ad groups and ads at once, so you review the full plan before deploying.Ready
Messaging-angle testTests new ad copy on a messaging angle you haven't tried yet.Recommendations feed & Chat
experiments:mutate → experimentArms:mutate → adGroupAds:mutate → experiments/{id}:scheduleExperimentApply: POST /api/insights/apply
Show request & response
POST customers/1234567890/adGroupAds:mutate
{
  "operations": [{
    "create": {
      "adGroup": "customers/1234567890/adGroups/222111",
      "status": "ENABLED",
      "ad": {
        "responsiveSearchAd": {
          "headlines": [{ "text": "Delivered in 90 minutes" }, { "text": "Track it live" }],
          "descriptions": [{ "text": "Real-time tracking on every same-day booking." }]
        }
      }
    }
  }]
}
{ "results": [{ "resourceName": "customers/1234567890/adGroupAds/222111~556677" }] }
Delivered as an experiment - nothing about your current ads changes until you conclude it.Ready
Budget-raise testTests a higher daily budget against your current one, split-tested rather than a raw raise.Not yet availableNot built yet.We already tell you whether a capped campaign would benefit from more budget. Turning that into a one-click reversible test is next.Not ready

New things

Created paused. Nothing you already have changes, and nothing new starts serving until you enable it.

ActionWhat happens in your accountWhere it comes fromAPINoteStatus
New adAdds a new ad alongside your current ones, created paused.Recommendations feed & Chat
adGroupAds:mutatePreview: POST /api/insights/preview · Apply: POST /api/insights/apply · Pause/enable: POST /api/insights/ad
Show request & response
POST customers/1234567890/adGroupAds:mutate
{
  "operations": [{
    "create": {
      "adGroup": "customers/1234567890/adGroups/222111",
      "status": "PAUSED",
      "ad": {
        "finalUrls": ["https://example.com/book"],
        "responsiveSearchAd": {
          "headlines": [{ "text": "Same-day delivery" }, { "text": "Book a courier now" }],
          "descriptions": [{ "text": "Send a parcel across the city in under two hours." }]
        }
      }
    }
  }]
}
{ "results": [{ "resourceName": "customers/1234567890/adGroupAds/222111~333444" }] }
Nothing about your existing ads changes.Ready
New Search campaignBuilds a whole new Search campaign, created paused.Recommendations feed & Chat
googleAds:mutate (campaignBudget + campaign + adGroup + adGroupCriterion + adGroupAd, one atomic batch)Apply: POST /api/insights/apply
Show request & response
POST customers/1234567890/googleAds:mutate
{
  "mutateOperations": [
    { "campaignBudgetOperation": { "create": {
        "resourceName": "customers/1234567890/campaignBudgets/-1",
        "name": "Courier - Same day [GoodLads]",
        "amountMicros": "20000000" } } },
    { "campaignOperation": { "create": {
        "resourceName": "customers/1234567890/campaigns/-1",
        "name": "Courier - Same day [GoodLads]",
        "status": "PAUSED",
        "advertisingChannelType": "SEARCH",
        "campaignBudget": "customers/1234567890/campaignBudgets/-1" } } },
    { "adGroupOperation": { "create": {
        "resourceName": "customers/1234567890/adGroups/-1",
        "name": "Same-day delivery",
        "campaign": "customers/1234567890/campaigns/-1",
        "status": "ENABLED" } } },
    { "adGroupCriterionOperation": { "create": {
        "adGroup": "customers/1234567890/adGroups/-1",
        "status": "ENABLED",
        "keyword": { "text": "same day courier", "matchType": "PHRASE" } } } },
    { "adGroupAdOperation": { "create": {
        "adGroup": "customers/1234567890/adGroups/-1",
        "status": "ENABLED",
        "ad": { "responsiveSearchAd": {
          "headlines": [{ "text": "Same-day delivery" }],
          "descriptions": [{ "text": "Book a courier and track it to the door." }] } } } } }
  ]
}
{
  "mutateOperationResponses": [
    { "campaignBudgetResult": { "resourceName": "customers/1234567890/campaignBudgets/998877" } },
    { "campaignResult": { "resourceName": "customers/1234567890/campaigns/998878" } },
    { "adGroupResult": { "resourceName": "customers/1234567890/adGroups/998879" } },
    { "adGroupCriterionResult": { "resourceName": "customers/1234567890/adGroupCriteria/998878~998880" } },
    { "adGroupAdResult": { "resourceName": "customers/1234567890/adGroupAds/998879~998881" } }
  ]
}
You review every ad group, keyword and ad before deploying.Ready
New App campaignBuilds a new App install campaign, created paused, for an app you already advertise.Chat
googleAds:mutate (campaignBudget + campaign [App channel] + campaignCriterion [locations, optional] + adGroup + adGroupAd, one atomic batch - no keywords)Apply: POST /api/insights/apply
Show request & response
POST customers/1234567890/googleAds:mutate
{
  "mutateOperations": [
    { "campaignBudgetOperation": { "create": {
        "resourceName": "customers/1234567890/campaignBudgets/-1",
        "name": "App install - Courier app [GoodLads]",
        "amountMicros": "15000000" } } },
    { "campaignOperation": { "create": {
        "resourceName": "customers/1234567890/campaigns/-1",
        "name": "App install - Courier app [GoodLads]",
        "status": "PAUSED",
        "advertisingChannelType": "MULTI_CHANNEL",
        "advertisingChannelSubType": "APP_CAMPAIGN",
        "campaignBudget": "customers/1234567890/campaignBudgets/-1",
        "appCampaignSetting": {
          "appId": "com.example.courier",
          "appStore": "GOOGLE_APP_STORE",
          "biddingStrategyGoalType": "OPTIMIZE_INSTALLS_TARGET_INSTALL_COST"
        },
        "targetCpa": { "targetCpaMicros": "3000000" } } } },
    { "adGroupOperation": { "create": {
        "resourceName": "customers/1234567890/adGroups/-1",
        "campaign": "customers/1234567890/campaigns/-1",
        "status": "ENABLED" } } },
    { "adGroupAdOperation": { "create": {
        "adGroup": "customers/1234567890/adGroups/-1",
        "status": "ENABLED",
        "ad": { "appAd": {
          "headlines": [{ "text": "Book a courier in seconds" }],
          "descriptions": [{ "text": "Same-day delivery, tracked door to door." }] } } } } }
  ]
}
{
  "mutateOperationResponses": [
    { "campaignBudgetResult": { "resourceName": "customers/1234567890/campaignBudgets/998890" } },
    { "campaignResult": { "resourceName": "customers/1234567890/campaigns/998891" } },
    { "adGroupResult": { "resourceName": "customers/1234567890/adGroups/998892" } },
    { "adGroupAdResult": { "resourceName": "customers/1234567890/adGroupAds/998892~998893" } }
  ]
}
Ask for it directly - it works end to end, but it's still new: not yet part of the automatic recommendations feed, and we're still gathering live results before recommending it by default.In progress

Live changes

Two actions take effect the moment you deploy them, because there's no reversible test version of either one. Both are still entirely your decision, one click at a time - and both are just as reversible to undo as they were to apply.

ActionWhat happens in your accountWhere it comes fromAPINoteStatus
Negative keywordsBlocks wasted search terms on your live campaign or ad group.Recommendations feed & Chat
campaignCriteria:mutate (campaign scope) or adGroupCriteria:mutate (ad-group scope)Apply: POST /api/insights/apply · Undo: POST /api/insights/negatives
Show request & response
POST customers/1234567890/campaignCriteria:mutate
{
  "operations": [
    { "create": { "campaign": "customers/1234567890/campaigns/998877",
                   "negative": true,
                   "keyword": { "text": "free courier", "matchType": "PHRASE" } } },
    { "create": { "campaign": "customers/1234567890/campaigns/998877",
                   "negative": true,
                   "keyword": { "text": "courier jobs", "matchType": "EXACT" } } }
  ]
}
{
  "results": [
    { "resourceName": "customers/1234567890/campaignCriteria/998877~111" },
    { "resourceName": "customers/1234567890/campaignCriteria/998877~112" }
  ]
}
Takes effect immediately - there's no test version of this, because a negative keyword has to block terms to save you anything. A two-step confirm always precedes it.Ready
App campaign bid targetAdjusts an App campaign's live bidding target directly.Recommendations feed
googleAds:search (reads the current target first, for the undo) → campaigns:mutateApply: POST /api/insights/apply · Undo: POST /api/insights/live-edit
Show request & response
POST customers/1234567890/campaigns:mutate
{
  "operations": [{
    "update": {
      "resourceName": "customers/1234567890/campaigns/998900",
      "targetCpa": { "targetCpaMicros": "2500000" }
    },
    "updateMask": "target_cpa.target_cpa_micros"
  }]
}
{ "results": [{ "resourceName": "customers/1234567890/campaigns/998900" }] }
App campaigns can't run our experiments at all, so this is the only way to move that lever. A two-step confirm always precedes it.Ready

See how these actions get proposed in the first place on the Insights page, the account journey they fit into on Jobs to be done, or which skill pairs which read with which write on Skills.