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.mdplugins/antigravity/markifact/skills/edit-meta-creative/SKILL.md
GitHubGoal
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)
- Account name (substring) — required.
- 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).
- 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.
- Old → new mapping if it's a find-and-replace (e.g. swap
utm_campaign=springforutm_campaign=summer, or replaceoldsite.comwithnewsite.com).
Workflow
- 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. - Inspect each with
get_operation_inputs. - Resolve account via
meta_ads_select_accounts(substring). - Resolve scope to a list of
ad_ids viameta_ads_list_adswith the right filters (campaign, ad set, status). Show the user the ad list and the count before doing anything else. - 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 newcreative_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.
- 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_approvalif approval is required. - 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_*_adop (e.g.meta_ads_create_single_image_ad) withcreative_only=true. That uploads the new media and returns acreative_idwithout creating a new ad. Then callmeta_ads_replace_ad_creativeto attach it. - Don't try to upload media inside the patched payload —
create_ad_creativewon't process it.
Platform-specific rules the model MUST respect
- Same
ad_idsurvives 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_attachmentsarray — 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_dataand 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_creativerejects payload → most common cause is referencing media not in this account. Ask user to upload the asset to the account first or use the dedicatedcreate_*_adop withcreative_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."