fix: fall back to Facebook-only when ad account can't use the page's Instagram
Meta rejects the whole ad creative with error 200/1815199 when the ad account isn't authorized (in Business Manager) to use the Instagram account linked to the chosen page. Picking a different page in the new selector doesn't help if that page's IG is also unshared. Now, when the creative fails for that specific reason, we retry without instagram_actor_id so the ad still publishes on Facebook, and the modal explains why Instagram was dropped and how to enable it. createFullCampaign gets the same fallback for free. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
dcaf7959ac
commit
a88e96adf5
2 changed files with 49 additions and 18 deletions
|
|
@ -80,6 +80,7 @@ export function AddAdModal({
|
||||||
const [publishing, setPublishing] = useState(false);
|
const [publishing, setPublishing] = useState(false);
|
||||||
const [error, setError] = useState<FriendlyError | string | null>(null);
|
const [error, setError] = useState<FriendlyError | string | null>(null);
|
||||||
const [done, setDone] = useState(false);
|
const [done, setDone] = useState(false);
|
||||||
|
const [instagramDropped, setInstagramDropped] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
|
|
@ -271,6 +272,7 @@ export function AddAdModal({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setInstagramDropped(Boolean(data.data?.instagram_dropped));
|
||||||
setDone(true);
|
setDone(true);
|
||||||
onCreated();
|
onCreated();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
@ -301,6 +303,13 @@ export function AddAdModal({
|
||||||
<p className="text-xs" style={{ color: 'var(--muted-fg)' }}>
|
<p className="text-xs" style={{ color: 'var(--muted-fg)' }}>
|
||||||
Ele foi criado como Pausado — ative-o no Ads Manager (ou na tabela) quando estiver pronto.
|
Ele foi criado como Pausado — ative-o no Ads Manager (ou na tabela) quando estiver pronto.
|
||||||
</p>
|
</p>
|
||||||
|
{instagramDropped && (
|
||||||
|
<div className="text-[11px] mt-1 px-3 py-2 rounded-lg text-left" style={{ background: 'rgba(234,179,8,0.08)', border: '1px solid rgba(234,179,8,0.2)', color: '#fcd34d' }}>
|
||||||
|
⚠️ Publicamos <strong>só no Facebook</strong>: a conta de anúncios ainda não tem permissão pra usar
|
||||||
|
{selectedInstagramAccount ? <> a conta <strong>@{selectedInstagramAccount.username}</strong></> : ' a conta do Instagram'} desta página.
|
||||||
|
Para ativar o Instagram, vincule a conta no Business Manager (Configurações do Negócio → Contas do Instagram → Atribuir conta de anúncios) e republique.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<button onClick={onClose} className="btn-primary mt-2">Fechar</button>
|
<button onClick={onClose} className="btn-primary mt-2">Fechar</button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -608,12 +608,16 @@ export async function addAdToAdSet(client: Client, params: AddAdToAdSetParams) {
|
||||||
// the most broadly compatible across placements.
|
// the most broadly compatible across placements.
|
||||||
const primaryImageHash = imageHashes[0];
|
const primaryImageHash = imageHashes[0];
|
||||||
|
|
||||||
let creative;
|
// Monta o criativo com (ou sem) o Instagram. Quando a conta de anúncios não
|
||||||
|
// tem permissão pra usar a conta de IG vinculada à página, a Meta recusa o
|
||||||
|
// criativo inteiro (erro 200 / subcode 1815199). Nesse caso refazemos o
|
||||||
|
// criativo só com o Facebook, em vez de barrar a publicação.
|
||||||
|
const buildCreative = (instagramActorId?: string) => {
|
||||||
if (params.is_whatsapp) {
|
if (params.is_whatsapp) {
|
||||||
creative = await createWhatsappAdCreative(client, {
|
return createWhatsappAdCreative(client, {
|
||||||
name: creativeName,
|
name: creativeName,
|
||||||
page_id: params.page_id,
|
page_id: params.page_id,
|
||||||
instagram_actor_id: params.instagram_actor_id,
|
instagram_actor_id: instagramActorId,
|
||||||
image_hash: primaryImageHash,
|
image_hash: primaryImageHash,
|
||||||
message: params.primary_text,
|
message: params.primary_text,
|
||||||
headline: params.headline,
|
headline: params.headline,
|
||||||
|
|
@ -623,11 +627,11 @@ export async function addAdToAdSet(client: Client, params: AddAdToAdSetParams) {
|
||||||
whatsapp_phone_number: params.whatsapp_phone_number,
|
whatsapp_phone_number: params.whatsapp_phone_number,
|
||||||
ad_account_id: params.ad_account_id,
|
ad_account_id: params.ad_account_id,
|
||||||
});
|
});
|
||||||
} else {
|
}
|
||||||
creative = await createAdCreative(client, {
|
return createAdCreative(client, {
|
||||||
name: creativeName,
|
name: creativeName,
|
||||||
page_id: params.page_id,
|
page_id: params.page_id,
|
||||||
instagram_actor_id: params.instagram_actor_id,
|
instagram_actor_id: instagramActorId,
|
||||||
image_hash: primaryImageHash,
|
image_hash: primaryImageHash,
|
||||||
link: params.link,
|
link: params.link,
|
||||||
message: params.primary_text,
|
message: params.primary_text,
|
||||||
|
|
@ -636,6 +640,23 @@ export async function addAdToAdSet(client: Client, params: AddAdToAdSetParams) {
|
||||||
call_to_action_type: params.cta,
|
call_to_action_type: params.cta,
|
||||||
ad_account_id: params.ad_account_id,
|
ad_account_id: params.ad_account_id,
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1815199: "A conta de anúncios não tem acesso a esta conta do Instagram".
|
||||||
|
const isInstagramPermissionError = (err: unknown) =>
|
||||||
|
err instanceof Error && /1815199/.test(err.message);
|
||||||
|
|
||||||
|
let creative;
|
||||||
|
let instagramDropped = false;
|
||||||
|
try {
|
||||||
|
creative = await buildCreative(params.instagram_actor_id);
|
||||||
|
} catch (err) {
|
||||||
|
if (params.instagram_actor_id && isInstagramPermissionError(err)) {
|
||||||
|
creative = await buildCreative(undefined);
|
||||||
|
instagramDropped = true;
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const creativeId = (creative as any)?.id;
|
const creativeId = (creative as any)?.id;
|
||||||
|
|
@ -659,6 +680,7 @@ export async function addAdToAdSet(client: Client, params: AddAdToAdSetParams) {
|
||||||
adset_id: params.adset_id,
|
adset_id: params.adset_id,
|
||||||
creative_id: creativeId,
|
creative_id: creativeId,
|
||||||
ad_id: adId,
|
ad_id: adId,
|
||||||
|
instagram_dropped: instagramDropped,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue