Un lead remplit un formulaire sur votre site. Trois jours plus tard, un commercial conclut la vente par téléphone. Ou un client réserve en ligne et paie en magasin. L’argent est bien réel, mais GA4 ne le voit jamais : vos rapports, vos audiences et vos key events s’arrêtent à l’envoi du formulaire.

GA4 offre deux moyens officiels de renvoyer ces conversions hors ligne : un fichier CSV (importé à la main ou récupéré en SFTP) et le Measurement Protocol, une API HTTP. Les deux fonctionnent. Les deux échouent en silence si vous négligez un détail. Voici la version courte, pas à pas.

Avant de commencer : quelle méthode ?

Tout ce qui suit vient de la documentation de Google (liens en fin d’article). Voici comment les deux options se comparent :

Import de données d’événements (CSV / SFTP) Measurement Protocol
Où Admin > Collecte et modification des données > Importation de données HTTP POST depuis votre serveur
Qui le met en place Toute personne capable d’exporter un CSV du CRM Un développeur
Âge maximal de l’événement Les 2 jours calendaires précédents, plus aujourd’hui Antidaté jusqu’à 72 heures ; reçu dans les 48 heures suivant l’événement en ligne d’origine pour être traité avec lui (par exemple pour l’attribution)
Délai avant les rapports Jusqu’à 24 heures Temps réel, en quelques secondes
Limites principales 1 Go par fichier, 120 imports par propriété et par jour 25 événements par requête, corps inférieur à 130 ko

Attention à la fenêtre de temps : les deux méthodes attendent l’événement hors ligne dans les quelques jours. Si votre cycle de vente est plus long, l’événement arrivera dans GA4, mais ne comptez pas qu’il soit rattaché à la session en ligne d’origine.

Étape 1 : capturer le client_id au moment où l’utilisateur est en ligne

C’est l’étape qui décide si le projet fonctionne. GA4 reconnaît un visiteur web par son client_id (pour une app, l’app_instance_id). Cette jointure apporte la géographie, l’appareil, les réglages de confidentialité et des identifiants publicitaires comme GBRAID/WBRAID. Quand le lead ou la commande est créé en ligne, lisez le client_id depuis le tag Google et stockez-le dans votre CRM, à côté du numéro de lead ou de commande, avec la commande gtag get :

gtag('get', 'G-XXXXXXXXXX', 'client_id', (clientId) => {
  // send clientId to your backend with the form or order
});
gtag('get', 'G-XXXXXXXXXX', 'session_id', (sessionId) => {
  // optional: keep it too (see step 4)
});

Dans mes audits, c’est là que la plupart des projets de conversions hors ligne meurent : le CRM contient l’e-mail et le montant du deal, mais pas de client_id. Une règle : pas de données personnelles. Google interdit d’associer des informations personnellement identifiables à l’import d’événements, donc ni e-mail ni téléphone dans le fichier ou le payload.

Étape 2 : préparer GA4 à recevoir l’événement

  1. Choisir le nom de l’événement : un événement recommandé quand il convient (purchase, close_convert_lead), sinon un nom personnalisé de 40 caractères maximum (lettres, chiffres, underscores).
  2. Éviter les noms réservés (session_start, first_visit, user_engagement, préfixes ga_, google_, firebase_) : ces données ne sont pas importées.
  3. Le marquer comme key event : Admin > Affichage des données > Événements > Créer un événement, « Marquer comme key event », « Créer avec du code ». Rôle Spécialiste du marketing minimum.
  4. Créer les dimensions personnalisées pour les nouveaux paramètres (étape du deal, ID de magasin). Google indique jusqu’à 4 heures avant leur reconnaissance.

Étape 3, option A : importer les événements avec un fichier CSV

Construire le fichier

Pour un flux web, les colonnes obligatoires sont measurement_id, client_id et event_name (pour une app : firebase_app_id et app_instance_id). Le reste est facultatif : timestamp_micros (dans les 2 jours calendaires précédents plus aujourd’hui, sinon GA4 prend l’heure de l’import), user_id, les colonnes de consentement ad_user_data et ad_personalization (vivement recommandées), event_param.<nom>, user_property.<nom>, item1.item_id.

Un fichier minimal pour deux ventes hors ligne :

measurement_id,client_id,user_id,timestamp_micros,ad_user_data,ad_personalization,event_name,event_param.transaction_id,event_param.value,event_param.currency
G-12345ABCDE,1234567890.0987654321,1000001,1761904800000000,granted,granted,purchase,ORD-20481,1250.00,EUR
G-12345ABCDE,2345678901.9876543210,,1761908400000000,granted,denied,purchase,ORD-20482,480.00,EUR

Deux pièges : pas de colonne en double (deux champs user_id, par exemple), et un transaction_id réel et unique pour chaque purchase.

L’importer

Admin > Collecte et modification des données > Importation de données (rôle Éditeur minimum) > Créer une source d’importation > type Données d’événements (traité comme si le tag l’avait collecté) > import manuel de CSV, ou SFTP pour une récupération quotidienne, hebdomadaire ou mensuelle > vérifier le mappage > Importer. Pour les envois suivants, « Importer maintenant » : le fichier doit avoir les mêmes champs, ou un sous-ensemble. Pour d’autres colonnes, il faut supprimer la source et en créer une nouvelle. En SFTP, le serveur doit gérer ssh-rsa ou ssh-dss et vous devez autoriser la clé publique de la source.

Étape 3, option B : envoyer les événements avec le Measurement Protocol

Créer un secret d’API et valider d’abord

Admin > Flux de données > votre flux web > Secrets de l’API Measurement Protocol > Créer. Gardez-le côté serveur : exposé côté client, il permet à n’importe qui d’envoyer des données parasites à votre propriété.

Le Measurement Protocol répond 2xx même quand GA4 écarte l’événement. Testez donc sur le serveur de validation (/debug dans le chemin) avec validation_behavior réglé sur ENFORCE_RECOMMENDATIONS :

curl -X POST "https://www.google-analytics.com/debug/mp/collect?measurement_id=G-12345ABCDE&api_secret=YOUR_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "1234567890.0987654321",
    "user_id": "1000001",
    "timestamp_micros": 1761904800000000,
    "validation_behavior": "ENFORCE_RECOMMENDATIONS",
    "consent": { "ad_user_data": "GRANTED", "ad_personalization": "GRANTED" },
    "events": [{
      "name": "purchase",
      "params": {
        "transaction_id": "ORD-20481",
        "value": 1250.00,
        "currency": "EUR",
        "session_id": "1761901200",
        "engagement_time_msec": 100
      }
    }]
  }'

Un tableau validationMessages vide signifie que la structure est correcte. Le validateur ne vérifie pas l’api_secret, et ces événements n’apparaissent jamais dans les rapports.

Envoyer en production

Même requête sur /mp/collect, sans validation_behavior. Règles à retenir :

Google précise que le Measurement Protocol complète le tag, il ne le remplace pas : seul, avec des client_id inventés, il ne donne qu’un reporting partiel.

Étape 4 : vérifier que l’événement est arrivé

Erreurs courantes et comment les repérer dans GA4

Le client_id est inventé

Un ID aléatoire généré côté serveur est accepté, mais GA4 ne peut pas le relier à l’activité en ligne : plus de géographie ni d’appareil. En audit, je croise l’événement hors ligne avec le pays ou la catégorie d’appareil dans une exploration. Si presque tout tombe dans « (not set) », les ID ne correspondent pas à de vrais visiteurs. Corrigez à l’étape 1.

L’événement arrive trop tard

Comparez les dates de l’événement dans GA4 avec celles de l’export CRM. Si toute une semaine de deals apparaît le même jour, votre lot part trop tard.

L’achat est compté deux fois, ou pas du tout

GA4 déduplique les achats qui partagent le même transaction_id, sur les flux web uniquement. Un ID réutilisé pour des commandes différentes fait sous-compter, et une chaîne vide fait dédupliquer tous les achats qui la portent. Vérifiez avec une exploration ID de transaction et achats : chaque ID une seule fois, jamais « (not set) ». Les pages officielles utilisées ne décrivent aucune clé de fusion event_id : si votre conception en dépend, testez-la d’abord.

« 200 OK » mais rien dans GA4

Causes habituelles : un nom réservé, un nom avec tiret ou espace, un api_secret erroné, un measurement_id d’un autre flux. Passez le payload par /debug/mp/collect, puis testez avec debug_mode dans DebugView.

La dimension personnalisée est vide, ou les anciennes données ne bougent pas

Le paramètre a été importé avant la dimension, ou moins de 4 heures après sa création. Et l’import est joint au moment du traitement, pas aux données déjà traitées ; supprimer une source ne supprime pas les données importées. Testez sur un petit fichier.

Conclusion

Les conversions hors ligne GA4 se résument à trois choses : stocker le client_id quand le visiteur est en ligne, renvoyer l’événement dans la fenêtre de temps (quelques jours au plus), et le vérifier dans le temps réel, DebugView ou une exploration plutôt que de se fier à une réponse 200. L’import CSV convient à une routine hebdomadaire menée par l’équipe commerciale ou CRM. Le Measurement Protocol convient à un vrai pipeline déclenché par votre back-office dès que le deal est conclu.

Si vos données GA4 ne correspondent pas à votre CRM et que vous ne savez pas d’où vient l’écart, c’est exactement ce que je vérifie dans un audit de tracking.

Sources

Pour aller plus loin, lisez la version complète de l’article (en anglais) : GA4 Offline Conversions: A Step-by-Step Tutorial (CSV Import and Measurement Protocol).

Besoin d’aide sur ce sujet ? Consultant GA4 et audit analytics.

Vous doutez de votre tracking ?

Réserver un appel Ou commencez par l’audit analytics gratuit →