Documentation

Configuration des annonces

Référence complète du CLI

Voici la référence complète du CLI d'Ads Uploader. Pour une introduction et un guide de démarrage, consulte Configuration par CLI.

Commandes

Authentification

CommandeCe qu'elle fait
ads loginS'authentifie via le navigateur (ouvre ton navigateur par défaut)
ads logoutEfface les identifiants stockés
ads whoamiAffiche l'utilisateur actuellement connecté
ads configAffiche la configuration (compte, URL de l'API, chemin des identifiants)
CommandeCe qu'elle fait
ads accountsListe tous les comptes publicitaires connectés à ton compte Meta
ads account <id>Définit un compte publicitaire par défaut pour les commandes futures
ads pagesListe les pages Facebook avec lesquelles tu peux faire de la publicité, y compris tout compte Instagram lié, à utiliser avec les substitutions de profil
ads campaignsListe les campagnes actives
ads campaigns --status allInclut les campagnes en pause et archivées
ads campaigns --search "text"Filtre les campagnes par nom
ads campaign <id>Affiche les ensembles d'annonces d'une campagne
ads adsets --campaign <id>Liste les ensembles d'annonces d'une campagne (prend en charge --search, --status)
ads adset <id>Affiche les annonces d'un ensemble d'annonces
ads ad <id>Affiche les détails complets de l'annonce, y compris les paramètres du créatif
ads presetsListe tes préréglages d'API enregistrés
ads presets <id>Affiche les détails d'un préréglage spécifique
ads presets:save --from-ad <adId> --name "Preset Name"Enregistre une annonce existante comme préréglage d'API
ads text-presetsListe tes préréglages de texte enregistrés
ads text-presets <id>Affiche les détails d'un préréglage de texte spécifique
ads uploadsListe les imports récents
ads uploads <batchId>Affiche les détails de l'import (fichiers, variantes, hachages)

Import de médias

CommandeCe qu'elle fait
ads upload <files...>Importe des images et des vidéos vers ton compte publicitaire
ads upload ./directory/Importe un répertoire entier

Les fichiers sont préparés en parallèle, et les pannes réseau transitoires sont réessayées automatiquement avec backoff. Tu as rarement besoin d'y toucher, mais ils sont disponibles :

Flag d'importDescription
--concurrency <n>Nombre de fichiers préparés en parallèle, 1-6 (par défaut : 4). Les grandes vidéos sont automatiquement limitées pour rester dans la mémoire.
--upload-timeout <ms>Délai d'expiration d'import par fichier (par défaut : 120000)
--api-timeout <ms>Délai d'expiration de la requête API en millisecondes (par défaut : 60000)

Création d'annonces

CommandeCe qu'elle fait
ads create spec.jsonCrée des annonces à partir d'un fichier de spécification
ads create:preview spec.jsonExécution à blanc montrant ce qui serait créé
ads create:interactiveAssistant guidé (accepte tous les flags de création)

Gestion des tâches

CommandeCe qu'elle fait
ads jobs <jobId>Vérifie le statut d'une tâche
ads jobs <jobId> --followDiffuse les mises à jour de progression en direct
ads jobs cancel <jobId>Annule une tâche en cours

Flags de création

Ces flags s'appliquent à ads create, ads create:preview et ads create:interactive. Ils peuvent être utilisés à la place d'un fichier de spécification ou avec lui.

FlagDescription
--account <id>Remplace le compte publicitaire par défaut
--preset <id>Utilise un préréglage d'API enregistré (alternative au fichier de spécification)
--text-preset <id>Charge un préréglage de texte enregistré
--copy-from <adId>Copie les paramètres d'une annonce existante
--upload <batchId>Spécifie l'ID de l'import
--status <PAUSED|ACTIVE>Définit le statut de l'annonce (par défaut : ACTIVE)
--pause-at <level>Niveau de mise en pause : ad (par défaut), adSet ou campaign
--daily-budget <amount>Remplace le budget quotidien par ensemble d'annonces (unités monétaires, p. ex. 50 pour 50 $)
--bid-amount <amount>Remplace l'enchère/plafond de coût par ensemble d'annonces (unités monétaires)
--page <id>Utilise cette page Facebook au lieu de celle du modèle (voir Options de profil)
--instagram <id>Utilise ce compte Instagram au lieu de celui du modèle
--threads <id>Utilise ce profil Threads au lieu de celui du modèle
--text-file <path>Charge la configuration de texte depuis un fichier JSON
--expandedAffiche les valeurs complètes du titre, du texte principal et de la description dans les aperçus

Flags de navigation

Ces flags sont disponibles sur campaigns, adsets, adset et campaign :

FlagDescription
--status <status>active (par défaut) ou all
--inactiveRaccourci pour --status all (sur campaigns)
--search <text>Filtre par nom (sur campaigns, adsets)

Flags de détail

Ces flags sont disponibles sur ad :

FlagDescription
--expandedAffiche les valeurs complètes du titre, du texte principal et de la description

Flags communs

FlagDescription
--account <id>Remplace le compte publicitaire par défaut pour toute commande
--jsonRenvoie du JSON brut (disponible sur la plupart des commandes, destiné au scripting)

Format du fichier de spécification

Le fichier de spécification JSON contrôle chaque aspect de la création d'annonces. Seuls deux champs sont obligatoires : une source de modèle (adPresetId ou copyFromAd) et uploadId.

Spécification minimale

{
  "adPresetId": "your_preset_id",
  "uploadId": "batch_abc123"
}

Exemple complet

{
  "adPresetId": "preset_id_here",
  "uploadId": "batch_abc123",
  "adSet": {
    "name": "My New Ad Set",
    "dailyBudget": 50
  },
  "adNamePattern": "{filename}",
  "texts": {
    "perAd": {
      "hero.jpg": {
        "headlines": ["Main Headline"],
        "bodies": ["Ad copy here."],
        "descriptions": ["Short description"],
        "cta": "SHOP_NOW",
        "link": "https://example.com/landing"
      }
    }
  },
  "creativeEnhancements": "none",
  "options": {
    "status": "PAUSED",
    "pauseAt": "adSet"
  }
}

Source de modèle

Il t'en faut une pour indiquer au CLI quelle configuration d'annonce utiliser comme base.

ChampDescription
adPresetIdL'ID d'un préréglage d'API enregistré. Fige la campagne, l'ensemble d'annonces et la configuration de l'annonce.
copyFromAdUn ID d'annonce Facebook dont copier les paramètres.

Lorsque tu utilises copyFromAd, fournis l'import et, facultativement, la campagne et l'ensemble d'annonces :

{
  "copyFromAd": "120233848667930472",
  "uploadId": "batch_abc123",
  "campaign": { "id": "120233848666410472" },
  "adSet": { "id": "120233848666620472" }
}

Pour trouver le bon ID d'annonce, navigue dans ton compte : ads campaigns puis ads campaign <id> puis ads adset <id> puis ads ad <id>.

Options de profil

Par défaut, les nouvelles annonces héritent de la page Facebook, du compte Instagram et du profil Threads de l'annonce modèle ou du préréglage. C'est le même contrôle Options de profil disponible via le panneau Valeurs par défaut dans l'application web. Remplace l'un d'eux par un bloc profile (ou par les flags --page / --instagram / --threads, qui ont priorité sur le fichier de spécification) :

{
  "copyFromAd": "120233848667930472",
  "uploadId": "batch_abc123",
  "profile": {
    "pageId": "123456789012345",
    "instagramId": "17841400000000000",
    "threadsId": "987654321098765"
  }
}

Exécute ads pages pour lister les ID de page que tu peux utiliser, ainsi que le compte Instagram lié à chaque page.

Important : si tu remplaces uniquement la page, les profils Instagram et Threads sont réinitialisés (ils appartenaient à l'ancienne page), ils ne sont pas reportés. Pour changer de page et conserver un profil Instagram ou Threads spécifique, définis-les explicitement. Tu peux aussi changer uniquement le profil Instagram ou Threads sans toucher à la page, en ne définissant que ces champs.

Les profils par campagne pour les lancements multicampagnes ne sont pas encore pris en charge dans les spécifications du CLI.

Structure de la campagne

Par défaut, les annonces vont dans la campagne de l'annonce modèle. Tu peux créer une nouvelle campagne en fournissant campaign.name.

Pour les modes multicampagnes, utilise campaign.mode avec un tableau campaigns :

{
  "campaign": {
    "mode": "duplicate",
    "campaigns": [
      { "name": "Campaign A" },
      { "name": "Campaign B" }
    ]
  }
}
ModeComportement
"single"Par défaut. Une seule campagne.
"duplicate"Tous les médias sont dupliqués dans chaque campagne.
"split"Les médias sont répartis équitablement entre les campagnes.

Modes d'ensemble d'annonces

Par défaut, les annonces vont dans l'ensemble d'annonces existant de l'annonce modèle. Les modes suivants te donnent le contrôle sur la façon dont les annonces sont réparties entre les ensembles d'annonces.

Créer un nouvel ensemble d'annonces :

{ "adSet": { "name": "My Ad Set" } }

Utiliser un ensemble d'annonces existant par ID :

{ "adSet": { "id": "120233848666620472" } }

Un ensemble d'annonces par fichier importé :

{ "adSet": { "mode": "perUpload" } }

Regroupement automatique en ensembles d'annonces de taille fixe :

{ "adSet": { "mode": "autoGroup", "adsPerAdSet": 5 } }

Groupes personnalisés avec contrôle total sur quels fichiers vont où :

{
  "adSet": {
    "groups": [
      { "name": "Images - April 10", "media": ["hero.jpg", "banner.jpg"] },
      { "name": "Videos - April 10", "media": ["promo.mp4"] }
    ]
  }
}

Modèle de nommage des ensembles d'annonces pour les modes à plusieurs ensembles d'annonces :

{ "adSet": { "mode": "perUpload", "namePattern": "Ad Set {index:01}" } }

Le regroupement par variantes regroupe les annonces par identifiant de variation dans le même ensemble d'annonces :

{ "adSet": { "mode": "autoGroup", "groupVariations": true, "variationIdentifier": "-" } }

Remplacement du budget et de l'enchère

Remplace le budget quotidien et/ou le montant de l'enchère sur les nouveaux ensembles d'annonces. Les valeurs sont dans les unités monétaires de ton compte (p. ex. 50 pour 50 $ ou 50 euros).

dailyBudget et bidAmount sont des champs Meta indépendants :

  • Campagnes ABO (le budget est sur l'ensemble d'annonces) : tu peux définir dailyBudget, bidAmount ou les deux. Les stratégies à plafond d'enchère comme COST_CAP, LOWEST_COST_WITH_BID_CAP et TARGET_COST nécessitent un bidAmount en plus du budget.
  • Campagnes CBO (le budget est sur la campagne) : ne définis pas dailyBudget sur l'ensemble d'annonces, Meta le rejette car le budget provient déjà de la campagne. Pour les stratégies à plafond d'enchère, définis uniquement bidAmount.
{ "adSet": { "dailyBudget": 50 } }
{ "adSet": { "bidAmount": 5 } }
{ "adSet": { "dailyBudget": 50, "bidAmount": 5 } }

Également disponible comme flags du CLI : --daily-budget 50, --bid-amount 5 ou les deux.

Configuration de texte

Le texte commun applique le même texte à toutes les annonces :

{
  "texts": {
    "common": {
      "headlines": ["Headline 1", "Headline 2"],
      "bodies": ["Primary text"],
      "descriptions": ["Description"]
    },
    "strategy": "flexible"
  }
}

Le texte par annonce te permet de définir un texte unique pour chaque fichier :

{
  "texts": {
    "perAd": {
      "hero.jpg": {
        "headlines": ["Hero Headline"],
        "bodies": ["Hero copy"],
        "descriptions": ["Hero desc"],
        "cta": "LEARN_MORE",
        "link": "https://example.com/hero",
        "urlTags": "utm_content=hero"
      },
      "banner.jpg": {
        "headlines": ["Banner Headline"],
        "bodies": ["Banner copy"]
      }
    }
  }
}

Les clés par annonce sont des noms de fichier (pas des chemins complets). Chaque entrée prend en charge : headlines, bodies, descriptions, cta, link, displayUrl, urlTags. Les champs que tu ne spécifies pas héritent de l'annonce modèle.

Les préréglages de texte te permettent de charger une configuration de texte enregistrée :

{ "textPresetId": "preset_id_here" }

Tu ne peux pas combiner textPresetId avec texts.

Les options de stratégie contrôlent la façon dont plusieurs variations de texte sont gérées :

  • "flexible" (par défaut) laisse Meta optimiser parmi tes variations de texte. Plusieurs titres et textes deviennent des options que Facebook combine librement.
  • "separate" crée une annonce distincte pour chaque combinaison de texte.

CTA et liens

Un CTA de niveau supérieur s'applique à toutes les annonces. Les CTA par annonce dans texts.perAd le remplacent.

{
  "cta": {
    "type": "SHOP_NOW",
    "link": "https://example.com",
    "displayUrl": "example.com"
  },
  "urlTags": "utm_source=facebook&utm_medium=paid"
}

Types de CTA standard : LEARN_MORE, SHOP_NOW, SIGN_UP, SUBSCRIBE, GET_OFFER, CONTACT_US, DOWNLOAD, ORDER_NOW, BUY_NOW, BOOK_NOW, APPLY_NOW, GET_QUOTE, GET_IN_TOUCH, WATCH_MORE

Les CTA spécifiques à l'objectif sont hérités de l'annonce modèle et ne doivent pas être définis manuellement. Les définir sur le mauvais type de campagne provoquera une erreur de l'API Facebook.

CTAObjectif de campagne requis
MESSAGE_PAGEDestination Messenger
WHATSAPP_MESSAGEDestination WhatsApp
INSTAGRAM_MESSAGEDestination DM Instagram
CALL_NOWCampagne d'appel

Test fractionné d'URL (destination fractionnée)

Fournis de 2 à 5 URL de destination sous texts.urlVariants et chaque ensemble d'annonces généré est dupliqué une fois par URL pour que Meta optimise chaque combinaison d'annonce et de page de destination indépendamment.

{
  "texts": {
    "common": { "headlines": ["Hero"], "bodies": ["Copy"] },
    "urlVariants": [
      { "link": "https://example.com/homepage", "label": "homepage" },
      { "link": "https://example.com/quiz", "label": "quiz-v2" }
    ]
  }
}
  • label est facultatif. Lorsqu'il est omis, le dernier slug de chemin de l'URL est utilisé (/quiz-v2quiz-v2), avec repli sur le nom d'hôte pour les URL racines.
  • En mode ensemble d'annonces unique, le label de chaque variante devient le nom complet de l'ensemble d'annonces.
  • Dans les modes perUpload et autoGroup, le nom de chaque ensemble d'annonces dupliqué ajoute _{label} au modèle (ou remplace un token {destination} si tu en inclus un).
  • Le token {date} dans un label se résout à la date du jour (p. ex. launch-{date}launch-2026-04-27).
  • Chaque URL de destination doit être unique. Les URL identiques (ou les variantes avec barre oblique finale ou avec une casse différente de la même URL) sont fusionnées en une seule entrée, alors assure-toi d'avoir au moins 2 destinations distinctes.
  • Le budget de ton ensemble d'annonces est multiplié par le nombre de variantes, puisque chaque doublon est son propre ensemble d'annonces.
  • Non compatible avec les annonces sources à destination spéciale (formulaire de prospects, Messenger, WhatsApp, DM Instagram, appel). Le CLI rejette cette combinaison avec une erreur claire, ces formats ne passent pas par cta.link.
  • Les substitutions de link par annonce dans texts.perAd perdent face à l'URL de la variante lorsque les deux sont définies.

Améliorations créatives

Contrôle les améliorations créatives Advantage+ :

{ "creativeEnhancements": "none" }
ValeurEffet
omisToutes les fonctionnalités désactivées
"metaDefaults"Alias obsolète pour "none"
"all"Toutes les fonctionnalités activées
"none"Toutes les fonctionnalités désactivées
["feature1", "feature2"]Seules les fonctionnalités listées activées, le reste désactivé

Fonctionnalités disponibles : text_translation, inline_comment, enhance_cta, text_optimizations, reveal_details_over_time, image_brightness_and_contrast, image_touchups, video_auto_crop, video_filtering, image_animation, image_templates, adapt_to_placement, product_extensions, description_automation, add_text_overlay, music, carousel_to_video, carousel_dynamic_description, multi_share_end_card, multi_share_optimized

Lorsque tu sélectionnes des fonctionnalités précises, ne liste que celles pertinentes pour le type de média. Les fonctionnalités vidéo (video_auto_crop, video_filtering) ne s'appliquent qu'aux annonces vidéo. Les fonctionnalités carrousel (carousel_to_video, carousel_dynamic_description, multi_share_end_card, multi_share_optimized) ne s'appliquent qu'aux annonces carrousel.

Annonces carrousel

Regroupe les fichiers importés en annonces carrousel avec un texte par carte et un texte global de carrousel facultatif. cardTexts contrôle les cartes individuelles. Le texte global du carrousel peut être placé sur l'objet carrousel ou défini dans texts.perAd à l'aide du name du carrousel ; les champs placés l'emportent lorsque les deux sont présents.

{
  "carousel": [
    {
      "name": "My Carousel",
      "cards": ["slide1.jpg", "slide2.jpg", "slide3.jpg"],
      "headlines": ["Overall Carousel Headline"],
      "bodies": ["Overall primary text"],
      "descriptions": ["Overall description"],
      "cta": "SHOP_NOW",
      "link": "https://example.com/carousel",
      "urlTags": "utm_content=my_carousel",
      "cardTexts": [
        { "headline": "Slide 1", "description": "First card", "link": "https://example.com/1" },
        { "headline": "Slide 2", "description": "Second card", "link": "https://example.com/2" }
      ]
    }
  ]
}

Forme alternative pour le texte global :

{
  "texts": {
    "perAd": {
      "My Carousel": {
        "headlines": ["Overall Carousel Headline"],
        "bodies": ["Overall primary text"],
        "descriptions": ["Overall description"],
        "cta": "SHOP_NOW",
        "link": "https://example.com/carousel",
        "urlTags": "utm_content=my_carousel"
      }
    }
  },
  "carousel": [
    {
      "name": "My Carousel",
      "cards": ["slide1.jpg", "slide2.jpg", "slide3.jpg"]
    }
  ]
}

Les cartes doivent référencer des noms de fichier de l'import. Minimum 2 cartes par carrousel. Les fichiers revendiqués par un carrousel sont retirés de la liste d'annonces standard.

Annonces flexibles

Regroupe plusieurs ressources en une seule annonce flexible où Meta choisit la meilleure ressource par placement :

{
  "flexible": [
    {
      "name": "Multi-Asset Ad",
      "assets": ["hero.jpg", "promo.mp4", "banner.jpg"]
    }
  ]
}

Minimum 2 ressources par groupe. Les fichiers revendiqués par un groupe flexible sont retirés de la liste d'annonces standard.

Annonces multimédias

Regroupe de 2 à 10 images ou vidéos importées en une annonce Multi Media de Meta :

{
  "multimedia": [
    {
      "name": "Mixed Media Ad",
      "assets": ["hero.jpg", "promo.mp4", "banner.jpg"],
      "assetTexts": [
        {
          "headline": "Hero headline",
          "primaryText": "Hero primary text",
          "description": "Hero description",
          "link": "https://example.com/hero",
          "displayUrl": "example.com/hero"
        }
      ]
    }
  ]
}

Les ressources doivent référencer des noms de fichier de l'import. assetTexts est facultatif et s'aligne sur assets par index ; chaque champ est une substitution unique pour cette ressource et les champs vides reviennent au texte principal et aux URL de l'annonce. La ressource principale rendue utilise le texte et l'URL principaux de l'annonce, et toute substitution du texte principal d'une ressource devient la première option de texte principal. Les vidéos ont besoin d'une URL de miniature publique mise en cache issue du traitement de l'import. Les fichiers revendiqués par un groupe Multi Media sont retirés de la liste d'annonces standard.

Nommage des annonces

Personnalise la façon dont tes annonces sont nommées :

{ "adNamePattern": "{filename} - {date}" }
Espace réservéCe qu'il insère
{filename}Nom de fichier d'origine sans extension
{index:01}Index complété par des zéros (01, 02, 03...)
{variation}Identifiant de variation si le regroupement par variantes est activé
{campaign}Nom de la campagne
{date}Date actuelle (AAAA-MM-JJ)
{date:short}Date courte (MM-JJ)
{timestamp}Horodatage Unix

Options

{
  "options": {
    "status": "PAUSED",
    "pauseAt": "adSet",
    "schedule": {
      "startTime": "2026-04-01T09:00:00",
      "endTime": "2026-04-30T23:59:59"
    }
  }
}
ChampValeursDescription
status"PAUSED", "ACTIVE"Statut de lancement de l'annonce (par défaut : ACTIVE)
pauseAt"ad", "adSet", "campaign"À quel niveau mettre en pause (par défaut : ad)
schedule.startTimeChaîne ISO 8601Heure de début programmée (utilise le fuseau horaire du compte publicitaire)
schedule.endTimeChaîne ISO 8601Heure de fin programmée (facultative)

Import et détection de variantes

Les groupes de variantes sont détectés automatiquement à partir des conventions de noms de fichier, tout comme dans l'application web. Consulte Variations de format d'image pour tous les détails sur les conventions de nommage.

Suffixes de ratio : hero_1x1.jpg + hero_4x5.jpg + hero_9x16.jpg + hero_16x9.jpg + hero_1.91x1.jpg sont regroupés en une seule annonce à variantes. Jusqu'à 5 ratios par groupe.

Position du token : le token de ratio peut apparaître à la fin (hero_4x5.jpg), au milieu (hero_4x5_v2.jpg) ou au début (4x5_hero.jpg).

Suffixes de mots hérités : hero.jpg + hero_vertical.jpg + hero_horizontal.jpg fonctionnent toujours et correspondent à 9x16 et 16x9.

Le délimiteur par défaut est _. Tu peux le changer (ou en accepter plusieurs) dans Compte > Valeurs par défaut > Placements > Séparateur de nom de fichier.

Modèles courants

Importer et créer avec un préréglage

ads upload ./creatives/hero.jpg ./creatives/banner.jpg
ads create:preview spec.json
ads create spec.json

spec.json contient :

{ "adPresetId": "PRESET_ID", "uploadId": "BATCH_ID" }

Copier les paramètres d'une annonce existante

Navigue dans ton compte pour trouver l'annonce :

ads campaigns
ads campaign 120233848666410472
ads adset 120233848666620472
ads ad 120233848667930472

Crée ensuite une spécification qui la référence :

{
  "copyFromAd": "120233848667930472",
  "uploadId": "BATCH_ID"
}

L'annonce source doit avoir des paramètres de créatif en ligne. Si elle a été construite à partir d'une publication de page existante, le CLI la rejettera avant d'envoyer une requête de création.

Enregistrer un préréglage d'API à partir d'une annonce existante

ads presets:save --from-ad 120233848667930472 --name "Spring Purchase Template"
ads create --preset PRESET_ID --upload BATCH_ID

Cela enregistre la même forme de préréglage d'API que celle utilisée par l'application web. L'annonce source doit avoir des paramètres de créatif en ligne ; les annonces basées sur des publications de page ne peuvent pas être enregistrées comme préréglages d'API.

Texte par annonce avec un texte unique par fichier

{
  "adPresetId": "PRESET_ID",
  "uploadId": "BATCH_ID",
  "texts": {
    "perAd": {
      "hero.jpg": {
        "headlines": ["Summer Sale Now On"],
        "bodies": ["Save up to 50% on all items"],
        "cta": "SHOP_NOW",
        "link": "https://example.com/summer"
      },
      "banner.jpg": {
        "headlines": ["New Collection Available"],
        "bodies": ["Browse our latest styles"],
        "cta": "LEARN_MORE",
        "link": "https://example.com/new"
      }
    }
  }
}

Regroupement automatique en plusieurs ensembles d'annonces

{
  "adPresetId": "PRESET_ID",
  "uploadId": "BATCH_ID",
  "adSet": { "mode": "autoGroup", "adsPerAdSet": 3 }
}

Notes importantes

  1. Toujours prévisualiser d'abord. create:preview détecte les erreurs de configuration avant de toucher à Facebook.
  2. Les annonces sont actives par défaut. Utilise --status PAUSED ou "status": "PAUSED" dans la spécification pour les créer en pause.
  3. uploadId provient de la sortie de l'import. C'est l'ID d'import renvoyé par ads upload.
  4. Les imports sont liés à un compte publicitaire. Les fichiers sont importés directement dans la bibliothèque de médias Facebook du compte sélectionné. L'ID d'import ne peut être utilisé qu'avec le même compte.
  5. copyFromAd a besoin de uploadId. Tu dois fournir l'import. Facultativement, fournis campaign.id et adSet.id pour contrôler le placement.
  6. Les clés de texte par annonce sont des noms de fichier. Utilise "hero.jpg", pas "/path/to/hero.jpg".
  7. textPresetId et texts sont mutuellement exclusifs. Utilise l'un ou l'autre, pas les deux.
  8. Les CTA spécifiques à l'objectif sont hérités du modèle. Ne définis pas MESSAGE_PAGE, WHATSAPP_MESSAGE, etc. manuellement.