SuperAPI AI Directory

clone

Marketing & growth

View repository

Updated 10-16 09:30

About this skill

Spotify Ads API — Campaign & Ad Set Cloning

Clone an existing campaign or ad set by reading its full hierarchy and recreating it as drafts with optional modifications. Nothing is published automatically.

Note: If the goal is to edit an existing campaign rather than duplicate it, use /spotify-ads-api:drafts stage-edit campaign <campaign_id> <changes> instead.

Setup

Set the plugin root and define the request wrapper:

PLUGIN_ROOT="${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-.}}"
api() { "$PLUGIN_ROOT/scripts/api-request.sh" clone "$@"; }

Before the first Ads API v3 call, read and follow $PLUGIN_ROOT/skills/api-reference/references/live-openapi.md.

To retrieve settings values (TOKEN, AD_ACCOUNT_ID, AUTO_EXECUTE, BASE_URL, SDK_HEADER, SKILL_HEADER, PLUGIN_VERSION) for use outside API calls, run api --env. The output is eval-safe, so eval $(api --env) assigns them all.

Parsing Arguments

  • campaign <campaign_id> → Clone a full campaign hierarchy (campaign + ad sets + ads)
  • ad-set <ad_set_id> → Clone a single ad set and its ads into an existing campaign
  • If no argument, ask the user which entity to clone.

Clone Campaign (campaign <campaign_id>)

Step 1: Read Source Hierarchy

Fetch the source campaign

api GET "ad_accounts/{ad_account_id}/campaigns/$CAMPAIGN_ID"

Fetch all ad sets under the campaign

api GET "ad_accounts/{ad_account_id}/ad_sets?campaign_ids=$CAMPAIGN_ID&limit=50&sort_direction=DESC"

Paginate with offset if total_results > 50.

Fetch all ads under the campaign

api GET "ad_accounts/{ad_account_id}/ads?campaign_ids=$CAMPAIGN_ID&limit=50&sort_direction=DESC"

Paginate with offset if total_results > 50.

Step 2: Display Source Tree

Present the source hierarchy in tree format:

Source: "Summer Promo" (REACH) — campaign_id: abc-123
├── Ad Set: "US 18-34 Audio" (AUDIO, $75/day, US, ages 18-34, Jun 1 – Jun 30)
│   ├── Ad: "30s Spot A" → SHOP_NOW → example.com [APPROVED]
│   └── Ad: "30s Spot B" → LEARN_MORE → example.com [APPROVED]
└── Ad Set: "US 25-54 Video" (VIDEO, $50/day, US, ages 25-54, Jun 1 – Jun 30)
    ├── Ad: "15s Video" → WATCH_NOW → example.com [APPROVED]
    └── Ad: "Old Creative" → SHOP_NOW → example.com [ARCHIVED] ← will be skipped

Note how many entities will be cloned and how many will be skipped (ARCHIVED or REJECTED ads are skipped by default).

Step 3: Ask for Modifications

Ask the user what to change. Default: clone as-is with " (Copy)" appended to names.

Modification options:

  • Name: New campaign name (default: "{original name} (Copy)")
  • Dates: New start_time and end_time for all ad sets. If the original dates are in the past, require new dates — the clone will fail with past dates.
  • Budget: Adjust budget for all ad sets. Options:
    • Same as original
    • Set all to a specific amount
    • Increase/decrease by a percentage
  • Targeting: Change geo, age range, platforms, or genders across all ad sets. Modifications apply to all ad sets uniformly. For per-ad-set changes, suggest cloning individual ad sets.
  • Ad set filter: Optionally exclude specific ad sets from the clone.

Step 4: Validate Before Execution

Date validation

If the user does not change dates and the source start_time is in the past, warn:

  • If start_time is in the past but end_time is in the future: "The cloned ad sets will start delivering immediately."
  • If end_time is in the past: "Source dates have passed. New dates are required for the clone."

Asset validation

For each ad that will be cloned, check that the referenced assets (asset_id, logo_asset_id, companion_asset_id) still exist and are in READY status:

api GET "ad_accounts/{ad_account_id}/assets/$ASSET_ID"

If any asset is ARCHIVED or REJECTED, warn the user and ask whether to skip that ad or select a replacement asset.

Budget type validation

If budget type is LIFETIME and the user changed dates, verify that end_time is still provided — LIFETIME budgets require an end time.

Audience estimate validation

If targeting, dates, objective, bid, or budget changed for any cloned ad set, run a pre-flight audience estimate before creating it.

api POST "estimates/audience" \
  '{
    "ad_account_id": "<AD_ACCOUNT_ID>",
    "start_date": "<start_time>",
    "asset_format": "<AUDIO|VIDEO|IMAGE|CATALOG>",
    "objective": "<campaign_objective>",
    "bid_strategy": "<MAX_BID|COST_PER_RESULT|AUTOBID|UNSET>",
    "bid_micro_amount": <bid>,
    "budget": {"micro_amount": <budget>, "type": "<DAILY|LIFETIME>", "currency": "<ad account billing currency>"},
    "targets": { <SAME_OR_MODIFIED_TARGETS> }
  }'

If the API returns "Min audience threshold was not met" (400), the targeting is too narrow. Pause before creating that ad set. Do not resend the same request, and do not widen the targeting on your own. Ask the user how to broaden it (for example broader targeting or a lower-threshold format), then estimate again with their choice.

Step 4.5: Validate the Clone Against Ad Product Rules

Read and follow $PLUGIN_ROOT/skills/api-reference/references/ad-product-validation.md. Fetch the live catalog once for the clone workflow and validate the complete destination hierarchy, including user modifications, runtime estimates, and referenced assets.

Resolve the product from the new campaign request. Preserve a known source CONTENT or FPMNG product only when the destination request explicitly includes that product; omit ad_product for the default AUCTION flow. Do not validate against the source product while creating a different destination product.

Apply assistant-inferred compliant adjustments before showing the clone plan. Surface only incompatible explicit choices; do not print a per-field checklist.

Step 5: Present Clone Plan

Show the full plan with changes highlighted:

Clone Plan:
Campaign: "Summer Promo (Copy)" (REACH) ← name changed
├── Ad Set: "US 18-34 Audio (Copy)" (AUDIO, $90/day ← was $75, US, ages 18-34, Jul 1 – Jul 31 ← was Jun 1-30)
│   ├── Ad: "30s Spot A" → SHOP_NOW → example.com
│   └── Ad: "30s Spot B" → LEARN_MORE → example.com
└── Ad Set: "US 25-54 Video (Copy)" (VIDEO, $60/day ← was $50, US, ages 25-54, Jul 1 – Jul 31)
    └── Ad: "15s Video" → WATCH_NOW → example.com

Entities to create: 1 campaign, 2 ad sets, 3 ads
Skipped: 1 archived ad ("Old Creative")

Ask for confirmation before executing.

Step 6: Create Drafts Sequentially

Create entities in dependency order, passing IDs forward.

6a. Create campaign

For a destination CONTENT or FPMNG campaign, include that explicit ad_product in the request. For the default AUCTION flow, omit ad_product.

api POST "ad_accounts/{ad_account_id}/drafts/campaigns" \
  '{"name":"Summer Promo (Copy)","delivery_goal_group":"AWARENESS"}'

Copy the source delivery_goal_group when present. If the source only exposes the deprecated objective, map it to delivery_goal_group using the mapping in the drafts skill; do not copy objective into the new draft.

Extract the new campaign id from the response. If this fails, stop — no dependent entities can be created.

6b. Create ad sets (using new campaign_id)

For each source ad set, excluding any the user filtered out:

api POST "ad_accounts/{ad_account_id}/drafts/ad_sets" \
  '{
    "name": "US 18-34 Audio (Copy)",
    "campaign_id": "<NEW_CAMPAIGN_ID>",
    "start_time": "2026-07-01T00:00:00Z",
    "end_time": "2026-07-31T23:59:59Z",
    "budget": {"micro_amount": 90000000, "type": "DAILY"},
    "asset_format": "AUDIO",
    "category": "<SAME_CATEGORY>",
    "targets": { <SAME_OR_MODIFIED_TARGETS> },
    "bid_strategy": "<SAME>",
    "bid_micro_amount": <SAME>,
    "pacing": "<SAME>",
    "delivery": "ON"
  }'

Extract each new ad set id. Map source ad set IDs to new ad set IDs for use in ad creation.

If an ad set creation fails, log the error and skip its ads. Continue with remaining ad sets.

6c. Create ads (using new ad_set_ids)

For each source ad, excluding ARCHIVED/REJECTED, mapped to the correct new ad set:

api POST "ad_accounts/{ad_account_id}/drafts/ads" \
  '{
    "name": "30s Spot A",
    "ad_set_id": "<NEW_AD_SET_ID>",
    "tagline": "<SAME>",
    "advertiser_name": "<SAME>",
    "assets": {
      "asset_id": "<SAME>",
      "logo_asset_id": "<SAME>",
      "companion_asset_id": "<SAME>"
    },
    "call_to_action": {
      "key": "<SAME>",
      "clickthrough_url": "<SAME>"
    },
    "third_party_tracking": <SAME_IF_PRESENT>,
    "delivery": "ON"
  }'

Only include third_party_tracking when it exists on the source ad. Preserve it exactly unless the user explicitly asks to remove or change tracking.

If an ad creation fails, log the error and continue with remaining ads.

Step 7: Validate and Display Summary

Fetch the new draft campaign after all child drafts are created, use its current draft_hierarchy_version, and run VALIDATE. Do not publish as part of cloning.

Clone Staged:
| Entity | Source ID | New ID | Name | Status |
|--------|-----------|--------|------|--------|
| Campaign | abc-123 | def-456 | Summer Promo (Copy) | DRAFT |
| Ad Set 1 | ... | ... | US 18-34 Audio (Copy) | DRAFT |
| ↳ Ad 1 | ... | ... | 30s Spot A | DRAFT |
| ↳ Ad 2 | ... | ... | 30s Spot B | DRAFT |
| Ad Set 2 | ... | ... | US 25-54 Video (Copy) | DRAFT |
| ↳ Ad 3 | ... | ... | 15s Video | DRAFT |

Staged: 1 campaign, 2 ad sets, 3 ads
Skipped: 1 archived ad
Failed: 0
Validation: Passed
Published: No

Clone Ad Set (ad-set <ad_set_id>)

Clone a single ad set and its ads into an existing or new campaign.

Step 1: Read Source

api GET "ad_accounts/{ad_account_id}/ad_sets/$AD_SET_ID"
api GET "ad_accounts/{ad_account_id}/ads?ad_set_ids=$AD_SET_ID&limit=50"

Step 2: Ask for Target Campaign

Ask the user where to place the cloned ad set:

  • Same campaign (default)
  • A different existing campaign (ask for campaign_id, or list campaigns to choose from)

Create or reuse a draft from the selected published target campaign before adding the cloned draft ad set. If a draft already exists, disclose its pending state before adding to it.

Step 3: Ask for Modifications

Same modification options as campaign clone (name, dates, budget, targeting) but applied to the single ad set only.

Step 4: Validate and Present Plan

Apply the same date, asset, budget, and audience checks as the campaign clone. Read and follow $PLUGIN_ROOT/skills/api-reference/references/ad-product-validation.md, fetch the catalog once for this clone workflow, and resolve the target campaign's product. Validate the final new ad set and ads against that destination product before presenting the existing confirmation. Do not print a per-field checklist or add another gate.

Step 5: Execute

Create the draft ad set, then create its draft ads:

# Create ad set
api POST "ad_accounts/{ad_account_id}/drafts/ad_sets" \
  '{...}'
# Create each ad under the new ad set
api POST "ad_accounts/{ad_account_id}/drafts/ads" \
  '{...}'

Step 6: Validate and Display Summary

Fetch the target draft campaign's current version, validate it, and use the same staged summary format as campaign clone, but without a newly cloned campaign row.


Fields Copied from Source

Entity Fields Copied Fields Generated/Modified
Campaign delivery_goal_group, purchase_order name (appended " (Copy)"), new id; map a legacy source objective to delivery_goal_group
Ad Set asset_format, category, targets, bid_strategy, bid_micro_amount, pacing, frequency_caps name, campaign_id, start_time, end_time, budget, new id
Ad tagline, advertiser_name, assets, call_to_action, third_party_tracking name (kept same), ad_set_id, new id

Execution Behavior

  • If auto_execute is true, execute after the user confirms the clone plan. The reading and modification steps always require user interaction.
  • If auto_execute is false, present each curl command and ask for confirmation before executing.
  • Always check the HTTP_STATUS: line from curl output to determine success or failure before interpreting the response body.
  • If campaign creation fails, stop entirely — no ad sets or ads can be created without a campaign.
  • If an ad set creation fails, skip its ads but continue with remaining ad sets. Show what was created and what failed in the summary.
  • If an ad creation fails, continue with remaining ads. Never automatically retry POST requests — the ad may have been created despite the error. Check for it first if retrying manually.
  • Never publish a clone automatically. Publishing is a separate operation and requires explicit confirmation immediately before PUBLISH.
  • Use published creation endpoints only when the user explicitly asks to clone directly to live entities. If a direct write is denied, do not infer that the credentials are read-only; offer the draft clone workflow instead.

Cross-references

  • If no existing campaign to clone, create one from scratch with /spotify-ads-api:build-campaign.
  • After cloning, monitor the new campaign with /spotify-ads-api:dashboard or /spotify-ads-api:monitor.
  • If asset issues are found, check asset status with /spotify-ads-api:assets list or upload new assets with /spotify-ads-api:assets upload.