POST /v1/ads/create · Meta · API v1 (spec v1.0.4) · observed 2026-08-13
On the attach shape (adSetId set), translations[] + defaultLocale pass your request validation (your distinctness validator fires on bad input — see the litmus below), the call returns 201, and the resulting Meta creative is built without the language asset_feed_spec: a plain single-language object_story_spec creative. No warning, no error — the multilingual intent is silently lost.
The identical creative surface on the standalone shape (no adSetId, no existingCampaignId) and on the composite shape (existingCampaignId) produces the correct Meta Dynamic Language Optimization creative. Verified across 18 ads on act_2191164328122008 (Meta Graph read of each ad's creative).
Expected: apply translations[] on attach
POST /v1/ads/create (media URLs are presigned R2 GETs, truncated here; full copies available on request):
{
"accountId": "6a7d9c5177555aae0159048e",
"adAccountId": "act_2191164328122008",
"name": "ZRN I18N VERIFY attach msrmz4y0",
"adName": "ZRN I18N VERIFY attach msrmz4y0",
"adSetId": "120253676856890536",
"status": "PAUSED",
"tracking": { "pixelId": "1177143927405920" },
"headline": "Replace the Scroll",
"body": "Still scrolling at 1am? Sutara replaces your screen with a story. Buddhist bedtime tales, narrated with rain and wind chimes. 7 minutes. Eyes closed. Your last thought tonight doesn't have to be a headline.",
"description": "Replace the Scroll · en",
"callToAction": "DOWNLOAD",
"linkUrl": "https://link.sutara.app/link/d4a9b2a7-9b3d-4862-a5bd-d2168f141f70?utm-content=2_SU_EN&...",
"video": { "url": "https://pi-agent....r2.cloudflarestorage.com/sutara/sutara/creatives/CRV-0045/v1/en/9x16.mp4?X-Amz-..." },
"defaultLocale": "en",
"translations": [
{
"locale": "es",
"headline": "Reemplaza el Scroll",
"body": "¿Sigues deslizando a la 1am? Sutara reemplaza tu pantalla con una historia. Cuentos budistas para dormir, narrados con lluvia y campanillas. 7 minutos. Ojos cerrados. Tu último pensamiento esta noche no tiene que ser un titular.",
"description": "Reemplaza el Scroll · es",
"linkUrl": "https://link.sutara.app/link/d4a9b2a7-9b3d-4862-a5bd-d2168f141f70?utm-content=2_SU_ES&...",
"videoUrl": "https://pi-agent....r2.cloudflarestorage.com/sutara/sutara/creatives/CRV-0045/v1/es/9x16.mp4?X-Amz-..."
},
{
"locale": "it",
"headline": "Sostituisci lo Scroll",
"body": "Ancora a scorrere all'1 di notte? Sutara sostituisce lo schermo con una storia. Racconti buddisti per dormire, narrati con pioggia e campanelli. 7 minuti. Occhi chiusi. Il tuo ultimo pensiero non deve essere un titolo di giornale.",
"description": "Sostituisci lo Scroll · it",
"linkUrl": "https://link.sutara.app/link/d4a9b2a7-9b3d-4862-a5bd-d2168f141f70?utm-content=2_SU_IT&...",
"videoUrl": "https://pi-agent....r2.cloudflarestorage.com/sutara/sutara/creatives/CRV-0045/v1/it/9x16.mp4?X-Amz-..."
},
{
"locale": "pt",
"headline": "Substitua o Scroll",
"body": "Ainda rolando às 1h da manhã? Sutara substitui sua tela por uma história. Contos budistas para dormir, narrados com chuva e sinos. 7 minutos. Olhos fechados. Seu último pensamento não precisa ser uma manchete.",
"description": "Substitua o Scroll · pt",
"linkUrl": "https://link.sutara.app/link/d4a9b2a7-9b3d-4862-a5bd-d2168f141f70?utm-content=2_SU_PT&...",
"videoUrl": "https://pi-agent....r2.cloudflarestorage.com/sutara/sutara/creatives/CRV-0045/v1/pt/9x16.mp4?X-Amz-..."
}
]
}
Result: 201, message "Creative attached to ad set and submitted for review", ad 120253678109620536, creative 1594060508857079.
Meta Graph read of that creative (GET /1594060508857079?fields=asset_feed_spec,object_story_spec):
{
"id": "1594060508857079",
"object_story_spec": {
"video_data": {
"title": "Replace the Scroll",
"link_description": "Replace the Scroll · en"
}
}
}
No asset_feed_spec. English only. All three translations gone.
Same body with the attach addressing removed and a minimal paused tree added instead (campaignName/goal: "conversions"/optimizationGoal: "OFFSITE_CONVERSIONS"/ budgetAmount: 10/budgetType: "daily"/countries: ["US"]/ promotedObject: { "pixelId": "1177143927405920", "customEventType": "PURCHASE" } — no adSetId, no existingCampaignId). Every copy/media/translations field byte-identical to Repro A.
Result: 201, ad 120253678130730536, creative 1091962366835035.
Meta Graph read of that creative: full language feed —
{
"id": "1091962366835035",
"asset_feed_spec": {
"videos": [
{ "adlabels": [{ "name": "default" }] },
{ "adlabels": [{ "name": "lang_0" }] },
{ "adlabels": [{ "name": "lang_1" }] },
{ "adlabels": [{ "name": "lang_2" }] }
],
"titles": [
{ "adlabels": [{ "name": "default" }], "text": "Replace the Scroll" },
{ "adlabels": [{ "name": "lang_0" }], "text": "Reemplaza el Scroll" },
{ "adlabels": [{ "name": "lang_1" }], "text": "Sostituisci lo Scroll" },
{ "adlabels": [{ "name": "lang_2" }], "text": "Substitua o Scroll" }
],
"asset_customization_rules": [ { "customization_spec": { "locales": [7, 23, "..."] } } ]
}
}
(The composite shape — same surface + existingCampaignId — also produces the feed: e.g. ads 120253676889490536 → creative 1075364648357004, 120253678205210536 → creative 1931618354171673.)
translations[] before discarding itSame attach body as Repro A but with translations[1] (it) carrying the same headline/body/description strings as translations[0] (es):
Result: 400
{
"error": "translations[1] (locale \"it\") has the same \"headline\" as translations[0] (locale \"es\"). Meta collapses identical text into one asset, which makes the language feed fail with a misleading \"too few texts\" error — every locale needs a distinct headline.",
"type": "invalid_request_error",
"code": "invalid_field_value",
"param": "translations[1].headline"
}
So the attach pipeline parses and validates translations[] — and then the creative builder ignores it. (The standalone shape returns the identical 400 on the same input.)
Every multi-ad multilingual ad set built via your documented pattern — "create the first ad via the normal shape, then attach the rest one call each" (your adSetId field docs) — ends up with a multilingual first ad and single-language siblings, with no signal to the caller. We shipped a production launch this way before catching it via Meta Graph inspection.
Apply translations[]/defaultLocale on the attach shape (preferred — the validation half already exists there), or reject them with a 400 like other unsupported attach fields. Please also update the adSetId field description either way: it currently enumerates the attach creative surface (headline/body/description/callToAction
imageUrl/video OR placementAssets) without saying translations is ignored.
Please authenticate to join the conversation.
Completed
Feature Request
29 days ago

Fedir Bobylev
Get notified by email when there are changes.
Completed
Feature Request
29 days ago

Fedir Bobylev
Get notified by email when there are changes.