edit-meta-creative

Edit a Meta ad's creative content — landing page URL, primary text, headline, description, CTA, link description — across one ad or many. Use when the user says "change the URL on my ads", "update the

SKILL.md

GitHub

Goal

Apply a creative-content change to one or more existing Meta ads without losing learning, frequency caps, ad post engagement, or the ad ID. The same ad_id keeps running with a new creative attached.

The Meta reality this command exists to handle

Meta does not allow in-place edits of creative content (URL, text, headline, image, CTA). The only way to change an existing ad's creative is:

get_ad_creative → modify payload locally → create_ad_creative → replace_ad_creative

Anything else (deleting + recreating the ad) destroys learning, frequency capping, social proof (likes/comments), and breaks any link people have to the ad post. Always use replace_ad_creative — never delete-and-recreate for a creative change.

Inputs to confirm (batch)

  1. Account name (substring) — required.
  2. Scope — required. One of:
    • Single ad ID(s).
    • All ads in a campaign / ad set (by name or ID).
    • All active ads in the account matching a filter (e.g. status=ACTIVE).
  3. What to change — required. Examples:
    • Landing page URL (the most common bulk edit).
    • Primary text / headline / description / link description.
    • Call-to-action button.
    • Display link / deep link.
  4. Old → new mapping if it's a find-and-replace (e.g. swap utm_campaign=spring for utm_campaign=summer, or replace oldsite.com with newsite.com).

Workflow

  1. Discover ops: meta_ads_select_accounts, meta_ads_list_ads, meta_ads_list_adsets, meta_ads_list_campaigns, meta_ads_get_ad_creative, meta_ads_create_ad_creative, meta_ads_replace_ad_creative.
  2. Inspect each with get_operation_inputs.
  3. Resolve account via meta_ads_select_accounts (substring).
  4. Resolve scope to a list of ad_ids via meta_ads_list_ads with the right filters (campaign, ad set, status). Show the user the ad list and the count before doing anything else.
  5. For each ad in scope: a. meta_ads_get_ad_creative(ad_id) → returns the raw creative payload. b. Modify the payload locally based on the requested change. Preserve all fields the user did not ask to change. Common fields to patch:
    • object_story_spec.link_data.link (URL for link/single-image ads)
    • object_story_spec.link_data.message (primary text)
    • object_story_spec.link_data.name (headline)
    • object_story_spec.link_data.description (link description)
    • object_story_spec.link_data.call_to_action.value.link (CTA link)
    • object_story_spec.link_data.call_to_action.type (CTA button type, e.g. LEARN_MORE, SHOP_NOW)
    • For video ads: object_story_spec.video_data.*
    • For carousel: each item under object_story_spec.link_data.child_attachments[] c. meta_ads_create_ad_creative(modified_payload) → returns new creative_id. Only works when the existing media (image_hash / video_id) is reused. If the user is also swapping in fresh media that isn't already in Meta, see "Fresh media" below. d. meta_ads_replace_ad_creative(ad_id, creative_id) → attaches the new creative.
  6. Build a diff preview before executing. Show the user a table of every ad with old → new for each changed field. Wait for explicit confirmation. Then run all replaces, batched into a single request_human_approval if approval is required.
  7. Confirm: count succeeded, count failed (with reason), list of new creative_ids.

Use safe-write-operations for steps 5c, 5d, and 6.

Fresh media (image/video the user wants to swap in)

meta_ads_create_ad_creative expects media that already exists in Meta as image_hash or video_id. If the user wants brand-new media:

  • Easiest path: use the dedicated meta_ads_create_*_ad op (e.g. meta_ads_create_single_image_ad) with creative_only=true. That uploads the new media and returns a creative_id without creating a new ad. Then call meta_ads_replace_ad_creative to attach it.
  • Don't try to upload media inside the patched payload — create_ad_creative won't process it.

Platform-specific rules the model MUST respect

  • Same ad_id survives the replace. Frequency caps, learning, social proof (likes/comments/shares), permalinks — all preserved. Tell the user this.
  • Replace is one creative per ad. You cannot attach the same new creative to many ads in one call — loop one ad at a time.
  • Significant creative changes can soft-reset learning. Meta usually keeps the ad in learning if the creative changes substantially (e.g. new image + new copy). URL-only and minor copy changes typically don't trigger a reset. Warn the user when the change is major.
  • Don't delete-then-recreate. That loses everything and is what this command exists to prevent.
  • Carousel ads: the modification has to handle the child_attachments array — patch the right index, don't replace the whole array unless the user wants every card changed.
  • Catalog (DPA) ads: the creative is template-driven from the product catalog — to change URLs you usually edit the catalog feed, not the creative. Check if the ad uses template_data and route the user to fix the feed instead.
  • Some fields are not editable even via this workflow (e.g. object_story_spec.page_id — to change Page you must create a new ad). If the user asks for that, say so.

Failure modes & recoveries

  • create_ad_creative rejects payload → most common cause is referencing media not in this account. Ask user to upload the asset to the account first or use the dedicated create_*_ad op with creative_only=true.
  • Ad uses template_data (catalog/DPA) → tell user URLs come from the product feed; offer to help update the catalog feed instead.
  • Replace fails for a single ad in a batch → continue with the rest, report failures separately at the end.

Output to user

Before execute — diff table:

Ad Field Before After
Spring Promo - Image 1 (123) link …/spring …/summer
Spring Promo - Image 2 (124) link …/spring …/summer

After execute — result:

Status Count
Replaced <n>
Skipped (template feed) <n>
Failed <n>

End with: "All updated ads keep their original ad IDs, frequency caps, and engagement. No learning reset is expected for URL-only changes."