---
title: "Apply Plan"
method: POST
path: "/plans/{plan_id}/apply"
---

# Apply Plan

`POST /plans/{plan_id}/apply`

## Request

- Base URL: `https://app.runconverge.com/api`
- URL: `https://app.runconverge.com/api/plans/{plan_id}/apply`
- Auth: none declared

Apply the accepted subset of a persisted plan and record outcomes.

The plan body itself is loaded from `plan.payload` — the FE no
longer ships it in the request. The endpoint is single-shot per
plan: a non-ACTIVE plan returns 409, so the caller can't double-apply
even if the FE's button-disable logic misfires.

## Path parameters

- `plan_id` integer, required

## Headers

- `Authorization` string, nullable
- `X-User-Id` string, nullable
- `X-Posthog-Session-Id` string

## Cookies

- `session_id` string, nullable

## Request body

- ApplyPlanRequest: User's per-suggestion verdict. `accepted` drives the apply loop; `rejected` and `skipped` are persisted alongside as the audit trail and to distinguish "user said no" (with an optional note — a reason, question, or counter-proposal) from "user didn't act on it". Applying with an empty `accepted` (only rejections) pushes nothing and cancels the plan — the user gave feedback without taking any change live. `edits` carries what the operator changed in the review UI before applying: a retuned value, a change swapped for a different one, or a change left out of the push. Each addresses a change inside an *accepted* suggestion, and a change takes at most one edit.
  - `accepted` string[]
  - `rejected` SuggestionFeedback[]
    - `suggestion_id` string, required
    - `feedback` string, nullable
  - `skipped` string[]
  - `edits` PlanChangeEdit[]
    - `suggestion_id` string, required
    - `change_index` integer, required
    - `edit` union, required
      - ValueEdit: Retune the value the agent proposed, leaving the change itself alone.
        - `kind` 'value'
        - `value` union, required
          - MonetaryOverride: Operator edit of a monetary change's target, in minor currency units. TODO: migrate this from minor units (`to_cents`) to major units (`to_amount`) to be consistent with `MonetaryChange`, which now carries major units.
            - `field` 'daily_budget' | 'lifetime_budget' | 'bid_amount', required
            - `to_cents` integer, required
          - SpendCapOverride: Operator edit of a spend-cap change's target. Like `SpendCapChange`, the target may be empty: `to_cents: null` removes the cap. Required with no default, so an omitted value can't be read as a removal.
            - `field` 'daily_spend_cap' | 'lifetime_spend_cap', required
            - `to_cents` integer, nullable, required
          - RoasGoalOverride: Operator edit of a ROAS-goal change's target (human-facing multiplier).
            - `field` 'roas_average_floor', required
            - `to_roas` number, required
          - DuplicateAdOverride
            - `status_option` 'ACTIVE' | 'PAUSED' | 'INHERITED_FROM_SOURCE', required
            - `rename_prefix` string, nullable
            - `rename_suffix` string, nullable
            - `field` 'duplicate_ad', required
          - DuplicateAdsetOverride
            - `status_option` 'ACTIVE' | 'PAUSED' | 'INHERITED_FROM_SOURCE', required
            - `rename_prefix` string, nullable
            - `rename_suffix` string, nullable
            - `field` 'duplicate_adset', required
          - DuplicateCampaignOverride
            - `status_option` 'ACTIVE' | 'PAUSED' | 'INHERITED_FROM_SOURCE', required
            - `rename_prefix` string, nullable
            - `rename_suffix` string, nullable
            - `field` 'duplicate_campaign', required
      - ReplaceEdit: Swap the change for a different one on the same object.
        - `kind` 'replace', required
        - `change` union, required
          - DailyBudgetChange
            - `target` ChangeTarget, required
              - …
            - `note` string, nullable
            - `currency` string, required
            - `from_amount` number, required
            - `to_amount` number, required
            - `scope` 'campaign' | 'adset', required
            - `field` 'daily_budget', required
          - LifetimeBudgetChange
            - `target` ChangeTarget, required
              - …
            - `note` string, nullable
            - `currency` string, required
            - `from_amount` number, required
            - `to_amount` number, required
            - `scope` 'campaign' | 'adset', required
            - `field` 'lifetime_budget', required
          - DailySpendCapChange
            - `target` ChangeTarget, required
              - …
            - `note` string, nullable
            - `currency` string, required
            - `from_amount` number, nullable, required
            - `to_amount` number, nullable, required
            - `field` 'daily_spend_cap', required
            - `scope` 'adset', required
          - LifetimeSpendCapChange
            - `target` ChangeTarget, required
              - …
            - `note` string, nullable
            - `currency` string, required
            - `from_amount` number, nullable, required
            - `to_amount` number, nullable, required
            - `field` 'lifetime_spend_cap', required
            - `scope` 'campaign' | 'adset', required
          - BidAmountChange: Ad set bid/cost goal (`bid_amount`), in the account's major units. Backs Meta's `LOWEST_COST_WITH_BID_CAP` (bid cap) and `COST_CAP` (cost-per-result goal) bid strategies — both store their target in the ad-set-level `bid_amount` field, so a single change variant covers both.
            - `target` ChangeTarget, required
              - …
            - `note` string, nullable
            - `currency` string, required
            - `from_amount` number, required
            - `to_amount` number, required
            - `field` 'bid_amount', required
            - `scope` 'adset', required
          - RoasGoalChange: Ad set minimum-ROAS goal for the `LOWEST_COST_WITH_MIN_ROAS` strategy.
            - `target` ChangeTarget, required
              - …
            - `note` string, nullable
            - `field` 'roas_average_floor', required
            - `scope` 'adset', required
            - `from_roas` number, required
            - `to_roas` number, required
          - StatusChange
            - `target` ChangeTarget, required
              - …
            - `note` string, nullable
            - `field` 'status', required
            - `scope` 'campaign' | 'adset' | 'ad', required
            - `from` 'ACTIVE' | 'PAUSED', required
            - `to` 'ACTIVE' | 'PAUSED', required
          - NameChange
            - `target` ChangeTarget, required
              - …
            - `note` string, nullable
            - `field` 'name', required
            - `scope` 'campaign' | 'adset' | 'ad', required
            - `name` string, required
          - LandingUrlChange: Change an ad's landing page URL — the destination "Website URL". Applied by minting a new creative from the source's spec with the link replaced (or rebuilt from the post a post-ID ad wraps), then pointing the ad at it. The new creative gets a new page post, so accumulated social proof (likes, comments) does not carry.
            - `target` ChangeTarget, required
              - …
            - `note` string, nullable
            - `field` 'landing_url', required
            - `scope` 'ad', required
            - `from_url` string, required
            - `to_url` string, required
          - DuplicateAdChange: Duplicate an existing ad into another adset reusing the source ad's creative
            - `target` ChangeTarget, required
              - …
            - `note` string, nullable
            - `field` 'duplicate_ad', required
            - `scope` 'ad', required
            - `destination_adset_id` string, nullable
            - `status_option` 'ACTIVE' | 'PAUSED' | 'INHERITED_FROM_SOURCE'
            - `rename_suffix` string, nullable
            - `rename_prefix` string, nullable
            - `landing_url` string, nullable
          - DuplicateAdsetChange: Duplicate an existing ad set — its settings and its ads. Applied by *recreating* the ad set (Meta's `/copies` can only name the copy via prefix/suffix, and name-based URL parameters freeze at first publish, so the copy must be created bearing its final name). The child ads are then duplicated into the copy reusing each source ad's creative, so post IDs carry over; the ads keep their source names and statuses.
            - `target` ChangeTarget, required
              - …
            - `note` string, nullable
            - `field` 'duplicate_adset', required
            - `scope` 'adset', required
            - `destination_campaign_id` string, nullable
            - `status_option` 'ACTIVE' | 'PAUSED' | 'INHERITED_FROM_SOURCE'
            - `rename_suffix` string, nullable
            - `rename_prefix` string, nullable
            - `budget` Budget: A monetary amount with its spend period.
              - …
            - `bid` MonetaryAmount: A currency-denominated value — major units on the wire, the ad account's currency, scaled to minor units at the Meta boundary.
              - …
            - `attribution` AttributionSetting: A Meta attribution setting, as the human-facing window pair. Translated to the ad-set-level `attribution_spec` at the applier boundary (`converge_api.plan.facebook.attribution_setting_to_spec`). Meta's valid combinations today: 1- or 7-day click, optionally with a 1-day engaged view (video) and/or a 1-day view.
              - …
            - `landing_url` string, nullable
          - DuplicateCampaignChange: Duplicate an existing campaign — its settings, ad sets, and ads. Applied by *recreating* the whole tree (see `DuplicateAdsetChange` for why `/copies` can't be used): the campaign is created bearing its final name, each ad set is recreated into it keeping its own name and status, and each ad is duplicated reusing the source ad's creative so post IDs carry over.
            - `target` ChangeTarget, required
              - …
            - `note` string, nullable
            - `field` 'duplicate_campaign', required
            - `scope` 'campaign', required
            - `status_option` 'ACTIVE' | 'PAUSED' | 'INHERITED_FROM_SOURCE'
            - `rename_suffix` string, nullable
            - `rename_prefix` string, nullable
            - `attribution` AttributionSetting: A Meta attribution setting, as the human-facing window pair. Translated to the ad-set-level `attribution_spec` at the applier boundary (`converge_api.plan.facebook.attribution_setting_to_spec`). Meta's valid combinations today: 1- or 7-day click, optionally with a 1-day engaged view (video) and/or a 1-day view.
              - …
      - DropEdit: Leave the change out of the push. A suggestion must keep one.
        - `kind` 'drop', required

## Response `200`

Successful Response

- ApplyPlanResponse
  - `outcomes` SuggestionOutcome[], required
    - `suggestion_id` string, required
    - `success` boolean, required
    - `error` string, nullable
    - `per_change` ChangeApplicationOutcome[]
      - `change_index` integer, required
      - `platform_object_id` string, required
      - `field` string, required
      - `integration_id` integer, nullable
      - `outcome` 'applied' | 'failed' | 'aborted' | 'skipped', required: What became of a single `Change` in an accepted suggestion.
      - `error` string, nullable
      - `created_platform_object_id` string, nullable
  - `status` 'active' | 'applied' | 'partially_applied' | 'superseded' | 'canceled', required

## Other responses

- `422`: Validation Error

## Changes

- **2026-10-02** `b5568b902b98`: 2 breaking, 3 info
  - removed the enum value `daily_spend_cap` of the request property `edits/items/edit/oneOf[subschema #1: ValueEdit]/value/oneOf[subschema #1: MonetaryOverride]/field`
  - removed the enum value `lifetime_spend_cap` of the request property `edits/items/edit/oneOf[subschema #1: ValueEdit]/value/oneOf[subschema #1: MonetaryOverride]/field`
  - mapped value for discriminator key `daily_spend_cap` changed from `#/components/schemas/MonetaryOverride` to `#/components/schemas/SpendCapOverride` for `edits/items/edit/oneOf[subschema #1: ValueEdit]/value` request property
  - mapped value for discriminator key `lifetime_spend_cap` changed from `#/components/schemas/MonetaryOverride` to `#/components/schemas/SpendCapOverride` for `edits/items/edit/oneOf[subschema #1: ValueEdit]/value` request property
  - …1 more
- …earlier changes not shown

[Operation history](https://skmtc.dev/runconverge/apis/runconverge-api/changes/plans/:plan_id/apply/post.md)

---

[API](https://skmtc.dev/runconverge/apis/runconverge-api.md) - [All operations](https://skmtc.dev/runconverge/apis/runconverge-api/llms.txt) - [OpenAPI document](https://skmtc.dev/runconverge/apis/runconverge-api/revisions/f723e951cc0b?raw)
