# Configurer le pixel OpenAI Ads (ChatGPT Ads) avec Google Tag Manager<no value>

## Introduction au tracking des publicités ChatGPT

OpenAI propose désormais de diffuser des publicités dans ChatGPT. Comme pour toute plateforme publicitaire, OpenAI ne peut pas savoir ce qui se passe sur votre site après un clic sur une annonce. Le **Measurement Pixel** est le script à installer sur votre site pour informer OpenAI des actions réalisées par vos visiteurs : pages vues, ajouts au panier, achats, leads, inscriptions.

Ces remontées servent à trois choses :

- **Attribuer vos conversions** : OpenAI relie chaque conversion à un clic sur une annonce (attribution post-clic) et, lorsque la fonctionnalité est disponible sur votre compte, à une impression (attribution post-vue, sur une fenêtre fixe d'un jour).
- **Optimiser vos campagnes** : l'enchère, le calcul du CPA et l'optimisation des conversions reposent sur les conversions post-clic remontées par le pixel.
- **Mesurer vos performances** dans Ads Manager, avec des montants et des devises fiables.

Plutôt que d'ajouter le snippet officiel dans une balise HTML personnalisée puis d'écrire vous-même chaque appel `oaiq("measure", ...)`, nous allons utiliser la balise **OpenAI Ads Measurement Pixel by DMS**, que j'ai développée pour Google Tag Manager Web. Elle charge le SDK, lit votre <a href="/fr/blog/definition/datalayer/" target="_blank">dataLayer</a> au format GA4, convertit les montants au format attendu par OpenAI et hache les données utilisateur dans le navigateur.

{{< callout icon="outline/info-circle" title="Consentement et législation" >}}
Le pixel OpenAI transmet des <a href="/fr/blog/definition/donnee-personnelle/" target="_blank">données personnelles</a> (identifiants publicitaires, email et téléphone hachés). En fonction de votre législation et de vos visiteurs, il faudra conditionner cette balise au consentement du visiteur. Rendez-vous dans la section [gestion du consentement](#gérer-le-consentement) de cet article.
{{< /callout >}}

## Ajouter la balise OpenAI Ads Measurement Pixel by DMS

<a href="https://github.com/data-marketing-school/openai-ads-measurement-pixel/blob/master/template.tpl" target="_blank">Téléchargez ici</a> le fichier `template.tpl` de la balise.

{{< figure src="images/blog/google-tag-manager/openai-ads-measurement-pixel/tag_template_github_dl.jpg" alt="Téléchargement du fichier template.tpl de la balise OpenAI Ads Measurement Pixel by DMS sur GitHub" caption="Téléchargement du fichier template.tpl sur GitHub" >}}

Rendez-vous dans la section **Templates** de <a href="https://tagmanager.google.com" target="_blank">Google Tag Manager</a>. Dans la section **Tag Templates**, cliquez sur **New**.

{{< figure src="images/blog/google-tag-manager/openai-ads-measurement-pixel/tag_template_import.jpg" alt="Section Templates de Google Tag Manager Web et bouton New pour créer un modèle de balise" caption="Section Templates de Google Tag Manager Web" >}}

Cliquez sur les 3 petits points en haut à droite de l'éditeur de modèle, puis sur **Import**. Sélectionnez le fichier `template.tpl` que vous venez de télécharger puis cliquez sur **Save**.

{{< figure src="images/blog/google-tag-manager/openai-ads-measurement-pixel/tag_template_saved.jpg" alt="Enregistrement du modèle OpenAI Ads Measurement Pixel by DMS dans l'éditeur de modèle de Google Tag Manager" caption="Enregistrement du modèle de balise OpenAI Ads Measurement Pixel by DMS" >}}

La balise est désormais disponible dans la liste de vos modèles lorsque vous créez une nouvelle balise.

## Récupérer votre Pixel ID

Dans OpenAI Ads Manager, rendez-vous dans **Tools** > **Conversions**, onglet **Data Source**, et créez une source de données web. OpenAI vous fournit un **Pixel ID**, une chaîne de 22 caractères qui ressemble à `Xk3PqR8mZt2vLn7wHs5yBa`, affichée sous le nom de la source.

{{< figure src="images/blog/google-tag-manager/openai-ads-measurement-pixel/ads_manager_pixel_id.jpg" alt="Pixel ID de la source de données web dans la section Conversions de OpenAI Ads Manager" caption="Pixel ID dans la section Conversions de OpenAI Ads Manager" >}}

C'est ce même Pixel ID qui sert au pixel navigateur et, si vous en avez besoin, à l'API de conversions côté serveur.

## Configurer la balise

Rendez-vous dans la section **Tags** de Google Tag Manager, cliquez sur **New** et sélectionnez la balise **OpenAI Ads Measurement Pixel by DMS**.

{{< figure src="images/blog/google-tag-manager/openai-ads-measurement-pixel/tag_setup_overview.jpg" alt="Vue d'ensemble de la balise OpenAI Ads Measurement Pixel by DMS dans Google Tag Manager Web" caption="Balise OpenAI Ads Measurement Pixel by DMS" >}}

### Pixel ID(s)

Renseignez votre Pixel ID. Pour envoyer le même événement à plusieurs pixels, par exemple un pixel par marque ou un pixel pour votre agence, séparez les identifiants par une virgule : `Xk3PqR8mZt2vLn7wHs5yBa, 7kPz2QxLm4Ns8vRtYb1cDw`.

Chaque pixel est initialisé à chaque déclenchement et reçoit l'événement via la commande `measureSingle` du SDK. Contrairement à la commande `measure`, elle ne cible que le pixel indiqué : si un autre pixel OpenAI est déjà présent sur la page, il ne recevra pas vos événements.

### Event Name Setup Method

Ce champ détermine le nom de l'événement OpenAI envoyé. Deux modes sont disponibles.

**Inherit From dataLayer** (par défaut) : la balise lit le nom de l'événement dataLayer sur lequel elle se déclenche et le convertit en événement OpenAI. [Voir la table de correspondance des événements GA4 vers OpenAI](#table-de-correspondance-des-événements-datalayer). Un événement dataLayer qui n'a pas d'équivalent est envoyé en tant qu'événement **custom**, dont le nom est celui de l'événement dataLayer en minuscules, les caractères non autorisés étant remplacés par `_`. Un événement `Newsletter Signup` devient donc l'événement custom `newsletter_signup` côté OpenAI.

**Override** : vous choisissez vous-même l'événement. Le champ **Event Type** propose **Standard Event**, qui affiche la liste déroulante des événements standards d'OpenAI, ou **Custom Event**, qui affiche le champ **Custom Event Name**.

{{< figure src="images/blog/google-tag-manager/openai-ads-measurement-pixel/event_name_override.jpg" alt="Mode Override de la balise avec le type Standard Event et l'événement page_viewed sélectionné" caption="Mode Override avec un événement standard" >}}

{{< callout context="note" icon="outline/info-circle" >}}
Les événements <code>appointment_scheduled</code>, <code>subscription_created</code> et <code>trial_started</code> n'ont pas d'équivalent dans le standard GA4 : utilisez le mode <b>Override</b> pour les envoyer. Les événements <code>app_installed</code> et <code>app_opened</code> ne sont pas pris en charge par le pixel navigateur, ils passent uniquement par l'API de conversions.
{{< /callout >}}

Un nom d'événement custom doit contenir entre 1 et 64 caractères (lettres, chiffres, tirets ou underscores), commencer et finir par une lettre ou un chiffre, et ne pas reprendre le nom d'un événement standard. Sinon la balise échoue et explique pourquoi dans la console de la preview.

### Ecommerce Data

Lorsque la case **Read e-commerce data from the dataLayer** est cochée (par défaut), la balise lit l'objet `ecommerce` de votre dataLayer : `ecommerce.value` devient le montant de l'événement, `ecommerce.currency` sa devise et `ecommerce.items` la liste des contenus.

Les champs **Amount**, **Currency** et **Plan ID** permettent de forcer une valeur. Un champ renseigné l'emporte toujours sur la valeur du dataLayer.

{{< callout context="caution" icon="outline/alert-triangle" title="Montants en unité mineure" >}}
OpenAI attend des montants entiers exprimés dans l'unité mineure de la devise : <code>2599</code> pour 25,99 USD. Vous n'avez rien à convertir : renseignez le montant comme dans GA4 (<code>25.99</code>) et la balise applique l'exposant ISO 4217 de la devise. <code>25.99 USD</code> devient <code>2599</code>, <code>1250 JPY</code> reste <code>1250</code> et <code>1.250 KWD</code> devient <code>1250</code>. Un montant sans devise valide n'est pas envoyé.
{{< /callout >}}

Le type de données de l'événement est choisi automatiquement à partir de son nom : les événements `lead_created`, `registration_completed` et `appointment_scheduled` n'acceptent qu'un montant et une devise, jamais de contenus. **Plan ID** n'est envoyé qu'avec `subscription_created`, `trial_started` et les événements custom.

### Items Data

Cette section pilote la construction du tableau `contents` envoyé à OpenAI.

- **Contents** : une variable renvoyant un tableau d'items. Elle remplace `ecommerce.items` lorsqu'elle est renseignée.
- **Item ID Key**, **Item Name Key**, **Quantity Key**, **Price Key** : les clés lues dans chaque item pour alimenter `id`, `name`, `quantity` et `amount`. Par défaut, `item_id`, `item_name`, `quantity` et `price`, c'est-à-dire le schéma GA4.
- **Content Type Key** : optionnel, la clé lue pour `content_type`, par exemple `item_category`.
- **Default Content Type** : la valeur envoyée lorsque aucune clé n'est définie ou que l'item n'en a pas. `product` par défaut.

{{< figure src="images/blog/google-tag-manager/openai-ads-measurement-pixel/items_data_section.jpg" alt="Section Items Data de la balise avec les clés d'items par défaut du schéma GA4" caption="Section Items Data avec les clés par défaut" >}}

Les clés acceptent la notation pointée, par exemple `product.sku`, ce qui permet d'utiliser la balise avec un dataLayer dont les produits ne suivent pas le schéma GA4, sans passer par une variable de transformation. Le prix de chaque item est lu dans l'unité principale puis converti en unité mineure avec la devise de l'événement.

### User Data

Lorsque la case **Read user data from the dataLayer** est cochée (par défaut), la balise lit l'objet `user_data` du dataLayer. Elle accepte aussi bien le format GA4 imbriqué (`user_data.address.first_name`) qu'un format aplati (`user_data.first_name`), et reconnaît plusieurs alias de clés. [Voir la table des clés lues](#table-des-clés-user_data).

Les champs **Email Address**, **Phone Number**, **External ID**, **First Name** et **Last Name** permettent de renseigner une valeur manuellement. Les champs **Country**, **City**, **Region** et **Postal Code** sont transmis en clair, comme le prévoit OpenAI.

{{< callout context="tip" icon="outline/shield-lock" title="Hachage dans le navigateur" >}}
L'email, le téléphone, l'identifiant externe, le prénom et le nom ne quittent jamais le navigateur en clair. La balise les normalise puis les hache en SHA-256 avec <b>exactement les mêmes règles que le SDK d'OpenAI</b> : email en minuscules, téléphone réduit à ses chiffres avec l'indicatif pays, nom sans espaces ni ponctuation. Une valeur déjà hachée (64 caractères hexadécimaux, ou une clé <code>sha256_email_address</code> dans le dataLayer) est transmise telle quelle. Une valeur que le SDK refuserait, comme un email invalide ou un téléphone de moins de 8 chiffres, est ignorée et signalée dans la console.
{{< /callout >}}

Pensez à pousser les numéros de téléphone avec l'indicatif international (`+353 1 555 0123`). Un numéro national perd son zéro initial lors de la normalisation et ne correspondra pas au même utilisateur saisi avec l'indicatif.

### Event ID Deduplication

Si vous envoyez le même événement à la fois côté navigateur (via cette balise) et côté serveur (via l'<a href="https://docs.addingwell.com/fr/openai-capi?ref=data-marketing-school" target="_blank">API de conversions d'OpenAI</a>), OpenAI risque de compter deux fois la même conversion. Pour l'éviter, on transmet un **Event ID** identique dans les deux envois : OpenAI conserve le premier événement reçu pour un même Pixel ID, nom d'événement et Event ID, et ignore le doublon.

Renseignez le champ **Event ID** avec une variable qui génère un identifiant unique et stable pour un même événement, par exemple la variable **Event Id** de mbaersch disponible dans la galerie GTM Web. Lorsque le champ est vide, aucun Event ID n'est envoyé.

Transmettez la même variable dans le paramètre `event_id` de vos balises GA4 envoyées au conteneur serveur : la balise OpenAI CAPI d'Addingwell by Didomi réutilise automatiquement ce paramètre comme identifiant d'événement, et OpenAI peut alors dédupliquer les deux envois.

{{< addingwell title="Envoyez aussi vos conversions côté serveur" doc="https://docs.addingwell.com/fr/openai-capi" doc_label="Documentation OpenAI CAPI" >}}
Le pixel seul reste exposé aux bloqueurs de publicité et aux limitations de Safari. Addingwell by Didomi propose une balise **OpenAI CAPI** pour GTM Server-Side qui convertit vos événements GA4 au format OpenAI, hache les données utilisateur et prolonge la durée de vie des identifiants `oppref` et `obref` à 365 jours. Sa documentation détaille la configuration, le déclenchement et la vérification de la déduplication avec le pixel.
{{< /addingwell >}}

{{< figure src="images/blog/google-tag-manager/openai-ads-measurement-pixel/event_id_field.jpg" alt="Section Event ID Deduplication de la balise avec la variable Event Id renseignée" caption="Champ Event ID renseigné avec la variable Event Id" >}}

### Pixel Options

- **Opt Out This Event From User-Level Personalization** : envoie `opt_out: true` avec l'événement, pour l'exclure de la personnalisation au niveau utilisateur.
- **Enable SDK Debug Mode** : active le mode debug du SDK, qui journalise son activité dans la console du navigateur avec le préfixe `[oaiq]`. Pratique pendant vos tests, à désactiver en production.

{{< cta-banner
  headline="Vous préférez déléguer la configuration du pixel OpenAI ?"
  subheadline="Pixel ID, événements, montants, données utilisateur, déduplication : je peux configurer la balise dans votre conteneur GTM et l'aligner sur votre dataLayer, qu'il soit au format GA4 ou custom."
  cta="Contacter Lucas" >}}

## Déclencher la balise

La façon de déclencher la balise dépend du format de votre dataLayer : une seule balise en mode **Inherit From dataLayer** s'il respecte le standard GA4, une balise par événement en mode **Override** sinon.

### Méthode 1 : avec un dataLayer au format standard GA4

Si votre dataLayer respecte le <a href="https://developers.google.com/analytics/devguides/collection/ga4/reference/events?client_type=gtm" target="_blank">standard GA4</a>, une seule balise suffit. Renseignez votre **Pixel ID**, laissez **Inherit From dataLayer** sélectionné et les deux cases de lecture du dataLayer cochées.

Déclenchez ensuite la balise sur l'ensemble des événements que vous souhaitez envoyer à OpenAI, sans oublier les pages vues. Dans cet exemple, je la déclenche sur `gtm.init` (déclencheur **Initialization - All Pages**), `view_item`, `add_to_cart`, `begin_checkout`, `purchase` et `generate_lead`.

<!-- TODO screenshot : assets/images/blog/google-tag-manager/openai-ads-measurement-pixel/inherit_dl_trigger.jpg — déclencheur Custom Event avec la liste des événements dataLayer -->

### Méthode 2 : avec un dataLayer custom

Si votre dataLayer est custom, créez une balise par événement OpenAI en mode **Override**, et faites vous-même la correspondance entre vos événements dataLayer et les événements OpenAI.

**Exemple avec un événement dataLayer custom `AddToCart`**

```javascript
dataLayer.push({
  event: "AddToCart",
  cart: {
    currency: "EUR",
    total: 30.03,
    products: [
      {
        sku: "SKU_12345",
        title: "Stan and Friends Tee",
        category: "Apparel",
        unit_price: 10.01,
        qty: 3
      }
    ]
  },
  user_data: {
    email: "jane@example.com",
    phone: "+353 1 555 0123",
    first_name: "Jane",
    last_name: "Doe"
  }
});
```

Configurez la balise ainsi :

| Champ | Valeur |
|---|---|
| **Event Name Setup Method** | Override, Standard Event, `items_added` |
| **Read e-commerce data from the dataLayer** | décochée |
| **Amount** | `{{DLV - cart.total}}` |
| **Currency** | `{{DLV - cart.currency}}` |
| **Contents** | `{{DLV - cart.products}}` |
| **Item ID Key** | `sku` |
| **Item Name Key** | `title` |
| **Quantity Key** | `qty` |
| **Price Key** | `unit_price` |
| **Content Type Key** | `category` |
| **Read user data from the dataLayer** | cochée |

<!-- TODO screenshot : assets/images/blog/google-tag-manager/openai-ads-measurement-pixel/custom_dl_tag_setup.jpg — balise configurée pour l'événement custom AddToCart -->

## Gérer le consentement

La balise n'appelle jamais la commande `oaiq("consent", ...)` du pixel : c'est votre déclencheur GTM qui décide si elle se déclenche, en fonction du choix exprimé par le visiteur dans votre CMP (plateforme de gestion du consentement).

Le pixel OpenAI est un vendor publicitaire. Conditionnez donc le déclenchement de la balise à l'acceptation du **vendor OpenAI** dans votre CMP ou, si votre CMP ne gère pas ce vendor, à l'acceptation de la **catégorie de finalités** qui le représente : **Targeting**, **Advertising**, **Performance**, ou par exemple la catégorie **C0004** (Targeting Cookies) avec OneTrust.

Quelle que soit votre CMP, la logique est la même :

- **Une variable GTM** qui retourne `granted` lorsque le vendor OpenAI ou sa catégorie est accepté, `denied` sinon.
- **Un déclencheur pour la page vue**, branché sur l'événement que votre CMP pousse dans le dataLayer lorsque le consentement est connu ou mis à jour, avec la condition variable égale à `granted`. Il remplace le déclencheur **Initialization - All Pages** de la [méthode 1](#méthode-1--avec-un-datalayer-au-format-standard-ga4), de façon à ne pas perdre la page vue lorsque le visiteur accepte après le chargement de la page.
- **La même condition** ajoutée à vos déclencheurs d'événements dataLayer (`add_to_cart`, `purchase`, `generate_lead`...).

{{< callout icon="outline/info-circle" title="Prérequis indispensable" >}}
OpenAI doit être déclaré comme <b>vendor</b> dans l'interface de votre CMP, ou rattaché à une catégorie de finalités publicitaires. Sans cette déclaration, le vendor ne sera jamais présent dans la liste des consentements accordés et votre variable GTM retournera systématiquement <code>denied</code>.
{{< /callout >}}

Voici comment procéder avec Axeptio, Didomi et OneTrust. Adaptez la même logique à votre CMP.

{{< tabs "openai-consent-cmp" >}}
{{< tab "Axeptio" "axptio.svg" >}}

Déclarez **OpenAI** comme vendor dans votre projet Axeptio et notez son identifiant.

Axeptio écrit la liste des vendors acceptés dans le cookie `axeptio_authorized_vendors` : une variable GTM qui vérifie la présence de cet identifiant dans le cookie sert de condition à vos déclencheurs.

Pour la page vue, déclenchez la balise sur l'événement `axeptio_update`, poussé dans le dataLayer à chaque fois que le consentement est connu ou mis à jour.

{{< /tab >}}
{{< tab "Didomi" "didomi.svg" >}}

Déclarez **OpenAI** comme vendor dans la console Didomi (Data Manager > Vendors) et notez son identifiant, ou rattachez-le à une finalité publicitaire.

Didomi pousse dans le dataLayer les listes `didomiVendorsEnabled` et `didomiPurposesEnabled` : une variable GTM qui vérifie la présence de l'identifiant dans la liste concernée sert de condition à vos déclencheurs.

Pour la page vue, déclenchez la balise sur l'événement `didomi-consent`.

{{< /tab >}}
{{< tab "OneTrust" "ot.svg" >}}

OneTrust raisonne par catégories de cookies : rattachez le pixel OpenAI à la catégorie **C0004 - Targeting Cookies** ou à votre catégorie publicitaire.

OneTrust expose les catégories acceptées dans la variable globale `OptanonActiveGroups`, sous la forme `,C0001,C0004,` : une variable GTM qui vérifie la présence de `,C0004,` sert de condition à vos déclencheurs.

Pour la page vue, déclenchez la balise sur l'événement `OneTrustGroupsUpdated`.

{{< /tab >}}
{{< /tabs >}}

Pour vérifier, ouvrez la preview GTM sur une page de votre site avant d'avoir fait votre choix dans la bannière : la balise ne doit pas apparaître dans les balises déclenchées et aucune requête vers `bzr.openai.com` ne doit partir. Acceptez ensuite le vendor OpenAI ou sa catégorie : la balise doit se déclencher sur l'événement de votre CMP et la requête `events` apparaître dans l'onglet Network.

Si votre site utilise déjà la commande de consentement du pixel OpenAI, elle reste entièrement sous votre contrôle : la balise ne la modifie pas.

{{< cta-banner
  headline="Besoin d'aide pour conditionner vos balises au consentement ?"
  subheadline="Axeptio, Didomi, OneTrust ou une autre CMP : je peux mettre en place les variables et les déclencheurs qui respectent le choix de vos visiteurs, pour le pixel OpenAI comme pour vos autres balises publicitaires."
  cta="Contacter Lucas" >}}

## Vérifier sa configuration

Avant de publier, vérifiez que les événements partent correctement. Trois outils sont utiles : la preview GTM, l'onglet Network du navigateur et l'Event Stream d'Ads Manager.

### Preview GTM

Cliquez sur **Preview** en haut à droite de GTM et naviguez sur votre site. Pour chaque événement dataLayer concerné, la balise doit apparaître avec le statut **Succeeded**.

{{< figure src="images/blog/google-tag-manager/openai-ads-measurement-pixel/preview_tag_succeeded.jpg" alt="Balise OpenAI Ads Measurement Pixel by DMS en statut Succeeded sur l'événement Initialisation dans la preview GTM" caption="Balise en statut Succeeded dans la preview GTM" >}}

Ouvrez ensuite l'onglet **Console** de la preview. La balise y journalise chaque envoi avec le préfixe `OpenAI Measurement Pixel`, en indiquant le nom de l'événement, les Pixel ID ciblés, les données de l'événement et l'objet utilisateur haché. C'est le moyen le plus rapide de vérifier que le montant a bien été converti et que les identifiants ont bien été hachés.

{{< figure src="images/blog/google-tag-manager/openai-ads-measurement-pixel/preview_console_log.jpg" alt="Message de la balise OpenAI dans l'onglet Console de la preview GTM avec le nom de l'événement, le Pixel ID et l'Event ID" caption="Message de la balise dans la console de la preview GTM" >}}

### Onglet Network

Dans les outils de développement du navigateur, filtrez les requêtes sur `bzr`. Vous verrez trois requêtes : le chargement du SDK `oaiq.min.js`, la configuration de votre pixel (un fichier JSON nommé d'après votre Pixel ID) et la requête `events` qui porte vos conversions. Le SDK regroupe les événements proches dans le temps et les envoie en POST vers `/v1/sdk/events`. Les événements `openai::sdk_init` et `oai::diagnostic` qui accompagnent les vôtres sont émis par le SDK lui-même, c'est normal. Le corps de la requête contient vos événements, l'identifiant de clic `oppref` s'il est présent, et un objet `user` avec deux blocs possibles :

- `in` : les identifiants que vous avez transmis via la balise (`em` pour l'email, `ph` pour le téléphone, `fn` et `ln` pour le prénom et le nom, `eid` pour l'identifiant externe).
- `fm`, `ht` ou `js` : les identifiants détectés automatiquement par le pixel dans vos formulaires, votre HTML ou vos scripts, lorsque la correspondance avancée automatique est activée sur votre pixel.

{{< figure src="images/blog/google-tag-manager/openai-ads-measurement-pixel/network_payload.jpg" alt="Payload de la requête events envoyée à bzr.openai.com dans l'onglet Network de Chrome, avec les événements et le bloc user" caption="Payload de la requête envoyée à OpenAI dans l'onglet Network" >}}

Si le même email apparaît dans `in` et dans un formulaire de la page, le SDK ne l'envoie qu'une fois : sa disparition du bloc `fm` est le signe que le hachage de la balise est identique à celui du pixel.

### Event Stream dans Ads Manager

Dans OpenAI Ads Manager, la section **Conversions** propose un onglet **Event Stream** qui liste les derniers événements reçus par votre pixel. C'est la confirmation finale que vos conversions arrivent bien avec le bon nom, le bon montant et la bonne devise.

### Erreurs fréquentes

| Message dans la console | Cause | Solution |
|---|---|---|
| `no Pixel ID configured` | Le champ Pixel ID(s) est vide ou ne contient que des virgules. | Renseignez au moins un Pixel ID. |
| `Invalid custom event name` | Le nom custom contient des caractères interdits ou dépasse 64 caractères. | Utilisez uniquement lettres, chiffres, `_` et `-`. |
| `matches a standard event name` | Le nom custom reprend un événement standard. | Sélectionnez l'événement standard dans la liste. |
| `No event name found in the dataLayer` | Mode Inherit sans clé `event` dans le dataLayer. | Déclenchez la balise sur un événement dataLayer nommé, ou passez en mode Override. |
| `amount ignored because no valid currency is available` | Un montant est présent sans devise ISO 4217 à 3 lettres. | Renseignez `ecommerce.currency` ou le champ Currency. |
| `skipped, value rejected by normalization` | Email invalide, téléphone trop court ou sans indicatif, nom composé uniquement de chiffres. | Corrigez la valeur poussée dans le dataLayer. |
| `could not hash` | Le navigateur ne fournit pas SubtleCrypto, généralement parce que la page n'est pas en HTTPS. | Testez sur une page sécurisée. |

{{< cta-banner
  headline="Un doute sur les données envoyées à OpenAI ?"
  subheadline="Je peux auditer votre implémentation : événements manquants, montants mal convertis, identifiants non hachés, doublons avec l'API de conversions, et vous livrer un plan de correction priorisé."
  cta="Contacter Lucas" >}}

## Tables de correspondance GA4 => OpenAI

### Table de correspondance des événements dataLayer

{{< callout context="note" icon="outline/info-circle" >}}
Cette table est utilisée par la balise uniquement lorsque le champ <b>Event Name Setup Method</b> est configuré en <b>Inherit From dataLayer</b>.
{{< /callout >}}

| **Nom de l'événement dataLayer** | **Nom de l'événement OpenAI** | **Type de données** |
|---|---|---|
| `gtm.init_consent` (Consent Initialization) | `page_viewed` | `contents` |
| `gtm.init` (Initialization) | `page_viewed` | `contents` |
| `gtm.js` (Container Loaded) | `page_viewed` | `contents` |
| `gtm.dom` (DOM Ready) | `page_viewed` | `contents` |
| `gtm.load` (Window Loaded) | `page_viewed` | `contents` |
| `page_view` | `page_viewed` | `contents` |
| `view_item` | `contents_viewed` | `contents` |
| `view_item_list` | `contents_viewed` | `contents` |
| `add_to_cart` | `items_added` | `contents` |
| `begin_checkout` | `checkout_started` | `contents` |
| `purchase` | `order_created` | `contents` |
| `generate_lead` | `lead_created` | `customer_action` |
| `sign_up` | `registration_completed` | `customer_action` |
| Tout autre événement | `custom` | `custom` |

<style>
    table {
        margin: 0;
    }
</style>

### Table de correspondance des paramètres e-commerce

{{< callout context="note" icon="outline/info-circle" >}}
Cette table est utilisée par la balise uniquement lorsque la case <b>Read e-commerce data from the dataLayer</b> est cochée, avec les clés d'items par défaut.
{{< /callout >}}

| **Chemin du paramètre GA4** | **Champ OpenAI** | **Transformation** |
|---|---|---|
| `ecommerce.value` | `amount` | Converti en unité mineure |
| `ecommerce.currency` | `currency` | Mis en majuscules |
| `ecommerce.items[item_id]` | `contents[].id` | |
| `ecommerce.items[item_name]` | `contents[].name` | |
| `ecommerce.items[quantity]` | `contents[].quantity` | Converti en entier |
| `ecommerce.items[price]` | `contents[].amount` | Prix unitaire converti en unité mineure |
| Valeur fixe | `contents[].content_type` | `product` par défaut |

### Table des clés user_data

{{< callout context="note" icon="outline/info-circle" >}}
Cette table est utilisée par la balise uniquement lorsque la case <b>Read user data from the dataLayer</b> est cochée. Chaque clé est cherchée dans <code>user_data.address</code> puis à la racine de <code>user_data</code>, dans l'ordre indiqué.
{{< /callout >}}

| **Champ OpenAI** | **Clés déjà hachées** | **Clés en clair (hachées par la balise)** |
|---|---|---|
| `email_sha256` | `sha256_email_address`, `sha256_email` | `email_address`, `email` |
| `phone_number_sha256` | `sha256_phone_number`, `sha256_phone` | `phone_number`, `phone` |
| `external_id_sha256` | `sha256_external_id`, `sha256_customer_id`, `sha256_user_id` | `external_id`, `customer_id`, `user_id`, puis `user_id` à la racine du dataLayer |
| `first_name_sha256` | `sha256_first_name` | `first_name` |
| `last_name_sha256` | `sha256_last_name` | `last_name` |
| `country` | | `country` |
| `city` | | `city` |
| `region` | | `region` |
| `postal_code` | | `postal_code`, `zip`, `zip_code`, `postcode` |

## Content Security Policy

Si votre site applique une Content Security Policy, autorisez `https://bzrcdn.openai.com` dans `script-src` et `connect-src`, ainsi que `https://bzr.openai.com` dans `connect-src` et `img-src`. Sans cela, le SDK ne se chargera pas ou ne pourra pas envoyer les événements.

## Conclusion

La balise **OpenAI Ads Measurement Pixel by DMS** vous permet d'installer le pixel des publicités ChatGPT sans écrire une ligne de code : elle exploite votre dataLayer GA4 existant, respecte les formats attendus par OpenAI pour les montants et les identifiants, et prépare la déduplication avec l'API de conversions. Le code source est disponible sur <a href="https://github.com/data-marketing-school/openai-ads-measurement-pixel" target="_blank">GitHub</a> sous licence Apache 2.0 : n'hésitez pas à ouvrir une issue si vous rencontrez un cas non couvert.

{{< cta-banner
  headline="Besoin d'aide pour votre tracking OpenAI Ads ?"
  subheadline="Je peux vous accompagner en tant que freelance pour déployer le pixel OpenAI dans Google Tag Manager, l'aligner sur votre dataLayer et mettre en place la déduplication avec l'API de conversions côté serveur."
  cta="Contacter Lucas" >}}

## FAQ - Pixel OpenAI Ads

{{< faq >}}
