Skip to main content

Paid-media campaign lifecycle

Erdo can read a connected ad account in full — campaigns, spend, status, keywords — and it can also act on a campaign: pause it, enable it, or change its daily budget. The lifecycle endpoints expose exactly those three actions over /v1 (and the matching MCP tools), so an external product or script can drive a decision the evidence argues for instead of someone performing it by hand in the provider’s console. Every lifecycle call goes through the same approval gate that governs agent-proposed changes. There is one gated path into a customer’s ad account, not two: the /v1 call files the very action an agent’s tool would file — same action key, same input, same deduplication — and a caller with an API key is never more trusted than an agent. Supported providers: google_ads. Any other provider is rejected with an unimplemented error rather than silently ignored. For new campaigns, see Search campaign setup, including the Google Search-only network default and the checks before launch.

The two outcomes

What happens depends on whether your organization has granted a standing always-approve policy for the action (the same standing grant you create by choosing “always allow” when deciding an agent’s approval):
  • No standing policy — propose. The call does not touch the provider. It files a pending approval request and returns status: "pending_approval" with the approval_request_id — accepted, not performed (202 semantics, expressed in the body). The approval appears in the activity feed and on GET /v1/approvals, stamped with subject_resource_type: "paid_media_campaign" and the campaign’s external id so you can list everything awaiting decision about one campaign. When a human approves it — feed, CLI, or POST /v1/approvals/{id}/decide — the exact stored action executes, under the approver’s identity. A rejection is terminal and nothing reaches the provider.
  • A recent reasoned rejection — declined. If a person declined this same change with a reason in the last seven days, and your request states no new reason, the call files nothing and returns status: "declined" with a decline object: the rejected approval_request_id, decided_at, the person’s reason, and who decided. There is no pending approval to wait on; stop proposing it until the hold passes, or re-propose with a reason that says what changed. See Declining with a reason.
  • Standing policy — execute. The call executes immediately through the same integration handler an agent-run approval would execute, and returns status: "executed" with the provider’s result.
Re-proposing the same change while one is pending folds onto the existing request instead of filing a duplicate — including a budget change with a different amount, which is still “the same decision to review” and updates the pending card rather than stacking a second one.

Read and scope campaign conversion goals

Two surfaces cover a campaign’s conversion goals — the goals a campaign optimizes toward:
The read returns the organization’s owned campaigns and conversion actions in one Google Ads account. In an account shared between organizations, only the campaigns and conversion actions recorded as this organization’s are returned. Each campaign carries id, name, status, bidding_strategy_type, and goal_config_level (CUSTOMER when the campaign follows the account’s goals, CAMPAIGN when it carries its own custom goal); a campaign-level campaign also carries conversion_actions — the custom goal’s conversion action ids and names. The conversion_actions top-level list holds the owned conversion actions with id, name, type, category, and primary_for_goal. An omitted customer_id uses the account selected on the Google Ads connection. The write pins one campaign’s custom conversion goal to exactly the listed conversion action ids and disables the campaign’s inherited account goals, so the campaign optimizes toward its own form submits and qualified-lead uploads only:
This is the same approval-gated scope_google_ads_campaign_conversion_goals action the agent files, with the same campaign and conversion-action ownership checks. Without a standing always-approve policy it returns status: "pending_approval" with an approval_request_id; with one it executes and returns status: "ready" with the resulting goal. When the campaign’s custom goal already equals the requested set it answers ready and writes nothing — no approval, no provider call. It never changes campaign status or bidding. The CLI equivalents are erdo paid-media campaigns list and erdo paid-media campaigns conversion-goals <campaign_id> --actions 111,222.

Set campaign status

  • externalID (path) — the provider’s campaign id.
  • provider — google_ads.
  • customer_id — the provider account the campaign belongs to (campaign ids are only unique within an account). Required.
  • status — paused or enabled. Removal is deliberately not exposed: deleting a campaign is destructive, not lifecycle.
  • reason (optional) — shown on the approval card as the why beside the what, so the approver reads the evidence, not just the action.

Set campaign daily budget

  • daily_budget_micros — the new daily budget in micros of the account currency (50000000 = $50.00). Must be positive.

Response

or, when a standing policy covers the action:

Following up on a pending action

List what is awaiting decision about one campaign:
Decide it:
Approving with a broader scope (always_org, with parameter constraints) creates the standing policy that makes subsequent identical calls execute immediately — see Approvals and Autonomy. Before proposing another ad, the campaign review checks your existing copy, approved price or budget band, and the action the destination actually offers. An ad leading to a request form should invite a request. It should not promise published plans or an immediate download unless the destination provides them. Shared research can suggest a local test; its outcome must be measured using your own traffic and leads. Adding a second ad is not a randomized A/B test.

What a new campaign starts with

When Erdo builds a paid-search campaign for you it does not start from a blank sheet. Two things go in before the first click is bought — one of them learned about your kind of business, the other read from your own account:
  • Exclusions. A library of search terms a business in your vertical should never pay for — someone looking to rent when you are selling, someone price-fishing well below your offer, someone searching a different product class entirely. It is grouped by vertical and by language, so a campaign running Spanish and Portuguese angles gets the Spanish and Portuguese exclusions too: a new-development condo tower selling in Miami excludes rental intent in all three languages from day one, because that is the single biggest source of wasted clicks in that market. These are on top of, not instead of, what your own campaigns have already learned — a new campaign inherits the exclusions your existing campaigns already carry. Once it is running, the traffic quality monitor tells you when off-offer traffic is climbing, and the exclusions that answer it are proposed for your approval like any other change to your account.
  • Cost expectations, taken from your own account. A new campaign may use an expected conversion rate as planning evidence. Erdo establishes that from your history — your other campaigns matched on the same offer, audience geography and language, and the leads they actually produced — and says in the plan which campaigns it read, the numerator and denominator, the date window and the uncertainty. Where you have no comparable history, it labels the planning assumption rather than presenting it as a measurement. Thirty observed conversions may be an internal benchmark-review trigger only; it is not a Google requirement, learning guarantee or statistical sufficiency claim. The review also considers Google’s actual strategy status, conversion delay/cycles and an agreed measurement window. See Google’s learning guidance, conversion-based bidding and evaluation guidance.
No other customer’s numbers reach your campaign. What a campaign cost and what it converted is that advertiser’s own trading data, and Erdo does not publish it — not by account, and not as a per-market, per-month table with the account’s name taken off, because a vertical plus a market plus a month is often enough to say whose it was. The exclusions library is the one thing that does transfer, and it holds general intent — the word “rental”, the word “cheap” — never another advertiser’s campaign, account, keywords, competitor names, or the neighbourhoods they target. Erdo’s engineers write and update it; nothing reads across customer accounts to fill it. A term specific to one business — a competitor’s name, a street, a neighbourhood — deliberately never enters it, because the next customer may be in that neighbourhood, and excluding it for everyone would block their best search term. Erdo does separately publish aggregate experiment-effect priors pooled across organizations, but that is a different mechanism with its own publication thresholds and its own opt-out — see cross-customer priors.

Conversion checks before serving

Enabling a campaign, ad group, ad or keyword checks the primary destinations that would become eligible to serve. Creating an enabled ad or keyword and editing its desktop or mobile URL use the same check. Paused drafts are checked when their parent campaign or ad group is enabled. A new campaign with no ads can be enabled; its first enabled ad must pass the destination check. Account mutations are serialized through the provider response so a concurrent parent enable and child creation cannot both validate an older paused state. A busy account returns a retryable error before any provider write. For a published Erdo page, the check reads its served scripts and every current experiment variant. An installed browser conversion placeholder, a foreign or removed conversion target, or a missing website target selected by the campaign refuses the provider mutation. The error identifies the campaign, page and configuration problem. An unavailable provider or page read also refuses the mutation. Organic publication remains available with an unfilled placeholder. Sitelinks are paid-click destinations too. Linking a sitelink to a published Erdo page whose conversion is still the unfilled placeholder is refused while the campaign is enabled, and enabling a campaign is refused while any of its sitelinks, or the account’s, still lands on such a page. Unlike the ad and keyword check, a sitelink page or link that cannot be read does not block the change; only a page known to carry the placeholder does. Script inspection follows conversion calls and the landing kit’s conversion configuration. An unused object is not proof of installation. Shadowed or reassigned runtime globals, escaping configurations and unsupported dynamic conversion targets remain unresolved and refuse activation. A helper’s literal event name is resolved only when every reference is a direct call with the same string argument. Standard Google queue bootstraps and closed helpers that forward untouched arguments are supported. Aliased conversion functions and mutated configuration arrays remain unresolved. The connected account’s actions and campaign goals determine whether a browser tag is required. Explicit offline-upload goals can operate without one. The check changes no conversion action, goal, bidding setting or page. Campaign goal scoping leaves campaign status unchanged; campaign enabling remains a separate operation that performs the destination check. Direct goal mutations recheck under the same account owner that each affected campaign is paused, or is serving on click or manual bidding, which ignores conversion goals. The scoping operation skips that recheck for its own goal writes while keeping the account lock, tenancy checks and ownership checks; neither path changes campaign status. External destinations return unverifiable_external in conversion_tracking_checks; this check cannot establish their conversion configuration. A successful configuration check does not prove that a visitor’s conversion was delivered or attributed by Google.

MCP tools

The same capability is available as MCP tools:
  • erdo_set_paid_media_campaign_status — provider, customer id, campaign id, paused/enabled, optional reason.
  • erdo_set_paid_media_campaign_budget — provider, customer id, campaign id, daily budget in micros, optional reason.
  • erdo_ensure_paid_media_conversion_action — exact action name, optional Google Ads customer id, and optional lead category. It reuses an enabled exact-name conversion action or requests approval to create an UPLOAD_CLICKS action with ONE_PER_CLICK counting and primary_for_goal disabled. An omitted customer id uses the selected operating account.
The same operation is available in the CLI:
Add --category SUBMIT_LEAD_FORM to classify form submissions, or --customer-id 1234567890 to target another account this Google Ads connection is authorized to operate. A ready result includes the conversion action id. If creation needs a human decision, the command returns pending_approval and the approval request id; the action is created after approval. Bidding is not on this surface. How a campaign bids — and, in particular, which of an organization’s campaigns share a bidding strategy so their conversions pool — is decided when the campaign is created or promoted, through approval cards rather than a lifecycle call. See How Erdo bids. Erdo’s campaign-write boundary is server-side: new campaigns and bidding updates must use conversion bidding, and an enable reads the campaign’s effective provider strategy first. A legacy campaign that reads as click or manual bidding, or whose strategy cannot be identified, is refused before the status write; PAUSED remains available for management. Existing campaigns are still reported by the read and monitoring surfaces so they can be reviewed and promoted through the approval path. Maximize Conversions does not require an arbitrary 15- or 30-conversion minimum; review the provider’s actual history, delay and strategy status instead. Conversion-goal scoping holds the same account mutation ownership from the initial campaign status read through the custom-goal configuration and inherited-goal disable writes. A concurrent account mutation through this platform is refused until that sequence finishes. Campaign creation holds the same ownership through organization binding, conversion scoping and any explicitly requested final enable; external Google UI or API writers do not participate in this lock. The operation remains bounded and never pauses or enables the existing campaign. A provider timeout can still leave a goal write’s outcome unknown, but campaign status remains unchanged. A serving campaign therefore keeps serving while its goal is isolated, regardless of bidding strategy. The approval card and result both state that status is unchanged. Scoping can briefly expose the additive goal state between its two writes; that bounded window is preferred to silently stranding a serving campaign in PAUSED. Retry the same idempotent scoping operation after a partial or unconfirmed goal write. No follow-up re-enable is needed.

Changing website conversion actions while ads serve

Website conversion-action creation, updates and removal are checked against the connected account’s current campaign goals and serving primary destinations before the provider write. A newly effective website action must already be installed on each affected owned public page; an installed target cannot be removed while its page is serving. New actions have no provider-generated label before creation, so use an explicit secondary action for setup or complete setup while the affected campaigns are paused. The platform never pauses campaigns or changes a primary setting as a side effect of this check. The check preserves customer and campaign goal modes, including custom-selected secondary actions and explicit offline conversion modes. Actual category/origin goal rows take precedence; newly created website goal combinations follow Google’s documented category inheritance. An unrelated foreign campaign does not block an action change, but an affected foreign managed destination cannot be verified and refuses the write without reading its private content. External primary destinations remain explicitly unverifiable. These checks establish configuration compatibility, not successful conversion delivery or attribution.

Supported serving channels

Conversion-integrity checks currently model Google Ads SEARCH campaigns, including desktop, mobile and keyword destinations. An enabled campaign in an unsupported or unknown channel is retained in the authoritative campaign inventory even when it has no ad groups. Enabling that campaign, requesting a new unsupported campaign as enabled, or making a relevant conversion-action change that affects such a campaign’s effective goals or may invalidate an installed secondary website tag returns a visible error before the requested provider mutation. Authoritatively unrelated goal changes and unchanged enabled secondary actions do not require that unsupported destination model. PERFORMANCE_MAX asset-group destinations are not modeled; an empty ad-group result does not establish that a campaign cannot serve. Paused campaign setup remains available. The platform does not pause campaigns, change URL expansion, or change primary conversion settings to satisfy this check. New website actions keep Google’s primary default unless explicitly set; setup that needs a secondary action must pass extra_params: {"primaryForGoal": false} and bind its campaign’s explicit goal while paused. These checks establish supported configuration compatibility, not successful conversion delivery. Search destination checks also read the authoritative ad type and campaign asset automation settings. Prospective serving Dynamic Search Ads (EXPANDED_DYNAMIC_SEARCH_AD) and opted-in FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION are refused because their effective landing URLs cannot be established from the configured ad URLs. AI Max search-term matching alone remains supported with a fixed primary destination. Paused ads, paused ancestors and campaigns without enabled ads retain their setup behavior. A serving ad needs a primary desktop destination. A responsive Search ad with no ad-level URL can use keyword-only destinations only when every enabled positive criterion is authoritatively a keyword with a fixed primary URL and no keywordless AI Max path exists. Mobile URLs alone do not prove the primary destination. Relevant campaign settings edits are checked against their prospective status and automation values, including fields forwarded through extra_params. Wildcard update masks are refused visibly; name the fields being changed. Authoritatively unrelated action mutations retain the structured unverifiable_dynamic_destination_unaffected_goal limitation rather than claiming all dynamic destinations were verified. No expansion or campaign setting is changed automatically. Google documents Dynamic Search Ad serving URLs and AI Max asset automation defaults. Conversion-action checks read and project the account action inventory once per request and batch campaign goal configurations and custom goals. Every enabled campaign remains in the check, including accounts whose inventory spans multiple batches. Missing, duplicate or unexpected goal owners fail visibly instead of using another campaign’s configuration. The snapshot is not retained across requests.