Send user-provided data with User-ID using the Measurement Protocol

Similar to using gtag, it is possible to use the Google Analytics Measurement Protocol for Google Analytics 4 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 and googlemail.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:
  • remove all non digit characters
  • add + prefix
  • hash using SHA256 algorithm
  • encode with hex string format.
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:
  • remove digits and symbol characters
  • lowercase
  • remove leading and trailing spaces
  • hash using SHA256 algorithm
  • encode with hex string format.
user_data.address[].sha256_last_name string Hashed and encoded last name of the user. Normalized as such:
  • remove digits and symbol characters
  • lowercase
  • remove leading and trailing spaces
  • hash using SHA256 algorithm
  • encode with hex string format.
user_data.address[].sha256_street string Hashed and encoded street and number of the user. Normalized as such:
  • remove symbol characters
  • lowercase
  • remove leading and trailing spaces
  • hash using SHA256 algorithm
  • encode with hex string format.
user_data.address[].city string City for the address of the user. Normalized as such:
  • remove digits and symbol characters
  • lowercase
  • remove leading and trailing spaces.
user_data.address[].region string State or territory for the address of the user. Normalized as such:
  • remove digits and symbol characters
  • lowercase
  • remove leading and trailing spaces.
user_data.address[].postal_code string Postal code for the address of the user. Normalized as such:
  • remove . and ~ characters
  • remove leading and trailing spaces.
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
      }]
    }
  })
});