Suivre les conversions ChatGPT Ads sur Shopify repose sur deux couches : le pixel OAIQ enregistré comme web pixel Shopify pour les événements navigateur, et la Conversions API alimentée par votre webhook orders/paid pour la copie serveur de chaque commande. Les deux copies portent le même identifiant d’événement (order_<orderId>), les montants partent en unités mineures, les emails et téléphones partent en hashes SHA-256, et vous validez toute la chaîne avec validate_only avant la mise en production.
Ce que vous construisez
Avant les étapes, la forme du montage terminé, parce que chaque décision ci-dessous en découle. Les citations de la documentation d’OpenAI et de Shopify sont traduites par nous.
| Couche | S’exécute où | Capture | Point faible |
|---|---|---|---|
| Pixel OAIQ (navigateur) | Le navigateur du visiteur, dans le bac à sable d’un web pixel Shopify | L’identifiant de clic oppref dans l’URL d’atterrissage, les événements de page et de panier, le passage en caisse | Tout ce qui empêche un script de s’exécuter ou une requête de quitter l’onglet |
| Conversions API (serveur) | Votre serveur ou un connecteur, déclenché par les webhooks Shopify | La commande payée avec montant, devise, lignes et identifiants client hashés | Ne lit pas le navigateur par lui-même ; le contexte du clic doit lui être relayé |
Les deux couches se rejoignent sur une règle de la référence de la 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. » OpenAI garde alors une copie et ignore l’autre. Envoyer les deux n’est pas de la redondance gratuite : la copie navigateur porte le contexte du clic, la copie serveur porte le montant fiable et les clés de rapprochement hashées. Le raisonnement derrière cette architecture à deux couches est détaillé dans Pixel OAIQ ou Conversions API.
Étape 1 : récupérer votre Pixel ID et votre clé API
Les deux identifiants viennent de l’onglet conversions du ChatGPT Ads Manager. Le Pixel ID va dans l’extrait navigateur et dans le paramètre de requête pid de chaque appel à l’API. La clé API est un jeton porteur : le point de terminaison de la Conversions API est POST https://bzr.openai.com/v1/events?pid=<PIXEL-ID> avec un en-tête Authorization: Bearer <key>.
Traitez la clé comme un secret de paiement. Elle n’a rien à faire dans le thème, dans un web pixel, ni dans aucun code livré au navigateur. Si vous passez par un connecteur, vérifiez qu’il chiffre la clé au repos ; Convrail la stocke en AES-256-GCM et la vérifie auprès d’OpenAI à l’instant où vous l’enregistrez, donc une faute de frappe est repérée avant le premier lot.
Étape 2 : installer le pixel comme web pixel Shopify
Shopify donne aux applications un mécanisme dédié aux scripts de suivi. D’après la présentation des web pixels Shopify, « les web pixels sont chargés dans un bac à sable sur le navigateur du visiteur », dans un « environnement strictement isolé fondé sur des web workers », et ils « respectent les signaux de consentement choisis par le client ». Ils peuvent « accéder de façon sécurisée à toutes les surfaces, vitrine, passage en caisse et pages post-achat comprises », ce qui compte parce que la caisse et la page de remerciement sont précisément là où un script collé dans le thème ne peut pas aller.
Conséquences pratiques :
- Aucune modification du thème. Le pixel est enregistré par l’application via la Web Pixel API. Rien n’est collé dans
theme.liquid, rien ne casse quand vous changez de thème, et la désinstallation de l’application retire le pixel. - Le consentement est géré par Shopify. Shopify n’exécute les rappels des pixels d’application qu’une fois que le choix de consentement du client l’autorise, donc vous n’avez pas à câbler l’appel OAIQ
oaiq("consent", ...)dans la logique de votre propre bandeau sur Shopify. - Le bac à sable limite ce qu’un script peut toucher. Un web pixel reçoit les événements standard de Shopify au lieu de lire le DOM. C’est un avantage : le pixel obtient des données propres et structurées pour
checkout_completed, y compris l’identifiant de commande que vous réutiliserez côté serveur.
Si vous préférez tout écrire vous-même, la référence du Measurement Pixel OpenAI documente le script https://bzrcdn.openai.com/sdk/oaiq.min.js, initialisé avec oaiq("init", { pixelId }), et des événements déclenchés avec oaiq("measure", eventName, eventData, options). Dans le bac à sable d’un web pixel, vous ne pouvez pas charger ce script comme le ferait un thème ; c’est pourquoi Convrail livre une implémentation native du même protocole pour ce bac à sable : elle lit oppref dans l’URL d’atterrissage, le conserve 7 jours dans le cookie first-party __oppref, et appelle le point de terminaison image d’OpenAI avec votre Pixel ID.
Étape 3 : faire correspondre les événements Shopify aux événements OAIQ
Les événements standard des web pixels Shopify comprennent page_viewed, product_viewed, collection_viewed, search_submitted, product_added_to_cart, checkout_started et checkout_completed. La Conversions API accepte ces types d’événements : appointment_scheduled, checkout_started, contents_viewed, custom, items_added, lead_created, order_created, page_viewed, registration_completed, subscription_created, trial_started, app_installed, app_opened. La correspondance appliquée par Convrail :
| Événement Shopify | Type d’événement OAIQ | Envoyé depuis |
|---|---|---|
page_viewed | page_viewed | navigateur |
product_viewed, collection_viewed | contents_viewed | navigateur |
search_submitted | page_viewed | navigateur |
product_added_to_cart | items_added | navigateur |
checkout_started | checkout_started | navigateur |
checkout_completed | order_created | navigateur et serveur, même identifiant d’événement |
Chaque événement navigateur porte un event_id. Pour les événements du haut de l’entonnoir, une valeur unique aléatoire suffit. Pour la commande, non : l’identifiant doit être une valeur que votre serveur peut reconstruire seul, sans parler au navigateur. order_<orderId> remplit cette condition, parce que l’identifiant de commande Shopify est présent à la fois dans l’événement checkout_completed et dans la charge utile du webhook orders/paid.
Étape 4 : envoyer la commande depuis votre serveur
Déclencher sur orders/paid, pas sur orders/create
Une commande peut être créée et jamais payée. orders/paid se déclenche quand le paiement est capturé, c’est-à-dire au moment où la conversion devient réelle. Construisez l’événement à l’arrivée de ce webhook.
Construire l’identifiant d’événement
order_5843921078 pour la commande Shopify 5843921078. La même chaîne que le pixel a utilisée au passage en caisse. Déterministe, donc une nouvelle livraison du webhook produit le même identifiant et OpenAI la traite comme un doublon plutôt que comme une seconde vente. Vos gestionnaires de webhooks doivent aussi être idempotents de votre côté : stockez l’événement une seule fois par triplet (boutique, identifiant d’événement, source).
Convertir le montant en unités mineures
L’API attend data.amount comme un entier dans l’unité mineure de la devise ; la référence donne l’exemple « utilisez 4200 pour 42,00 $ ». Quelques cas :
| Total Shopify | Devise | Exposant de l’unité mineure | data.amount |
|---|---|---|---|
| 129.90 | EUR | 2 | 12990 |
| 42.00 | USD | 2 | 4200 |
| 4200 | JPY | 0 | 4200 |
| 12.500 | KWD | 3 | 12500 |
Faites la multiplication avec l’exposant de la devise réelle, pas avec un 100 codé en dur. En JavaScript, l’arithmétique flottante sur 129.90 * 100 donne 12989.999999999998 ; analysez plutôt la chaîne comme un entier d’unités mineures, ou utilisez une bibliothèque décimale.
Hasher les identifiants client
L’objet user accepte des champs hashés sous forme de listes de condensats SHA-256 en hexadécimal minuscule de 64 caractères : emails_sha256, phone_numbers_sha256, external_ids_sha256, first_names_sha256, last_names_sha256. Les règles de normalisation de la référence :
- Email : retirer les espaces aux extrémités, passer en minuscules, puis hasher.
Jane.Doe@Example.comdevientjane.doe@example.com. - Téléphone : conserver « 8 à 15 chiffres après avoir retiré un
+initial, les zéros initiaux, les espaces, les parenthèses, les points et les tirets ».+33 6 12 34 56 78devient33612345678. - Noms : minuscules, retirer les espaces et la ponctuation ASCII.
Jean-Pierredevientjeanpierre.
Champs bruts (non hashés) : ip_address, user_agent, countries, regions, cities, postal_codes, obref, android_advertising_id. N’envoyez jamais un email ou un téléphone en clair, dans aucun champ, hashé ou non. La normalisation complète et le raisonnement RGPD sont dans Suivi côté serveur sans fuite de données personnelles.
La charge utile du lot
Un événement order_created avec les champs qu’une commande Shopify payée peut remplir. Les hashes ci-dessous sont les vrais condensats SHA-256 de jane.doe@example.com, 33612345678 et customer-7781.
{
"validate_only": false,
"integration_source": "convrail",
"events": [
{
"id": "order_5843921078",
"type": "order_created",
"timestamp_ms": 1788687000000,
"action_source": "web",
"source_url": "https://shop.example.com/checkouts/thank_you",
"oppref": "<valeur capturée dans l'URL d'atterrissage>",
"user": {
"emails_sha256": ["86e0b9e56c17cc4d12387e1949b85053fbe73bc3ce5a1188713a9d300cc6133d"],
"phone_numbers_sha256": ["8a3e7886c9335e82e02299fa3e87b46e2de3b0c63d56003e30a5029394a47661"],
"external_ids_sha256": ["91d8dedaef47ad2016875e1ecbb6f01c00bba531c0d4c3c5661ed3129ae381b0"],
"ip_address": "203.0.113.42",
"user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15",
"countries": ["FR"]
},
"data": {
"type": "contents",
"amount": 12990,
"currency": "EUR",
"contents": [
{
"id": "SKU-1001-M",
"group_id": "PRD-1001",
"name": "Linen shirt, medium",
"content_type": "product",
"quantity": 1,
"amount": 12990,
"currency": "EUR"
}
]
}
}
]
}
Règles de champs qui piègent souvent, toutes issues de la référence :
source_urlest « obligatoire pour les événements web quandaction_sourcevautweb».timestamp_ms« doit se situer dans les 7 derniers jours et au plus 10 minutes dans le futur ». Une dérive d’horloge serveur de quelques minutes est tolérée ; un rattrapage de plus d’une semaine est rejeté.eventscontient jusqu’à 1 000 événements, et « si un événement du lot échoue, tout le lot échoue ». Validez chaque événement avant de l’ajouter à un lot, sinon une commande malformée bloque 999 bonnes.opprefest « un identifiant d’attribution opaque, fourni par OpenAI ». Votre serveur ne l’a pas, sauf si le navigateur le lui relaie. Le pixel de Convrail poste une copie compacte de chaque événement à Convrail avec le même identifiant d’événement et l’opprefconservé, pour que la copie serveur puisse le porter.
Reprises et gestion des échecs
Sur 429 ou 5xx, rejouez avec un délai exponentiel et les mêmes identifiants d’événements ; la déduplication rend la reprise inoffensive. Sur 4xx, ne rejouez pas à l’aveugle : la charge utile est fausse et le restera. Conservez le lot en échec avec son statut HTTP et son texte d’erreur quelque part où vous pouvez le lire. Convrail range les lots épuisés dans une file des échecs et ouvre une alerte de santé par boutique, dans l’application et par email.
Étape 5 : tester avec validate_only
Mettez "validate_only": true en tête du lot. La référence le décrit ainsi : « valide les événements sans les enregistrer quand la valeur est true ». Envoyez une poignée de commandes à l’allure réelle et corrigez tout ce qu’OpenAI rejette : mauvais type de montant, source_url manquant, un hash qui ne fait pas 64 caractères hexadécimaux minuscules, un horodatage en secondes au lieu de millisecondes.
Convrail démarre chaque boutique en mode test pour cette raison. Les événements traversent tout le pipeline (webhook, hashage, mise en lots, appel HTTP) et OpenAI les vérifie, mais rien n’est compté. Quand l’écran Événements montre les copies navigateur et serveur d’une même commande arriver côte à côte avec le statut accepté, passez en production.
Ce qu’il faut vérifier dans les premières 24 heures
- Des paires, pas des événements isolés. Pour chaque commande réelle, un événement navigateur et un événement serveur avec le même
order_<orderId>. Un événement serveur seul sur chaque commande signifie que le pixel ne se déclenche pas sur la page de remerciement ; un événement navigateur seul signifie que le webhook ou l’appel à l’API échoue. - Montants et devise. Prenez trois commandes, comparez
data.amountau total Shopify en unités mineures. Boutiques multidevises : vérifiez une commande dans chaque devise de présentation. - Des hashes uniquement. Ouvrez une charge utile envoyée et cherchez
@. Il ne doit apparaître nulle part dansuser. opprefsur les commandes issues d’une publicité. Arrivez sur votre boutique par un vrai clic sur une publicité ChatGPT, passez une commande de test, confirmez que l’événement serveur porte l’identifiant.- Réception dans l’Ads Manager. Les événements de la Conversions API sont le signal que l’Ads Manager optimise et rapporte ; confirmez que l’onglet conversions montre les événements comme reçus. L’attribution suit les règles d’OpenAI : « L’attribution au clic utilise la fenêtre de clic configurée applicable. Les conversions après affichage utilisent une fenêtre fixe d’un jour. »
- Rien dans la file des échecs. S’il y a quelque chose, lisez le texte d’erreur avant de relancer quoi que ce soit.
Le faire vous-même ou utiliser Convrail
Une comparaison honnête. Le faire vous-même est tout à fait réaliste pour une équipe qui exploite déjà une pile de suivi côté serveur pour d’autres canaux.
| Sujet | Vous-même | Avec Convrail |
|---|---|---|
| Installation du pixel | Écrire et publier une extension web pixel Shopify, ou utiliser un pixel personnalisé dans les réglages Événements clients | Installé par l’application via la Web Pixel API, retiré à la désinstallation |
Relais de l’oppref vers le serveur | Construire votre propre point de terminaison pour recevoir l’oppref du navigateur et le joindre à la commande | Le pixel relaie une copie compacte de chaque événement avec le même identifiant d’événement |
| Webhook de commande, hashage, unités mineures | Votre code, vos tests ; l’exposant de devise et le piège des flottants sont à votre charge | Pris en charge, devises à zéro et trois décimales comprises |
| Lots et reprises | Votre file, votre délai exponentiel et votre stockage des échecs | Lots jusqu’à 1 000, délai exponentiel sur 429 et 5xx, file des échecs, alerte de santé |
| Garde-fou données personnelles | Votre discipline de revue de code | Un garde-fou automatique refuse toute charge utile contenant des données personnelles en clair, couvert par un test automatisé |
| Mode test | Basculer validate_only dans votre configuration | Chaque boutique démarre en mode test ; bascule dans les réglages |
| Visibilité | Vos journaux | Écran Événements avec source, statut et horodatage par événement |
| Coût | Temps d’ingénierie, maintenance continue à mesure que l’API évolue | Gratuit pendant l’accès anticipé ; aucune offre payante n’existe encore |
| Contrôle | Total | Vous dépendez d’un tiers sur un chemin critique pour vos conversions |
La dernière ligne est le vrai contre-argument. Si ChatGPT Ads devient un canal majeur et que vous possédez déjà un pipeline de suivi mature, y ajouter une destination de plus peut coûter moins cher à long terme qu’une dépendance. Si vous n’avez pas ce pipeline, c’est dans sa construction pour un seul canal que le temps s’en va.
Erreurs fréquentes
- Des identifiants d’événements aléatoires sur la commande. Le navigateur et le serveur doivent pouvoir produire le même identifiant sans se coordonner. Tout ce qui n’est pas dérivé de l’identifiant de commande casse la déduplication.
- Un montant en flottant ou en unités majeures.
129.9ou"129.90"n’est pas un entier en unités mineures. L’API attend12990. - Envoyer des emails en clair « pour aider le rapprochement ». L’objet
usern’accepte que des emails hashés, et un email en clair dans n’importe quel champ est une fuite de données vers un tiers. - Déclencher l’événement de commande sur
orders/create. Les commandes non payées et abandonnées deviennent des conversions. - Rejouer les réponses 4xx. Le lot est malformé ; le rejouer gaspille votre quota et masque le bogue.
- Sauter
validate_only. Le premier lot en production est un mauvais moment pour découvrir quetimestamp_msétait en secondes. - Coller le pixel dans le thème. Un script de thème ne suit pas le visiteur dans le passage en caisse Shopify ; un web pixel le fait (Shopify cite la caisse et les pages post-achat parmi les surfaces accessibles à un web pixel). Les extraits collés dans le thème manquent l’événement de commande côté navigateur.
Et maintenant
Installez l’application Shopify de Convrail, connectez votre Pixel ID et votre clé API, et regardez la première commande arriver deux fois avec un seul identifiant sur la page suivi des conversions ; les détails de l’intégration sont sur la page Shopify.
Sources
Questions fréquentes
Faut-il modifier mon thème Shopify pour installer le pixel ChatGPT Ads ?
Non. Un web pixel Shopify s'enregistre par la Web Pixel API et s'exécute dans le bac à sable fourni par Shopify : rien n'est collé dans theme.liquid. Désinstaller l'application qui l'a enregistré le retire complètement.
Envoyer la même commande depuis le pixel et la Conversions API la compte-t-elle deux fois ?
Non, tant que les deux copies portent le même identifiant sous le même Pixel ID. OpenAI déduplique sur le Pixel ID, le nom d'événement et l'identifiant d'événement, et conserve la première copie reçue.
Quel format de montant la Conversions API attend-elle pour une commande Shopify ?
Un entier dans l'unité mineure de la devise : 12990 pour 129,90 EUR et 4200 pour 42,00 USD. Les devises sans décimales comme le JPY s'envoient telles quelles, 4200 pour 4200 JPY.
Puis-je tester le suivi des conversions ChatGPT Ads sans polluer mes vraies données ?
Oui. Envoyez vos lots avec validate_only à true : OpenAI vérifie le format sans enregistrer les événements. Désactivez le drapeau une fois les charges utiles acceptées.
Combien de temps ai-je pour envoyer une commande à la Conversions API après qu'elle a eu lieu ?
L'horodatage de l'événement doit se situer dans les 7 derniers jours et au plus 10 minutes dans le futur. Un webhook déclenché au paiement laisse une large marge pour les reprises.