Skip to main content

user_data โ€” user identifiers for matching

user_data parameters are critical for the quality of signal sent to Meta CAPI and Google Ads Enhanced Conversions. They allow Meta/Google to match the event to a user account and attribute conversions correctly.

When to include user_dataโ€‹

Eventuser_data
purchaseREQUIRED โ€” send everything you have
begin_checkout, add_shipping_info, add_payment_infoIf user logged in or fields filled
generate_lead, sign_upREQUIRED
add_to_cart, view_item, etc.If user logged in
page_viewIf user logged in

The more identity signals you send (and the earlier in the funnel), the better the Meta matching and the more accurate the Google Ads attribution.

Send values in plain text โ€” do not hashโ€‹

โš ๏ธ Important: send user_data values in plain text. GTM Server hashes them with SHA-256 before sending to Meta. If you hash on the front-end, you'll hash twice โ†’ matching breaks.

window.dataLayer.push({
event: 'purchase',
ecommerce: { /* ... */ },
user_data: {
email: 'jane.doe@example.com',
phone: '+33612345678', // E.164 ideal
first_name: 'Jane',
last_name: 'Doe',
address: {
street: '12 Lilac Street',
city: 'Paris',
region: 'IDF', // state / region
postal_code: '75011',
country: 'FR' // ISO 3166-1 alpha-2
},
external_id: 'user_12345', // internal DB ID (anonymized OK)
date_of_birth: '1990-04-15', // YYYY-MM-DD
gender: 'f' // 'm' / 'f'
}
});

Supported fieldsโ€‹

FieldFormatMaps to MetaMaps to GAds
emaillowercase, trimmedememail
phoneE.164 with or without +phphone_number
first_namestringfnfirst_name
last_namestringlnlast_name
date_of_birthYYYY-MM-DDdb (โ†’ YYYYMMDD)โ€”
gender'm' or 'f'geโ€”
address.streetstringโ€”street_address
address.citystringctcity
address.regionstringstregion
address.postal_codestringzppostal_code
address.countryISO alpha-2 ('FR')countrycountry
external_idstringexternal_idโ€”

Normalize before pushingโ€‹

GTM Server normalizes + hashes, but you can boost match quality by:

{
email: rawEmail.trim().toLowerCase(),
phone: rawPhone.replace(/[^\d]/g, ''), // digits only, e.g. "33612345678"
first_name: firstName.trim().toLowerCase(),
last_name: lastName.trim().toLowerCase(),
address: {
city: city.trim().toLowerCase(),
postal_code: postalCode.replace(/\s/g, ''),
country: country.toUpperCase()
}
}

Helperโ€‹

function buildUserData(customer) {
if (!customer) return undefined;
const ud = {};
if (customer.email) ud.email = customer.email.trim().toLowerCase();
if (customer.phone) ud.phone = customer.phone.replace(/[^\d+]/g, '');
if (customer.firstName) ud.first_name = customer.firstName.trim().toLowerCase();
if (customer.lastName) ud.last_name = customer.lastName.trim().toLowerCase();
if (customer.id) ud.external_id = String(customer.id);
if (customer.birthDate) ud.date_of_birth = customer.birthDate; // YYYY-MM-DD
if (customer.gender) ud.gender = customer.gender.toLowerCase().startsWith('f') ? 'f' : 'm';

if (customer.address) {
ud.address = {};
if (customer.address.street) ud.address.street = customer.address.street.trim().toLowerCase();
if (customer.address.city) ud.address.city = customer.address.city.trim().toLowerCase();
if (customer.address.region) ud.address.region = customer.address.region.trim().toLowerCase();
if (customer.address.postalCode) ud.address.postal_code = customer.address.postalCode.replace(/\s/g, '');
if (customer.address.country) ud.address.country = customer.address.country.toUpperCase();
}

return ud;
}

The agency configures Google Consent Mode v2 in GTM. You don't have to filter events by consent state โ€” always push.

GTM will block PII forwarding to Meta/Google when the user denied ad_user_data or ad_storage. CAPI falls back to Meta's modeled signal.

โš ๏ธ The CMP must load before GTM and call gtag('consent', 'default', {...}) early. Coordinate the injection order with the agency.