ServicesÀ proposNotesContact Me contacter →
EN FR
Note

La résolution d'identité pour le Customer 360

Comment lier les enregistrements de contacts CRM aux identifiants de cookies GA4 dans BigQuery — les trois stratégies de clé de jointure, le matching déterministe vs probabiliste, et les outils open source.

Planté
ga4bigquerydbtdata modelingdata engineering

Les données CRM et les données GA4 peuvent partager un projet ou un dataset BigQuery, mais ne partagent aucun identifiant. GA4 tracke les appareils via user_pseudo_id ; le CRM tracke les contacts par email et identifiant interne. Ces systèmes décrivent les mêmes utilisateurs à travers des prismes différents sans clé de jointure native.

La résolution d’identité crée cette clé de jointure dans le warehouse, là où les deux sources de données résident déjà.

La clé de jointure n’existe pas — il faut la créer

L’export BigQuery de GA4 fournit deux identifiants par événement : user_pseudo_id (un identifiant d’appareil basé sur les cookies, défini automatiquement) et user_id (un identifiant personnalisé que vous définissez vous-même via le dataLayer ou gtag). Votre CRM fournit des emails, numéros de téléphone et identifiants d’enregistrement internes. Aucun de ces éléments ne se recoupent par défaut.

Trois stratégies fonctionnent en pratique, et la plupart des équipes finissent par en combiner au moins deux.

Stratégie 1 : Capturer le client ID GA4 à la soumission de formulaire

Lorsqu’un visiteur remplit un formulaire sur votre site, un champ caché récupère la valeur user_pseudo_id et la transmet à votre CRM avec l’email et le nom. Cela nécessite une petite modification frontend — un snippet JavaScript qui lit le cookie GA4 et remplit un champ caché — et une propriété CRM personnalisée pour stocker la valeur.

// Lire le client ID GA4 depuis le cookie
function getGA4ClientId() {
const cookie = document.cookie
.split('; ')
.find(row => row.startsWith('_ga='));
if (cookie) {
// Format du cookie _ga : GA1.1.XXXXXXXXXX.XXXXXXXXXX
return cookie.split('.').slice(2).join('.');
}
return null;
}
// Remplir le champ de formulaire caché
document.querySelector('input[name="ga4_client_id"]').value = getGA4ClientId();

Le résultat est un lien direct et déterministe entre un contact CRM et son historique de navigation GA4. C’est là que la plupart des équipes commencent, car cela capture les leads issus du marketing qui ne se connectent jamais à un produit.

Stratégie 2 : Définir le user_id GA4 sur l’identifiant de contact CRM à la connexion

Si les utilisateurs se connectent à votre produit ou portail, poussez l’identifiant de contact CRM (ou un email hashé) dans le champ user_id de GA4 via le dataLayer :

dataLayer.push({
'user_id': 'crm_contact_12345' // ou un email hashé
});

GA4 applique ce user_id à tous les événements de la même session une fois qu’il est défini. La limitation : il ne s’applique pas rétroactivement aux sessions précédentes. Le user backstitching corrige cela dans le warehouse.

Stratégie 3 : Associer sur l’identifiant de transaction

Pour le e-commerce, si vos événements d’achat GA4 incluent un transaction_id qui apparaît également dans vos commandes CRM, vous pouvez faire la jointure directement dessus. Fiable lorsqu’il est disponible, mais cela ne donne l’identité que pour les utilisateurs convertissants — cela ne vous dit rien sur les visiteurs qui ont parcouru le site sans acheter.

Combiner les stratégies

Le champ de formulaire caché capture les leads issus du marketing qui ne se connectent jamais. L’approche user_id couvre les utilisateurs authentifiés entre les sessions. Ensemble, elles couvrent la majorité de votre trafic identifiable. L’identifiant de transaction comble les lacunes pour les conversions e-commerce.

Matching déterministe vs probabiliste

Une fois que vous disposez de clés de jointure pour certains utilisateurs, la question est de savoir comment étendre la couverture.

Le matching déterministe utilise des identifiants exacts : email, user_id, numéro de téléphone, identifiant de transaction. Si un user_pseudo_id GA4 a été capturé avec un email via une soumission de formulaire, et que cet email existe dans votre CRM, vous avez une correspondance. Sans ambiguïté. C’est toujours le point de départ.

Le SQL est simple — joindre vos données de soumission de formulaire (où user_pseudo_id a été capturé avec un email) avec vos contacts CRM sur l’email :

SELECT
form.user_pseudo_id,
form.email,
crm.contact_id AS crm_contact_id
FROM {{ ref('base__form_submissions') }} form
INNER JOIN {{ ref('base__crm__contacts') }} crm
ON LOWER(TRIM(form.email)) = LOWER(TRIM(crm.email))

Le LOWER(TRIM(...)) est défensif mais essentiel. Les incohérences de casse et d’espacement dans les emails sont omniprésentes entre les systèmes. Sans normalisation, vous perdez des correspondances qui devraient être triviales.

Le matching probabiliste — matching flou sur les noms, adresses IP partagées, similarité comportementale — offre une précision moindre. Un test d’un membre de la communauté dbt a montré que le matching de chaînes Jaro-Winkler en SQL atteignait un taux de fusion de 45 % sur un dataset de 6 500 enregistrements, contre 66 % avec le modèle probabiliste de Splink. L’écart est significatif, et les faux positifs dans la résolution d’identité causent de vrais problèmes en aval : un enregistrement fusionné combinant deux personnes différentes pollue chaque métrique construite dessus.

Pour la plupart des équipes analytics, le matching déterministe seul suffit. Le gain marginal du matching probabiliste justifie rarement le risque de faux positifs, sauf si vous disposez d’une équipe dédiée à la qualité des données pour examiner les correspondances.

Construire la table de mapping d’identité

Le résultat de la résolution d’identité est une table de mapping : une ligne par paire d’identités connue. Dans dbt, il s’agit d’un modèle intermédiaire :

-- int__identity_resolved.sql
{{ config(
materialized='incremental',
incremental_strategy='merge',
unique_key=['identifier_type', 'identifier_value']
) }}
WITH form_identities AS (
SELECT
'user_pseudo_id' AS identifier_type,
user_pseudo_id AS identifier_value,
email,
crm_contact_id,
submitted_at AS identified_at
FROM {{ ref('int__form_submissions_with_crm') }}
WHERE crm_contact_id IS NOT NULL
),
login_identities AS (
SELECT
'user_id' AS identifier_type,
user_id AS identifier_value,
NULL AS email,
crm_contact_id,
event_timestamp AS identified_at
FROM {{ ref('base__ga4__events') }}
WHERE user_id IS NOT NULL
AND event_name = 'login'
),
all_identities AS (
SELECT * FROM form_identities
UNION ALL
SELECT * FROM login_identities
)
SELECT
identifier_type,
identifier_value,
email,
crm_contact_id,
identified_at
FROM all_identities
QUALIFY ROW_NUMBER() OVER (
PARTITION BY identifier_type, identifier_value
ORDER BY identified_at DESC
) = 1

Le QUALIFY gère le cas où le même identifiant est associé à plusieurs contacts CRM dans le temps (par exemple, un appareil partagé). Prendre le mapping le plus récent est la valeur par défaut la plus sûre.

Ce modèle utilise merge comme stratégie incrémentale car les mappings d’identité sont relativement petits (au maximum une ligne par paire d’identités connue) mais se mettent à jour fréquemment à mesure que de nouvelles soumissions de formulaires et connexions créent de nouveaux mappings.

Outils open source

Pour les équipes ayant besoin d’un graphe d’identité plus complet au-delà du simple matching déterministe :

  • dbt-id-resolution de RudderStack construit des graphes d’identité itératifs en utilisant des modèles incrémentaux. Il gère l’identité transitive (si A = B et B = C, alors A = C), ce qui est difficile à implémenter correctement en SQL pur.
  • Hightouch et Brooklyn Data préconisent un pattern warehouse-native plus simple avec deux tables principales : events (chaque paire d’identifiants observée) et user_identities (identifiants canoniques résolus).
  • Splink est une bibliothèque Python pour le linkage d’enregistrements probabiliste qui peut s’exécuter sur BigQuery. À utiliser lorsque la couverture du matching déterministe est réellement insuffisante et que vous avez la capacité d’examiner les faux positifs.

Le choix entre ces outils et une solution faite maison dépend de la complexité de votre graphe d’identité. Si vous avez deux ou trois types d’identifiants et des clés de jointure propres, l’approche maison dans la table de mapping d’identité ci-dessus est plus simple et plus maintenable. Si vous avez cinq types d’identifiants ou plus avec des relations transitives, un package dédié représente une économie d’effort significative.

Ce qui rend cela difficile

La résolution d’identité ressemble à un problème de jointure, mais c’est vraiment un problème de qualité des données. Les défis qui font trébucher les équipes :

  • Normalisation des emails : jane@acme.com vs Jane@Acme.com vs jane@acme.com (espace en fin). Normaliser tôt.
  • Appareils partagés : deux personnes utilisant le même navigateur créent des mappings user_pseudo_id ambigus. Le mapping le plus récent est une heuristique, pas une solution.
  • Hygiène des données CRM : les contacts dupliqués dans le CRM produisent des correspondances dupliquées. Nettoyer le CRM d’abord, ou intégrer la déduplication dans votre couche d’identité.
  • Consentement : lier un identifiant de cookie à un contact CRM est une activité de traitement au sens du RGPD. Voir Privacy Constraints for Linked Analytics Data pour les implications de conformité.

La table de mapping d’identité est le fondement d’un modèle Customer 360. Les erreurs de résolution d’identité se propagent dans chaque métrique construite par-dessus.