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โ
| Event | user_data |
|---|---|
purchase | REQUIRED โ send everything you have |
begin_checkout, add_shipping_info, add_payment_info | If user logged in or fields filled |
generate_lead, sign_up | REQUIRED |
add_to_cart, view_item, etc. | If user logged in |
page_view | If 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โ
| Field | Format | Maps to Meta | Maps to GAds |
|---|---|---|---|
email | lowercase, trimmed | em | email |
phone | E.164 with or without + | ph | phone_number |
first_name | string | fn | first_name |
last_name | string | ln | last_name |
date_of_birth | YYYY-MM-DD | db (โ YYYYMMDD) | โ |
gender | 'm' or 'f' | ge | โ |
address.street | string | โ | street_address |
address.city | string | ct | city |
address.region | string | st | region |
address.postal_code | string | zp | postal_code |
address.country | ISO alpha-2 ('FR') | country | country |
external_id | string | external_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;
}
GDPR / consentโ
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.