# Pixel OAIQ ou Conversions API : pourquoi le suivi ChatGPT Ads a besoin des deux

> Le pixel OAIQ voit le contexte du clic, la Conversions API chaque commande payée. Envoyez les deux avec un même identifiant : OpenAI n'en garde qu'une.

Canonical: https://convrail.com/fr/blog/pixel-oaiq-ou-conversions-api/

Le pixel OAIQ et la Conversions API mesurent les mêmes conversions depuis deux endroits qui échouent différemment. Le pixel s'exécute dans le navigateur, où lui seul peut capturer l'identifiant de clic `oppref`, et où bloqueurs, consentement et onglets fermés font disparaître des événements. La Conversions API s'exécute depuis votre serveur, où rien n'est bloqué et où les données client hashées améliorent le rapprochement. Envoyez les deux avec un même identifiant d'événement ; OpenAI n'en garde qu'une copie.

## Deux points de vue sur la même vente

Une conversion est un fait unique : un client a payé une commande. Les deux couches sont deux témoins de ce fait, et chaque témoin voit quelque chose que l'autre ne peut pas voir. Les citations de la documentation d'OpenAI et de Shopify sont traduites par nous.

| | Pixel OAIQ (navigateur) | Conversions API (serveur) |
| --- | --- | --- |
| Où le code s'exécute | Navigateur du visiteur (sur Shopify, dans le bac à sable d'un web pixel) | Votre serveur ou un connecteur, sur un webhook |
| Capture `oppref` | Oui, depuis l'URL d'atterrissage, conservé dans le cookie `__oppref` | Seulement si le navigateur le relaie |
| Connaît l'URL de la page | Oui | Seulement ce que le webhook ou le relais navigateur fournit |
| Dépend de l'exécution d'un script | Oui | Non |
| Dépend de l'onglet resté ouvert | Oui | Non |
| Exposé aux bloqueurs | Oui | Non |
| Porte des identifiants client hashés | Pas dans la requête image | Oui, dans l'objet `user` |
| Peut être rejoué après un échec | Non, le moment est passé | Oui, avec le même identifiant d'événement |
| Peut être validé avant la mise en production | Non | Oui, avec `validate_only` |

Aucune colonne n'est complète seule. C'est tout l'argument, et la suite de cet article est le détail derrière chaque ligne.

## Ce que seul le pixel peut voir

### L'identifiant de clic

Quand un visiteur clique sur une publicité ChatGPT, l'URL d'atterrissage porte un paramètre `oppref`. La [référence du Measurement Pixel](https://developers.openai.com/ads/measurement-pixel) indique que le pixel « capture `oppref` depuis l'URL de la page d'atterrissage, un identifiant respectueux de la vie privée » et « stocke `oppref` dans un cookie first-party `__oppref` pour que les pages vues suivantes puissent le réutiliser ». La référence de la Conversions API décrit la même valeur comme « un identifiant d'attribution opaque, fourni par OpenAI ».

Votre serveur ne voit jamais cette URL. Le webhook qui vous annonce qu'une commande a été payée contient la commande, pas la page d'atterrissage sur laquelle le client est arrivé trois jours plus tôt. Le navigateur est donc le seul endroit où `oppref` peut être capturé, et le cookie est ce qui le transporte de la page d'atterrissage au passage en caisse. Le pixel de Convrail le conserve 7 jours et l'inclut dans la copie compacte de chaque événement qu'il poste à Convrail, pour que la copie serveur de la commande puisse porter le même identifiant.

### La référence navigateur

L'objet `user` de la Conversions API accepte `obref`, décrit comme une « référence navigateur opaque issue du cookie `__obref` du pixel ». Encore une valeur qui n'existe que parce qu'un pixel s'est exécuté dans ce navigateur.

### Le contexte de page

`source_url` est « obligatoire pour les événements web quand `action_source` vaut `web` ». Le pixel l'a gratuitement. Le serveur doit se le faire dire.

## Où les événements navigateur se perdent

Aucun de ces mécanismes ne produit une erreur que vous remarqueriez. L'événement n'arrive simplement jamais.

- **Le script ne se charge pas.** Les bloqueurs de contenu maintiennent des listes de domaines de suivi et d'URL de scripts. Un script bloqué ne s'exécute jamais, donc aucune requête n'est tentée. Sur Shopify, les web pixels s'exécutent dans un bac à sable fourni par la plateforme, ce qui change la façon dont le script est livré mais pas le fait qu'une requête vers le point de terminaison d'OpenAI doit encore quitter le navigateur.
- **Le consentement est refusé ou arrive tard.** Le pixel expose `oaiq("consent", false)` et `oaiq("consent", true)`, et la référence précise qu'il « initialise le consentement à `true` par défaut sauf si vous le mettez à `false` ». Une boutique qui exige un consentement explicite ne déclenchera jamais, à raison, le pixel pour les visiteurs qui refusent. Un bandeau de consentement qui se résout après que la page de remerciement s'est déjà affichée peut manquer l'événement de commande même pour les visiteurs qui acceptent. Sur Shopify, les web pixels « respectent les signaux de consentement choisis par le client » (d'après la [présentation des web pixels](https://shopify.dev/docs/apps/build/marketing-analytics/pixels)), donc la plateforme l'applique pour vous.
- **L'onglet se ferme avant.** L'événement de commande se déclenche sur la page de remerciement. Un client qui ferme l'onglet, navigue ailleurs, ou dont le navigateur suspend la requête d'arrière-plan avant qu'elle se termine ne laisse aucune trace.
- **La page ne s'affiche jamais.** Des moyens de paiement qui redirigent vers la boutique peuvent aboutir sur une page que le pixel ne couvre pas, ou sur une confirmation envoyée par email plutôt qu'affichée par un chargement de page.
- **Les conditions réseau.** Les connexions mobiles coupent. Une requête qui expire n'est pas rejouée par un pixel ; le moment est passé.

Nous n'avons pas mesuré la part de commandes que chaque mécanisme retire sur une boutique type, et nous ne reprenons pas ici les chiffres d'autres acteurs. Le point est structurel : chaque mécanisme ci-dessus est invisible de votre côté, et la couche serveur est immune à tous.

## Ce que seul le serveur peut garantir

### La fiabilité

Un webhook `orders/paid` arrive que le navigateur du client ait coopéré ou non. Votre serveur construit l'événement `order_created`, le met en lot et l'envoie. Si OpenAI renvoie 429 ou une 5xx, vous rejouez avec un délai exponentiel et le même identifiant d'événement, et la déduplication rend la reprise inoffensive. Si le lot échoue définitivement, vous l'avez toujours : Convrail conserve les lots épuisés dans une file des échecs avec le statut HTTP et le texte d'erreur, et ouvre une alerte de santé au lieu de perdre les événements.

La Conversions API vous laisse aussi de la marge pour récupérer. Le `timestamp_ms` « doit se situer dans les 7 derniers jours et au plus 10 minutes dans le futur », donc une panne de quelques heures de votre côté est survivable ; vous rejouez quand elle se termine.

### Le rapprochement hashé

L'objet `user` est l'endroit où le serveur gagne sa place. Il accepte des listes de hashes SHA-256 : `emails_sha256`, `phone_numbers_sha256`, `external_ids_sha256`, `first_names_sha256`, `last_names_sha256`, plus les champs bruts `ip_address`, `user_agent`, `countries`, `regions`, `cities`, `postal_codes`. C'est ainsi qu'OpenAI rapproche une conversion d'un utilisateur ChatGPT quand le contexte navigateur manque ou est ambigu. La requête image du pixel ne porte rien de tout cela, et la [référence de la balise image](https://developers.openai.com/ads/image-tag) est explicite : « Ne mettez pas de données personnelles, de secrets, d'identifiants de session, d'identifiants client ou d'identifiants de commande dans un paramètre de requête ».

Le hashage a des règles (email nettoyé des espaces et en minuscules ; numéro de téléphone réduit à 8-15 chiffres après retrait du `+` initial, des zéros initiaux, des espaces, parenthèses, points et tirets), et il a un poids juridique : un hash est une donnée pseudonymisée, pas une donnée anonyme. Les deux sujets sont traités dans [Suivi côté serveur sans fuite de données personnelles](/fr/blog/tracking-serveur-sans-fuite-donnees-personnelles/).

### Le mode test

`validate_only: true` fait valider un lot par OpenAI « sans l'enregistrer ». Vous pouvez prouver que la forme de votre charge utile est correcte avant qu'une seule conversion réelle soit en jeu. Il n'existe aucun équivalent pour le pixel.

## Comment fonctionne la déduplication

Envoyer les deux copies compterait chaque commande deux fois si OpenAI ne pouvait pas les reconnaître comme une seule. La règle, tirée de la [référence de la Conversions API](https://developers.openai.com/ads/conversions-api) :

> Si vous envoyez la même conversion depuis le pixel et depuis la Conversions API, réutilisez la même valeur comme `id` côté API et comme `event_id` côté pixel. La déduplication utilise votre Pixel ID, `event_name` et `id`.

Et : « OpenAI utilise le premier événement reçu pour une clé donnée et ignore les doublons suivants ».

Trois conséquences méritent d'être écrites noir sur blanc.

1. **La clé a trois parties.** Le même identifiant sous un autre Pixel ID n'est pas un doublon. Le même identifiant avec un autre type d'événement n'en est pas un non plus : un `order_created` et un `checkout_started` qui partagent un identifiant sont deux événements. Pour les événements `custom`, le `custom_event_name` fait aussi partie de l'identité.
2. **L'identifiant doit être reproductible sans coordination.** Le navigateur se déclenche sur la page de remerciement ; le serveur se déclenche quand le webhook arrive, peut-être quelques secondes plus tard, peut-être après une reprise une heure plus tard. Aucun des deux ne peut demander à l'autre quel identifiant il a utilisé. Une valeur dérivée de la commande, `order_<orderId>`, est la seule qui fonctionne. Convrail utilise exactement celle-là sur les deux couches.
3. **Le premier gagne, donc la copie navigateur gagne en général.** La page de remerciement se déclenche le plus souvent avant que le webhook soit traité. Ce n'est pas un problème : la copie navigateur porte le contexte du clic. Quand la copie navigateur est perdue, la copie serveur est la seule et elle est conservée. Si vous voulez que les données `user` plus riches de la copie serveur soient celles qu'OpenAI stocke, vous ne pouvez pas forcer cet ordre ; les deux copies doivent donc être aussi complètes que leur couche le permet.

Une reprise est un doublon par conception. Renvoyer un lot en échec avec les mêmes identifiants est sûr précisément grâce à cette règle ; générer de nouveaux identifiants à chaque reprise transformerait chaque panne récupérée en conversions gonflées.

## La balise image : une solution de repli, pas une troisième couche

OpenAI documente aussi une option sans JavaScript : une image de 1x1 pixel pointant vers `https://bzr.openai.com/v1/sdk/events?pid=<PIXEL-ID>&event=<name>&event_id=<id>&data[type]=contents&data[amount]=<minor units>&data[currency]=<ISO>`, avec un paramètre `oppref` facultatif. Elle sert, selon les mots de la référence, « à enregistrer des chargements de page sans exécuter de JavaScript ».

Ses limites, toutes tirées de la [référence de la balise image](https://developers.openai.com/ads/image-tag) :

- « Chaque requête envoie un événement. La balise image ne regroupe pas les événements. »
- « Une balise image statique se charge avec la page. Elle ne peut pas mesurer les clics, les envois de formulaire ni les autres interactions qui surviennent ensuite. »
- « La balise image ne prend pas en charge d'objet `user`. » Aucun rapprochement hashé.
- « La balise image ne capture pas `oppref` automatiquement. Transmettez-le seulement quand votre système de rendu de page possède déjà la valeur. »

Ce dernier point est celui qui fait mal. Une balise image sur une page de remerciement ne porte `oppref` que si votre serveur l'a injecté dans l'URL, ce qui suppose que votre serveur l'a déjà reçu et stocké depuis la page d'atterrissage. À ce stade vous avez construit l'essentiel d'une intégration côté serveur et vous devriez envoyer la commande par la Conversions API. La balise image reste utile là où aucun script ne peut s'exécuter (un email de confirmation, une page de type AMP, un gabarit verrouillé), et elle se déduplique de la même manière : « utilisez la valeur `event_id` de la balise image pour le champ `id` de l'appel à l'API » sous le même Pixel ID.

## Quels événements envoyer depuis où

| Événement | Navigateur (pixel) | Serveur (Conversions API) | Raison |
| --- | --- | --- | --- |
| `page_viewed` | Oui | Non | Seul le navigateur sait qu'une page a été vue ; il n'existe aucun équivalent serveur |
| `contents_viewed` (produit, collection) | Oui | Non | Idem ; faible valeur unitaire, fort volume |
| `items_added` | Oui | Facultatif | Les modifications de panier existent côté serveur sur certaines plateformes, mais le navigateur est la source naturelle |
| `checkout_started` | Oui | Facultatif | Le navigateur le déclenche au bon moment ; une copie serveur ajoute peu |
| `order_created` | Oui | Oui, même identifiant d'événement | La conversion qui compte : navigateur pour le contexte du clic, serveur pour la fiabilité et le rapprochement hashé |
| `subscription_created`, `trial_started`, `registration_completed` | Si une page existe | Oui | Souvent conclus hors d'une page vue ou confirmés de façon asynchrone ; le serveur, lui, sait |
| Remboursements et annulations | Non | Non pris en charge comme types d'événements | La Conversions API n'a aucun type d'événement de remboursement dans sa liste documentée ; gérez les marges dans vos propres rapports |

Le schéma : tout le haut de l'entonnoir depuis le navigateur seulement, l'événement d'argent depuis les deux, tout ce qui est confirmé de façon asynchrone depuis le serveur. Les cinq premières lignes sont la correspondance que Convrail applique sur Shopify et WooCommerce, et le guide d'installation est dans [Suivre les conversions ChatGPT Ads sur Shopify](/fr/blog/suivre-conversions-chatgpt-ads-shopify/).

## Erreurs fréquentes

- **Traiter la couche serveur comme une sauvegarde à activer plus tard.** La déduplication ne fonctionne que si les identifiants ont été conçus pour elle dès le premier jour. Greffer des identifiants déterministes sur un pixel qui en envoyait des aléatoires implique une période pendant laquelle les deux couches comptent double.
- **Envoyer aussi le haut de l'entonnoir depuis le serveur.** Dix copies de `page_viewed` par session depuis votre backend ajoutent du volume sans information et consomment pour rien la limite de 1 000 événements par lot.
- **Oublier que le Pixel ID fait partie de la clé.** Un pixel de préproduction et un pixel de production qui reçoivent les mêmes identifiants de commande sont deux flux séparés ; rien ne se déduplique entre eux.
- **Relayer `oppref` par l'URL de la page de remerciement.** Les paramètres de requête finissent dans les journaux, les outils d'analyse et les référents. Relayez-le par une requête first-party vers votre propre point de terminaison, comme le fait le pixel de Convrail.
- **Croire que la balise image est « le pixel sans JavaScript ».** C'est un événement unique, déclenché au seul chargement de page, sans objet `user`. Elle ne peut remplacer le script ni pour les interactions ni pour la commande.
- **Juger le pixel sur ses chiffres dans l'Ads Manager.** Si le pixel est votre seule couche, le nombre rapporté est le nombre du pixel ; vous n'avez aucune référence indépendante pour remarquer ce qu'il manque. La couche serveur est cette référence. Le moniteur de santé de Convrail s'en sert : zéro événement navigateur en 24 heures pendant que les événements serveur continuent d'arriver est signalé comme un pixel cassé.

## Et maintenant

Mettez en place les deux couches avec un identifiant d'événement partagé dès le départ, et laissez le [module de suivi des conversions de Convrail](/fr/tracking-conversions/) gérer le relais, le hashage et les reprises pour que les deux témoins décrivent toujours la même vente.