Similar to using gtag, it is possible to use the Google Analytics Measurement Protocol to send user-provided data along with User-ID, which can be used to improve behavior and conversion measurement.
To send user-provided data along with a Measurement Protocol request, add the
user_data
parameter in the JSON payload. The user_id
parameter must be
present whenever user_data
is provided.
The Measurement Protocol is using the same normalization and hashing algorithm
as the
Google Ads API Enhanced Measurement
feature.
For privacy concerns, email addresses, phone numbers, first names, last names,
and street addresses must be hashed using the
SHA-256 algorithm before
being uploaded. The hashed value should be encoded in hex string format (string
object containing only hexadecimal digits), such as88d7ecb5c5b21d7b1
.
In order to standardize the hash results, prior to hashing one of these values you must:
- Remove leading and trailing whitespaces.
- Convert the text to lowercase.
- Format phone numbers according to the E164 standard.
- Remove all periods (.) that precede the domain name in
gmail.com
andgooglemail.com
email addresses.
JSON post body
Key | Type | Description |
---|---|---|
user_id | string | A unique identifier for a user. See User-ID for cross-platform analysis for more information on this identifier. |
user_data | object | Enhanced user data fields identifying a user. |
user_data.sha256_email_address[] | string array | Hashed and encoded email address of the user.
Normalized as such:
|
user_data.sha256_phone_number[] | string array | Hashed and encoded phone number of the user.
Normalized as such:
|
user_data.address[] | array | Identifies a user based on physical location. |
user_data.address[].sha256_first_name | string | Hashed and encoded first name of the user.
Normalized as such:
|
user_data.address[].sha256_last_name | string | Hashed and encoded last name of the user.
Normalized as such:
|
user_data.address[].sha256_street | string | Hashed and encoded street and number of the user.
Normalized as such:
|
user_data.address[].city | string | City for the address of the user.
Normalized as such:
|
user_data.address[].region | string | State or territory for the address of the user.
Normalized as such:
|
user_data.address[].postal_code | string | Postal code for the address of the user.
Normalized as such:
|
user_data.address[].country | string | Country code for the address of the user. Formatted according to ISO 3166-1 alpha-2 standard. |
See the Measurement Protocol reference documentation for more information about how the transport and payload are formatted.
Send User-provided Data
Unlike gtag, which automatically hashes sensitive user-provided data, the Measurement Protocol requires a developer to hash sensitive user-provided data using a secure one-way hashing algorithm called SHA256 and encode it using hex string format prior to calling the API.
All user data fields starting with the sha256
prefix in their name should be
only populated with hashed and hex-encoded values.
The following example code performs the necessary encryption and encoding steps:
Node.js
const { subtle } = require('crypto').webcrypto;
async function populateSensitiveUserData(value) {
const encoder = new TextEncoder();
// Convert a string value to UTF-8 encoded text.
const value_utf8 = encoder.encode(value);
// Compute the hash (digest) using the SHA-256 algorithm.
const hash_sha256 = await subtle.digest('SHA-256', value_utf8);
// Convert buffer to byte array.
const hash_array = Array.from(new Uint8Array(hash_sha256));
// Return a hex-encoded string.
return hash_array.map(b => b.toString(16).padStart(2, "0")).join('');
};
// Test the encryption function by calling it.
async function main() {
return await populateSensitiveUserData('<value>');
}
main()
.then(v => console.log(v))
.catch(err => console.error(err));
As a convenience shortcut, all repeated fields inside the user_data
object
(such as address
, sha256_email_address
, sha256_phone_number
) can be
passed a singular value instead of an array.
The following sample code calls the Measurement Protocol and passes user data along with User-ID.
Node.js
const measurement_id = 'G-XXXXXXXXXX';
const api_secret = '<secret_value>';
// Populate mock User Data using the `populateSensitiveUserData` function defined
// above.
const yourEmailSha256Variable = await populateSensitiveUserData('test@yourdomain.com');
const yourPhoneSha256Variable = await populateSensitiveUserData('+15555555555');
const yourFirstNameSha256Variable = await populateSensitiveUserData('john');
const yourLastNameSha256Variable = await populateSensitiveUserData('doe');
const yourStreetAddressSha256Variable = await populateSensitiveUserData('123 main street');
// Populate mock unencrypted user data.
const yourCityVariable = 'san francisco';
const yourRegionVariable = 'california';
const yourPostalCodeVariable = '94000';
const yourCountryVariable = 'US';
fetch(`https://www.google-analytics.com/mp/collect?measurement_id=${measurement_id}&api_secret=${api_secret}`, {
method: "POST",
body: JSON.stringify({
client_id: 'XXXXXXXXXX.YYYYYYYYYY',
user_id: "XXX",
events: [{
name: 'purchase'
}],
user_data: {
sha256_email_address: yourEmailSha256Variable,
sha256_phone_number: yourPhoneSha256Variable,
address: {
sha256_first_name: yourFirstNameSha256Variable,
sha256_last_name: yourLastNameSha256Variable,
sha256_street: yourStreetAddressSha256Variable,
city: yourCityVariable,
region: yourRegionVariable,
postal_code: yourPostalCodeVariable,
country: yourCountryVariable
}
}
})
});
Multiple values
Developers can optionally provide multiple values (up to 3 for phone and email and 2 for address) by using an array value rather than a string. If you capture more than one value, providing this will increase the likelihood of a match.
Node.js
const measurement_id = 'G-XXXXXXXXXX';
const api_secret = '<secret_value>';
fetch(`https://www.google-analytics.com/mp/collect?measurement_id=${measurement_id}&api_secret=${api_secret}`, {
method: "POST",
body: JSON.stringify({
client_id: 'XXXXXXXXXX.YYYYYYYYYY',
user_id: "XXX",
events: [{
name: 'purchase'
}],
user_data: {
sha256_email_address: [yourEmailSha256Variable1, yourEmailSha256Variable2],
sha256_phone_number: [yourPhoneSha256Variable1, yourPhoneSha256Variable2],
address: [{
sha256_first_name: yourFirstNameSha256Variable1,
sha256_last_name: yourLastNameSha256Variable1,
sha256_street: yourStreetAddressSha256Variable1,
city: yourCityVariable1,
region: yourRegionVariable1,
postal_code: yourPostalCodeVariable1,
country: yourCountryVariable1
},{
sha256_first_name: yourFirstNameSha256Variable2,
sha256_last_name: yourLastNameSha256Variable2,
sha256_street: yourStreetAddressSha256Variable2,
city: yourCityVariable2,
region: yourRegionVariable2,
postal_code: yourPostalCodeVariable2,
country: yourCountryVariable2
}]
}
})
});