> ## Documentation Index
> Fetch the complete documentation index at: https://docs.erdo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Paid-media campaign lifecycle

> Pause, resume, or re-budget a provider ad campaign over /v1 — through the same approval gate that governs agent-proposed changes

# 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.

## 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](/approvals) 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.

* **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.

## Set campaign status

```
POST /v1/paid-media/campaigns/{externalID}/status
```

```json theme={null}
{
  "provider": "google_ads",
  "customer_id": "1234567890",
  "status": "paused",
  "reason": "CPL 2.1x target over the last 14 days"
}
```

* `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

```
POST /v1/paid-media/campaigns/{externalID}/budget
```

```json theme={null}
{
  "provider": "google_ads",
  "customer_id": "1234567890",
  "daily_budget_micros": 50000000,
  "reason": "rank-constrained; excess budget buys no additional impressions"
}
```

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

## Response

```json theme={null}
{
  "status": "pending_approval",
  "approval_request_id": "0f9f6c1e-…",
  "action_display": "Set Google Ads campaign 'Brand — Miami' (23926631489) status to 'PAUSED'"
}
```

or, when a standing policy covers the action:

```json theme={null}
{
  "status": "executed",
  "action_display": "Set Google Ads campaign 'Brand — Miami' (23926631489) status to 'PAUSED'",
  "result": { "success": true, "campaign_id": "23926631489", "...": "…" }
}
```

## Following up on a pending action

List what is awaiting decision about one campaign:

```
GET /v1/approvals?status=pending&subject_resource_type=paid_media_campaign&subject_resource_id=23926631489
```

Decide it:

```
POST /v1/approvals/{approval_request_id}/decide
{ "decision": "approved" }
```

Approving with a broader scope (`always_org`, with parameter constraints)
creates the standing policy that makes subsequent identical calls execute
immediately — see [Approvals](/approvals) and [Autonomy](/autonomy).

## 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.
