Java
// Creates a builder for constructing the enhancement adjustment.
ConversionAdjustment.Builder enhancementBuilder =
ConversionAdjustment.newBuilder().setAdjustmentType(ConversionAdjustmentType.ENHANCEMENT);
// Extracts user email, phone, and address info from the raw data, normalizes and hashes it,
// then wraps it in UserIdentifier objects.
// Creates a separate UserIdentifier object for each. The data in this example is hardcoded, but
// in your application you might read the raw data from an input file.
// IMPORTANT: Since the identifier attribute of UserIdentifier
// (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier) is a
// oneof
// (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set only ONE of
// hashedEmail, hashedPhoneNumber, mobileId, thirdPartyUserId, or addressInfo. Setting more
// than one of these attributes on the same UserIdentifier will clear all the other members
// of the oneof. For example, the following code is INCORRECT and will result in a
// UserIdentifier with ONLY a hashedPhoneNumber.
//
// UserIdentifier incorrectlyPopulatedUserIdentifier =
// UserIdentifier.newBuilder()
// .setHashedEmail("...")
// .setHashedPhoneNumber("...")
// .build();
Map<String, String> rawRecord =
ImmutableMap.<String, String>builder()
// Email address that includes a period (.) before the Gmail domain.
.put("email", "alex.2@example.com")
// Address that includes all four required elements: first name, last name, country
// code, and postal code.
.put("firstName", "Alex")
.put("lastName", "Quinn")
.put("countryCode", "US")
.put("postalCode", "94045")
// Phone number to be converted to E.164 format, with a leading '+' as required.
.put("phone", "+1 800 5550102")
// This example lets you input conversion details as arguments, but in reality you might
// store this data alongside other user data, so we include it in this sample user
// record.
.put("orderId", orderId)
.put("conversionActionId", Long.toString(conversionActionId))
.put("conversionDateTime", conversionDateTime)
.put("currencyCode", "USD")
.put("userAgent", userAgent)
.build();
// Creates a SHA256 message digest for hashing user identifiers in a privacy-safe way, as
// described at https://support.google.com/google-ads/answer/9888656.
MessageDigest sha256Digest = MessageDigest.getInstance("SHA-256");
// Creates a list for the user identifiers.
List<UserIdentifier> userIdentifiers = new ArrayList<>();
// Creates a user identifier using the hashed email address, using the normalize and hash method
// specifically for email addresses.
UserIdentifier emailIdentifier =
UserIdentifier.newBuilder()
// Optional: specify the user identifier source.
.setUserIdentifierSource(UserIdentifierSource.FIRST_PARTY)
// Uses the normalize and hash method specifically for email addresses.
.setHashedEmail(normalizeAndHashEmailAddress(sha256Digest, rawRecord.get("email")))
.build();
userIdentifiers.add(emailIdentifier);
// Checks if the record has a phone number, and if so, adds a UserIdentifier for it.
if (rawRecord.containsKey("phone")) {
UserIdentifier hashedPhoneNumberIdentifier =
UserIdentifier.newBuilder()
.setHashedPhoneNumber(normalizeAndHash(sha256Digest, rawRecord.get("phone"), true))
.build();
// Adds the hashed phone number identifier to the UserData object's list.
userIdentifiers.add(hashedPhoneNumberIdentifier);
}
// Checks if the record has all the required mailing address elements, and if so, adds a
// UserIdentifier for the mailing address.
if (rawRecord.containsKey("firstName")) {
// Checks if the record contains all the other required elements of a mailing address.
Set<String> missingAddressKeys = new HashSet<>();
for (String addressKey : new String[] {"lastName", "countryCode", "postalCode"}) {
if (!rawRecord.containsKey(addressKey)) {
missingAddressKeys.add(addressKey);
}
}
if (!missingAddressKeys.isEmpty()) {
System.out.printf(
"Skipping addition of mailing address information because the following required keys"
+ " are missing: %s%n",
missingAddressKeys);
} else {
// Creates an OfflineUserAddressInfo object that contains all the required elements of a
// mailing address.
OfflineUserAddressInfo addressInfo =
OfflineUserAddressInfo.newBuilder()
.setHashedFirstName(
normalizeAndHash(sha256Digest, rawRecord.get("firstName"), false))
.setHashedLastName(normalizeAndHash(sha256Digest, rawRecord.get("lastName"), false))
.setCountryCode(rawRecord.get("countryCode"))
.setPostalCode(rawRecord.get("postalCode"))
.build();
UserIdentifier addressIdentifier =
UserIdentifier.newBuilder().setAddressInfo(addressInfo).build();
// Adds the address identifier to the UserData object's list.
userIdentifiers.add(addressIdentifier);
}
}
// Adds the user identifiers to the enhancement adjustment.
enhancementBuilder.addAllUserIdentifiers(userIdentifiers);
C#
// Normalize and hash the raw data, then wrap it in UserIdentifier objects.
// Create a separate UserIdentifier object for each. The data in this example is
// hardcoded, but in your application you might read the raw data from an input file.
//
// IMPORTANT: Since the identifier attribute of UserIdentifier
// (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)
// is a oneof
// (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set
// only ONE of hashed_email, hashed_phone_number, mobile_id, third_party_user_id,
// or address-info. Setting more than one of these attributes on the same UserIdentifier
// will clear all the other members of the oneof. For example, the following code is
// INCORRECT and will result in a UserIdentifier with ONLY a hashed_phone_number:
// UserIdentifier incorrectlyPopulatedUserIdentifier = new UserIdentifier()
// {
// HashedEmail = "..."
// HashedPhoneNumber = "..."
// }
UserIdentifier addressIdentifier = new UserIdentifier()
{
AddressInfo = new OfflineUserAddressInfo()
{
HashedFirstName = NormalizeAndHash("Dana"),
HashedLastName = NormalizeAndHash("Quinn"),
HashedStreetAddress = NormalizeAndHash("1600 Amphitheatre Pkwy"),
City = "Mountain View",
State = "CA",
PostalCode = "94043",
CountryCode = "US"
},
// Optional: Specifies the user identifier source.
UserIdentifierSource = UserIdentifierSource.FirstParty
};
// Creates a user identifier using the hashed email address.
UserIdentifier emailIdentifier = new UserIdentifier()
{
UserIdentifierSource = UserIdentifierSource.FirstParty,
// Uses the normalize and hash method specifically for email addresses.
HashedEmail = NormalizeAndHashEmailAddress("dana@example.com")
};
// Adds the user identifiers to the enhancement adjustment.
enhancement.UserIdentifiers.AddRange(new[] { addressIdentifier, emailIdentifier });
PHP
// Creates the conversion enhancement.
$enhancement =
new ConversionAdjustment(['adjustment_type' => ConversionAdjustmentType::ENHANCEMENT]);
// Extracts user email, phone, and address info from the raw data, normalizes and hashes it,
// then wraps it in UserIdentifier objects.
// Creates a separate UserIdentifier object for each. The data in this example is hardcoded,
// but in your application you might read the raw data from an input file.
// IMPORTANT: Since the identifier attribute of UserIdentifier
// (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier) is a
// oneof
// (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set only ONE
// of hashedEmail, hashedPhoneNumber, mobileId, thirdPartyUserId, or addressInfo. Setting
// more than one of these attributes on the same UserIdentifier will clear all the other
// members of the oneof. For example, the following code is INCORRECT and will result in a
// UserIdentifier with ONLY a hashedPhoneNumber.
//
// $incorrectlyPopulatedUserIdentifier = new UserIdentifier([
// 'hashed_email' => '...',
// 'hashed_phone_number' => '...'
// ]);
$rawRecord = [
// Email address that includes a period (.) before the Gmail domain.
'email' => 'alex.2@example.com',
// Address that includes all four required elements: first name, last name, country
// code, and postal code.
'firstName' => 'Alex',
'lastName' => 'Quinn',
'countryCode' => 'US',
'postalCode' => '94045',
// Phone number to be converted to E.164 format, with a leading '+' as required.
'phone' => '+1 800 5550102',
// This example lets you input conversion details as arguments, but in reality you might
// store this data alongside other user data, so we include it in this sample user
// record.
'orderId' => $orderId,
'conversionActionId' => $conversionActionId,
'conversionDateTime' => $conversionDateTime,
'currencyCode' => 'USD'
];
// Creates a list for the user identifiers.
$userIdentifiers = [];
// Uses the SHA-256 hash algorithm for hashing user identifiers in a privacy-safe way, as
// described at https://support.google.com/google-ads/answer/9888656.
$hashAlgorithm = "sha256";
// Creates a user identifier using the hashed email address, using the normalize and hash
// method specifically for email addresses.
$emailIdentifier = new UserIdentifier([
// Uses the normalize and hash method specifically for email addresses.
'hashed_email' => self::normalizeAndHashEmailAddress(
$hashAlgorithm,
$rawRecord['email']
),
// Optional: Specifies the user identifier source.
'user_identifier_source' => UserIdentifierSource::FIRST_PARTY
]);
$userIdentifiers[] = $emailIdentifier;
// Checks if the record has a phone number, and if so, adds a UserIdentifier for it.
if (array_key_exists('phone', $rawRecord)) {
$hashedPhoneNumberIdentifier = new UserIdentifier([
'hashed_phone_number' => self::normalizeAndHash(
$hashAlgorithm,
$rawRecord['phone'],
true
)
]);
// Adds the hashed email identifier to the user identifiers list.
$userIdentifiers[] = $hashedPhoneNumberIdentifier;
}
// Checks if the record has all the required mailing address elements, and if so, adds a
// UserIdentifier for the mailing address.
if (array_key_exists('firstName', $rawRecord)) {
// Checks if the record contains all the other required elements of a mailing
// address.
$missingAddressKeys = [];
foreach (['lastName', 'countryCode', 'postalCode'] as $addressKey) {
if (!array_key_exists($addressKey, $rawRecord)) {
$missingAddressKeys[] = $addressKey;
}
}
if (!empty($missingAddressKeys)) {
printf(
"Skipping addition of mailing address information because the "
. "following required keys are missing: %s%s",
json_encode($missingAddressKeys),
PHP_EOL
);
} else {
// Creates an OfflineUserAddressInfo object that contains all the required
// elements of a mailing address.
$addressIdentifier = new UserIdentifier([
'address_info' => new OfflineUserAddressInfo([
'hashed_first_name' => self::normalizeAndHash(
$hashAlgorithm,
$rawRecord['firstName'],
false
),
'hashed_last_name' => self::normalizeAndHash(
$hashAlgorithm,
$rawRecord['lastName'],
false
),
'country_code' => $rawRecord['countryCode'],
'postal_code' => $rawRecord['postalCode']
])
]);
// Adds the address identifier to the user identifiers list.
$userIdentifiers[] = $addressIdentifier;
}
}
// Adds the user identifiers to the conversion.
$enhancement->setUserIdentifiers($userIdentifiers);
Python
# Extracts user email, phone, and address info from the raw data, normalizes
# and hashes it, then wraps it in UserIdentifier objects. Creates a separate
# UserIdentifier object for each. The data in this example is hardcoded, but
# in your application you might read the raw data from an input file.
# IMPORTANT: Since the identifier attribute of UserIdentifier
# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)
# is a oneof
# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must
# set only ONE of hashed_email, hashed_phone_number, mobile_id,
# third_party_user_id, or address_info. Setting more than one of these
# attributes on the same UserIdentifier will clear all the other members of
# the oneof. For example, the following code is INCORRECT and will result in
# a UserIdentifier with ONLY a hashed_phone_number:
#
# incorrectly_populated_user_identifier = client.get_type("UserIdentifier")
# incorrectly_populated_user_identifier.hashed_email = "...""
# incorrectly_populated_user_identifier.hashed_phone_number = "...""
raw_record = {
# Email address that includes a period (.) before the Gmail domain.
"email": "alex.2@example.com",
# Address that includes all four required elements: first name, last
# name, country code, and postal code.
"first_name": "Alex",
"last_name": "Quinn",
"country_code": "US",
"postal_code": "94045",
# Phone number to be converted to E.164 format, with a leading '+' as
# required.
"phone": "+1 800 5550102",
# This example lets you input conversion details as arguments, but in
# reality you might store this data alongside other user data, so we
# include it in this sample user record.
"order_id": order_id,
"conversion_action_id": conversion_action_id,
"conversion_date_time": conversion_date_time,
"currency_code": "USD",
"user_agent": user_agent,
}
# Constructs the enhancement adjustment.
conversion_adjustment = client.get_type("ConversionAdjustment")
conversion_adjustment.adjustment_type = (
client.enums.ConversionAdjustmentTypeEnum.ENHANCEMENT
)
# Creates a user identifier using the hashed email address, using the
# normalize and hash method specifically for email addresses.
email_identifier = client.get_type("UserIdentifier")
# Optional: Specifies the user identifier source.
email_identifier.user_identifier_source = (
client.enums.UserIdentifierSourceEnum.FIRST_PARTY
)
# Uses the normalize and hash method specifically for email addresses.
email_identifier.hashed_email = normalize_and_hash_email_address(
raw_record["email"]
)
# Adds the email identifier to the conversion adjustment.
conversion_adjustment.user_identifiers.append(email_identifier)
# Checks if the record has a phone number, and if so, adds a UserIdentifier
# for it.
if raw_record.get("phone") is not None:
phone_identifier = client.get_type("UserIdentifier")
phone_identifier.hashed_phone_number = normalize_and_hash(
raw_record["phone"]
)
# Adds the phone identifier to the conversion adjustment.
conversion_adjustment.user_identifiers.append(phone_identifier)
# Checks if the record has all the required mailing address elements, and if
# so, adds a UserIdentifier for the mailing address.
if raw_record.get("first_name") is not None:
# Checks if the record contains all the other required elements of a
# mailing address.
required_keys = ["last_name", "country_code", "postal_code"]
# Builds a new list of the required keys that are missing from
# raw_record.
missing_keys = [
key for key in required_keys if key not in raw_record.keys()
]
if len(missing_keys) > 0:
print(
"Skipping addition of mailing address information because the"
f"following required keys are missing: {missing_keys}"
)
else:
# Creates a user identifier using sample values for the user address,
# hashing where required.
address_identifier = client.get_type("UserIdentifier")
address_info = address_identifier.address_info
address_info.hashed_first_name = normalize_and_hash(
raw_record["first_name"]
)
address_info.hashed_last_name = normalize_and_hash(
raw_record["last_name"]
)
address_info.country_code = raw_record["country_code"]
address_info.postal_code = raw_record["postal_code"]
# Adds the address identifier to the conversion adjustment.
conversion_adjustment.user_identifiers.append(address_identifier)
Ruby
# Extracts user email, phone, and address info from the raw data, normalizes
# and hashes it, then wraps it in UserIdentifier objects. Creates a separate
# UserIdentifier object for each. The data in this example is hardcoded, but
# in your application you might read the raw data from an input file.
# IMPORTANT: Since the identifier attribute of UserIdentifier
# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)
# is a oneof
# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must
# set only ONE of hashed_email, hashed_phone_number, mobile_id,
# third_party_user_id, or address_info. Setting more than one of these
# attributes on the same UserIdentifier will clear all the other members of
# the oneof. For example, the following code is INCORRECT and will result in
# a UserIdentifier with ONLY a hashed_phone_number:
#
# incorrectly_populated_user_identifier.hashed_email = "...""
# incorrectly_populated_user_identifier.hashed_phone_number = "...""
raw_record = {
# Email address that includes a period (.) before the Gmail domain.
"email" => "alex.2@example.com",
# Address that includes all four required elements: first name, last
# name, country code, and postal code.
"first_name" => "Alex",
"last_name" => "Quinn",
"country_code" => "US",
"postal_code" => "94045",
# Phone number to be converted to E.164 format, with a leading '+' as
# required.
"phone" => "+1 800 5550102",
# This example lets you input conversion details as arguments, but in
# reality you might store this data alongside other user data, so we
# include it in this sample user record.
"order_id" => order_id,
"conversion_action_id" => conversion_action_id,
"conversion_date_time" => conversion_date_time,
"currency_code" => "USD",
"user_agent" => user_agent,
}
enhancement = client.resource.conversion_adjustment do |ca|
ca.conversion_action = client.path.conversion_action(customer_id, conversion_action_id)
ca.adjustment_type = :ENHANCEMENT
ca.order_id = order_id
# Sets the conversion date and time if provided. Providing this value is
# optional but recommended.
unless conversion_date_time.nil?
ca.gclid_date_time_pair = client.resource.gclid_date_time_pair do |pair|
pair.conversion_date_time = conversion_date_time
end
end
# Creates a user identifier using the hashed email address, using the
# normalize and hash method specifically for email addresses.
ca.user_identifiers << client.resource.user_identifier do |ui|
# Uses the normalize and hash method specifically for email addresses.
ui.hashed_email = normalize_and_hash_email(raw_record["email"])
# Optional: Specifies the user identifier source.
ui.user_identifier_source = :FIRST_PARTY
end
# Checks if the record has a phone number, and if so, adds a UserIdentifier
# for it.
unless raw_record["phone"].nil?
ca.user_identifiers << client.resource.user_identifier do |ui|
ui.hashed_phone_number = normalize_and_hash_email(raw_record["phone"])
end
end
# Checks if the record has all the required mailing address elements, and if
# so, adds a UserIdentifier for the mailing address.
unless raw_record["first_name"].nil?
# Checks if the record contains all the other required elements of a
# mailing address.
required_keys = ["last_name", "country_code", "postal_code"]
# Builds a new list of the required keys that are missing from
# raw_record.
missing_keys = required_keys - raw_record.keys
if missing_keys
puts(
"Skipping addition of mailing address information because the" \
"following required keys are missing: #{missing_keys}"
)
else
ca.user_identifiers << client.resource.user_identifier do |ui|
ui.address_info = client.resource.offline_user_address_info do |info|
# Certain fields must be hashed using SHA256 in order to handle
# identifiers in a privacy-safe way, as described at
# https://support.google.com/google-ads/answer/9888656.
info.hashed_first_name = normalize_and_hash( raw_record["first_name"])
info.hashed_last_name = normalize_and_hash( raw_record["last_name"])
info.postal_code = normalize_and_hash(raw_record["country_code"])
info.country_code = normalize_and_hash(raw_record["postal_code"])
end
end
end
end
Perl
# Construct the enhancement adjustment.
my $enhancement =
Google::Ads::GoogleAds::V16::Services::ConversionAdjustmentUploadService::ConversionAdjustment
->new({
adjustmentType => ENHANCEMENT
});
# Extract user email, phone, and address info from the raw data,
# normalize and hash it, then wrap it in UserIdentifier objects.
# Create a separate UserIdentifier object for each.
# The data in this example is hardcoded, but in your application
# you might read the raw data from an input file.
#
# IMPORTANT: Since the identifier attribute of UserIdentifier
# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)
# is a oneof
# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set
# only ONE of hashed_email, hashed_phone_number, mobile_id, third_party_user_id,
# or address-info. Setting more than one of these attributes on the same UserIdentifier
# will clear all the other members of the oneof. For example, the following code is
# INCORRECT and will result in a UserIdentifier with ONLY a hashed_phone_number:
#
# my $incorrect_user_identifier = Google::Ads::GoogleAds::V16::Common::UserIdentifier->new({
# hashedEmail => '...',
# hashedPhoneNumber => '...',
# });
my $raw_record = {
# Email address that includes a period (.) before the Gmail domain.
email => 'alex.2@example.com',
# Address that includes all four required elements: first name, last
# name, country code, and postal code.
firstName => 'Alex',
lastName => 'Quinn',
countryCode => 'US',
postalCode => '94045',
# Phone number to be converted to E.164 format, with a leading '+' as
# required.
phone => '+1 800 5550102',
# This example lets you input conversion details as arguments,
# but in reality you might store this data alongside other user data,
# so we include it in this sample user record.
orderId => $order_id,
conversionActionId => $conversion_action_id,
conversionDateTime => $conversion_date_time,
currencyCode => "USD",
userAgent => $user_agent,
};
my $user_identifiers = [];
# Create a user identifier using the hashed email address, using the normalize
# and hash method specifically for email addresses.
my $hashed_email = normalize_and_hash_email_address($raw_record->{email});
push(
@$user_identifiers,
Google::Ads::GoogleAds::V16::Common::UserIdentifier->new({
hashedEmail => $hashed_email,
# Optional: Specify the user identifier source.
userIdentifierSource => FIRST_PARTY
}));
# Check if the record has a phone number, and if so, add a UserIdentifier for it.
if (defined $raw_record->{phone}) {
# Add the hashed phone number identifier to the list of UserIdentifiers.
push(
@$user_identifiers,
Google::Ads::GoogleAds::V16::Common::UserIdentifier->new({
hashedPhoneNumber => normalize_and_hash($raw_record->{phone}, 1)}));
}
# Confirm the record has all the required mailing address elements, and if so, add
# a UserIdentifier for the mailing address.
if (defined $raw_record->{firstName}) {
my $required_keys = ["lastName", "countryCode", "postalCode"];
my $missing_keys = [];
foreach my $key (@$required_keys) {
if (!defined $raw_record->{$key}) {
push(@$missing_keys, $key);
}
}
if (@$missing_keys) {
print
"Skipping addition of mailing address information because the following"
. "keys are missing: "
. join(",", @$missing_keys);
} else {
push(
@$user_identifiers,
Google::Ads::GoogleAds::V16::Common::UserIdentifier->new({
addressInfo =>
Google::Ads::GoogleAds::V16::Common::OfflineUserAddressInfo->new({
# First and last name must be normalized and hashed.
hashedFirstName => normalize_and_hash($raw_record->{firstName}),
hashedLastName => normalize_and_hash($raw_record->{lastName}),
# Country code and zip code are sent in plain text.
countryCode => $raw_record->{countryCode},
postalCode => $raw_record->{postalCode},
})}));
}
}
# Add the user identifiers to the enhancement adjustment.
$enhancement->{userIdentifiers} = $user_identifiers;