async def update_ad_creative(
ad_creative_id: str,
meta_access_token: Optional[str] = None,
name: Optional[str] = None,
primary_text: Optional[str] = None,
primary_text_variants: Optional[List[str]] = None,
headline_text: Optional[str] = None,
headline_variants: Optional[List[str]] = None,
description_text: Optional[str] = None,
description_variants: Optional[List[str]] = None,
optimization_type: Optional[str] = None,
dynamic_creative_spec: Optional[Dict[str, Any]] = None,
call_to_action_type: Optional[str] = None,
lead_form_id: Optional[str] = None,
ad_formats: Optional[List[str]] = None,
) -> str:
if not ad_creative_id:
return _json({"error": "No creative ID provided"})
primary_text_variants = _normalize_list_argument(primary_text_variants)
headline_variants = _normalize_list_argument(headline_variants)
description_variants = _normalize_list_argument(description_variants)
ad_formats = _normalize_list_argument(ad_formats)
attempted_content_fields = [
field
for field, value in {
"primary_text": primary_text,
"primary_text_variants": primary_text_variants,
"headline_text": headline_text,
"headline_variants": headline_variants,
"description_text": description_text,
"description_variants": description_variants,
"call_to_action_type": call_to_action_type,
"lead_form_id": lead_form_id,
}.items()
if value is not None
]
if attempted_content_fields:
return _json(
{
"error": "Content updates are not allowed on existing creatives",
"explanation": (
"The Meta API does not allow updating content fields (primary_text, headline_text, description_text, CTA, image, video, URL) "
"on existing creatives."
),
"workaround": (
"Create a new creative via create_ad_creative, then call update_ad with the new ad_creative_id."
),
"ad_creative_id": ad_creative_id,
"attempted_content_fields": attempted_content_fields,
}
)
if optimization_type and optimization_type != "DEGREES_OF_FREEDOM":
return _json({"error": f"Invalid optimization_type '{optimization_type}'. Only 'DEGREES_OF_FREEDOM' is supported."})
update_payload: Dict[str, Any] = {}
if name:
update_payload["name"] = name
if optimization_type or dynamic_creative_spec or ad_formats:
resolved_update_formats = list(ad_formats) if ad_formats else (
["AUTOMATIC_FORMAT"] if optimization_type == "DEGREES_OF_FREEDOM" else ["SINGLE_IMAGE"]
)
feed: Dict[str, Any] = {
"ad_formats": resolved_update_formats,
}
if optimization_type:
feed["optimization_type"] = optimization_type
update_payload["asset_feed_spec"] = feed
if dynamic_creative_spec:
update_payload["dynamic_creative_spec"] = dynamic_creative_spec
if not update_payload:
return _json({"error": "No update parameters provided"})
try:
result = await make_api_request(ad_creative_id, meta_access_token, update_payload, method="POST")
except Exception as exc: # noqa: BLE001
return _json(
{
"error": "Failed to update ad creative",
"details": str(exc),
"update_data_sent": update_payload,
}
)
if isinstance(result, dict) and result.get("id"):
details = await make_api_request(
ad_creative_id,
meta_access_token,
{
"fields": (
"id,name,status,thumbnail_url,image_url,image_hash,object_story_spec,"
"url_tags,link_url,dynamic_creative_spec"
)
},
)
return _json({"success": True, "ad_creative_id": ad_creative_id, "details": details})
error_obj = result.get("error", {}) if isinstance(result, dict) else {}
details = error_obj.get("details", {}) if isinstance(error_obj, dict) else {}
inner = details.get("error", {}) if isinstance(details, dict) else {}
error_subcode = inner.get("error_subcode") if isinstance(inner, dict) else error_obj.get("error_subcode")
if error_subcode == 1815573:
return _json(
{
"error": "Content updates are not allowed on existing creatives",
"explanation": (
"The Meta API does not allow updating content fields (primary_text, headline_text, description_text, CTA, image, video, URL) "
"on existing creatives."
),
"workaround": (
"Create a new creative via create_ad_creative, then call update_ad with the new ad_creative_id."
),
"ad_creative_id": ad_creative_id,
"attempted_updates": update_payload,
}
)
return _json(result if isinstance(result, dict) else {"data": result})