Remarketing Samples

The code samples below provide examples of common remarketing functions using the AdWords API. Client Library.

Create a remarketing user list (audience)

// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Google.Api.Ads.AdWords.Lib;
using Google.Api.Ads.AdWords.v201809;

using System;
using System.Collections.Generic;
using System.Linq;

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201809
{
    /// <summary>
    /// This code example illustrates how to create a user list a.k.a. audience.
    /// </summary>
    public class AddAudience : ExampleBase
    {
        /// <summary>
        /// Main method, to run this code example as a standalone application.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        public static void Main(string[] args)
        {
            AddAudience codeExample = new AddAudience();
            Console.WriteLine(codeExample.Description);
            try
            {
                codeExample.Run(new AdWordsUser());
            }
            catch (Exception e)
            {
                Console.WriteLine("An exception occurred while running this code example. {0}",
                    ExampleUtilities.FormatException(e));
            }
        }

        /// <summary>
        /// Returns a description about the code example.
        /// </summary>
        public override string Description
        {
            get
            {
                return "This code example illustrates how to create a user list a.k.a. audience.";
            }
        }

        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="user">The AdWords user.</param>
        public void Run(AdWordsUser user)
        {
            using (AdwordsUserListService userListService =
                (AdwordsUserListService) user.GetService(AdWordsService.v201809
                    .AdwordsUserListService))
                using (ConversionTrackerService conversionTrackerService =
                    (ConversionTrackerService) user.GetService(AdWordsService.v201809
                        .ConversionTrackerService))
                {
                    BasicUserList userList = new BasicUserList
                    {
                        name = "Mars cruise customers #" + ExampleUtilities.GetRandomString(),
                        description = "A list of mars cruise customers in the last year.",
                        status = UserListMembershipStatus.OPEN,
                        membershipLifeSpan = 365
                    };

                    UserListConversionType conversionType = new UserListConversionType
                    {
                        name = userList.name
                    };
                    userList.conversionTypes = new UserListConversionType[]
                    {
                        conversionType
                    };

                    // Optional: Set the user list status.
                    userList.status = UserListMembershipStatus.OPEN;

                    // Create the operation.
                    UserListOperation operation = new UserListOperation
                    {
                        operand = userList,
                        @operator = Operator.ADD
                    };

                    try
                    {
                        // Add the user list.
                        UserListReturnValue retval = userListService.mutate(new UserListOperation[]
                        {
                            operation
                        });

                        UserList newUserList = retval.value[0];

                        Console.WriteLine("User list with name '{0}' and id '{1}' was added.",
                            newUserList.name, newUserList.id);

                        List<string> conversionIds = new List<string>();
                        Array.ForEach(userList.conversionTypes,
                            delegate(UserListConversionType item)
                            {
                                conversionIds.Add(item.id.ToString());
                            });

                        // Create the selector.
                        Selector selector = new Selector()
                        {
                            fields = new string[]
                            {
                                ConversionTracker.Fields.Id,
                                ConversionTracker.Fields.GoogleGlobalSiteTag,
                                ConversionTracker.Fields.GoogleEventSnippet
                            },
                            predicates = new Predicate[]
                            {
                                Predicate.In(ConversionTracker.Fields.Id, conversionIds.ToArray())
                            }
                        };

                        // Get all conversion trackers.
                        ConversionTrackerPage page = conversionTrackerService.get(selector);

                        if (page != null && page.entries != null)
                        {
                            foreach (ConversionTracker tracker in page.entries)
                            {
                                Console.WriteLine(
                                    "Google global site tag:\n{0}\nGoogle event snippet:\n{1}",
                                    tracker.googleGlobalSiteTag, tracker.googleGlobalSiteTag);
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        throw new System.ApplicationException(
                            "Failed to add user lists (a.k.a. audiences).", e);
                    }
                }
        }
    }
}

Create an AdWords conversion tracker and add to it upload conversions

// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Google.Api.Ads.AdWords.Lib;
using Google.Api.Ads.AdWords.v201809;

using System;
using System.Collections.Generic;

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201809
{
    /// <summary>
    /// This code example adds an AdWords conversion tracker and an upload conversion tracker.
    /// </summary>
    public class AddConversionTrackers : ExampleBase
    {
        /// <summary>
        /// Main method, to run this code example as a standalone application.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        public static void Main(string[] args)
        {
            AddConversionTrackers codeExample = new AddConversionTrackers();
            Console.WriteLine(codeExample.Description);
            try
            {
                codeExample.Run(new AdWordsUser());
            }
            catch (Exception e)
            {
                Console.WriteLine("An exception occurred while running this code example. {0}",
                    ExampleUtilities.FormatException(e));
            }
        }

        /// <summary>
        /// Returns a description about the code example.
        /// </summary>
        public override string Description
        {
            get
            {
                return "This code example adds an AdWords conversion tracker and an upload " +
                    "conversion tracker.";
            }
        }

        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="user">The AdWords user.</param>
        public void Run(AdWordsUser user)
        {
            using (ConversionTrackerService conversionTrackerService =
                (ConversionTrackerService) user.GetService(AdWordsService.v201809
                    .ConversionTrackerService))
            {
                List<ConversionTracker> conversionTrackers = new List<ConversionTracker>();

                // Create an Adwords conversion tracker.
                AdWordsConversionTracker adWordsConversionTracker = new AdWordsConversionTracker
                {
                    name =
                        "Earth to Mars Cruises Conversion #" + ExampleUtilities.GetRandomString(),
                    category = ConversionTrackerCategory.DEFAULT,

                    // Set optional fields.
                    status = ConversionTrackerStatus.ENABLED,
                    viewthroughLookbackWindow = 15,
                    defaultRevenueValue = 23.41,
                    alwaysUseDefaultRevenueValue = true
                };
                conversionTrackers.Add(adWordsConversionTracker);

                // Create an upload conversion for offline conversion imports.
                UploadConversion uploadConversion = new UploadConversion
                {
                    // Set an appropriate category. This field is optional, and will be set to
                    // DEFAULT if not mentioned.
                    category = ConversionTrackerCategory.LEAD,
                    name = "Upload Conversion #" + ExampleUtilities.GetRandomString(),
                    viewthroughLookbackWindow = 30,
                    ctcLookbackWindow = 90,

                    // Optional: Set the default currency code to use for conversions
                    // that do not specify a conversion currency. This must be an ISO 4217
                    // 3-character currency code such as "EUR" or "USD".
                    // If this field is not set on this UploadConversion, AdWords will use
                    // the account's currency.
                    defaultRevenueCurrencyCode = "EUR",

                    // Optional: Set the default revenue value to use for conversions
                    // that do not specify a conversion value. Note that this value
                    // should NOT be in micros.
                    defaultRevenueValue = 2.50
                };

                // Optional: To upload fractional conversion credits, mark the upload conversion
                // as externally attributed. See
                // https://developers.google.com/adwords/api/docs/guides/conversion-tracking#importing_externally_attributed_conversions
                // to learn more about importing externally attributed conversions.

                // uploadConversion.isExternallyAttributed = true;

                conversionTrackers.Add(uploadConversion);

                try
                {
                    // Create operations.
                    List<ConversionTrackerOperation> operations =
                        new List<ConversionTrackerOperation>();
                    foreach (ConversionTracker conversionTracker in conversionTrackers)
                    {
                        operations.Add(new ConversionTrackerOperation()
                        {
                            @operator = Operator.ADD,
                            operand = conversionTracker
                        });
                    }

                    // Add conversion tracker.
                    ConversionTrackerReturnValue retval =
                        conversionTrackerService.mutate(operations.ToArray());

                    // Display the results.
                    if (retval != null && retval.value != null)
                    {
                        foreach (ConversionTracker conversionTracker in retval.value)
                        {
                            Console.WriteLine(
                                "Conversion with ID {0}, name '{1}', status '{2}' and " +
                                "category '{3}' was added.", conversionTracker.id,
                                conversionTracker.name, conversionTracker.status,
                                conversionTracker.category);
                            if (conversionTracker is AdWordsConversionTracker)
                            {
                                AdWordsConversionTracker newAdWordsConversionTracker =
                                    (AdWordsConversionTracker) conversionTracker;
                                Console.WriteLine(
                                    "Google global site tag:\n{0}\nGoogle event snippet:\n{1}",
                                    newAdWordsConversionTracker.googleGlobalSiteTag,
                                    newAdWordsConversionTracker.googleEventSnippet);
                            }
                        }
                    }
                    else
                    {
                        Console.WriteLine("No conversion trackers were added.");
                    }
                }
                catch (Exception e)
                {
                    throw new System.ApplicationException("Failed to add conversion trackers.", e);
                }
            }
        }
    }
}

Create and populate a user list

// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Google.Api.Ads.AdWords.Lib;
using Google.Api.Ads.AdWords.v201809;

using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201809
{
    /// <summary>
    /// This code example adds a user list (a.k.a. audience) and uploads hashed
    /// email addresses to populate the list.
    ///
    /// <p>
    /// <em>Note:</em> It may take up to several hours for the list to be
    /// populated with members. Email addresses must be associated with a Google
    /// account. For privacy purposes, the user list size will show as zero until
    /// the list has at least 1000 members. After that, the size will be rounded
    /// to the two most significant digits.
    /// </p>
    /// </summary>
    public class AddCrmBasedUserList : ExampleBase
    {
        private static readonly string[] EMAILS = new string[]
        {
            "customer1@example.com",
            "customer2@example.com",
            " Customer3@example.com "
        };

        private const string FIRST_NAME = "John";
        private const string LAST_NAME = "Doe";
        private const string COUNTRY_CODE = "US";
        private const string ZIP_CODE = "10001";

        private SHA256 digest = SHA256.Create();

        /// <summary>
        /// Main method, to run this code example as a standalone application.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        public static void Main(string[] args)
        {
            AddCrmBasedUserList codeExample = new AddCrmBasedUserList();
            Console.WriteLine(codeExample.Description);
            try
            {
                codeExample.Run(new AdWordsUser());
            }
            catch (Exception e)
            {
                Console.WriteLine("An exception occurred while running this code example. {0}",
                    ExampleUtilities.FormatException(e));
            }
        }

        /// <summary>
        /// Returns a description about the code example.
        /// </summary>
        public override string Description
        {
            get
            {
                return "This code example adds a user list (a.k.a. audience) and " +
                    "uploads hashed email addresses to populate the list.";
            }
        }

        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="user">The AdWords user.</param>
        public void Run(AdWordsUser user)
        {
            using (AdwordsUserListService userListService =
                (AdwordsUserListService) user.GetService(AdWordsService.v201809
                    .AdwordsUserListService))
            {
                // Create a user list.
                CrmBasedUserList userList = new CrmBasedUserList()
                {
                    name = "Customer relationship management list #" +
                        ExampleUtilities.GetRandomString(),
                    description = "A list of customers that originated from email addresses",

                    // CRM - based user lists can use a membershipLifeSpan of 10000 to indicate
                    // unlimited; otherwise normal values apply.
                    membershipLifeSpan = 30L,
                    uploadKeyType = CustomerMatchUploadKeyType.CONTACT_INFO
                };

                // Create operation.
                UserListOperation operation = new UserListOperation()
                {
                    operand = userList,
                    @operator = Operator.ADD
                };

                try
                {
                    // Add user list.
                    UserListReturnValue result = userListService.mutate(new UserListOperation[]
                    {
                        operation
                    });

                    Console.WriteLine(
                        "Created new user list with name = '{0}' and id = " + "'{1}'.",
                        result.value[0].name, result.value[0].id);

                    // Get user list ID.
                    long userListId = result.value[0].id;

                    // Prepare the emails for upload.
                    List<Member> memberList = new List<Member>();

                    // Hash normalized email addresses based on SHA-256 hashing algorithm.
                    string[] emailHashes = new string[EMAILS.Length];
                    for (int i = 0; i < EMAILS.Length; i++)
                    {
                        Member member = new Member
                        {
                            hashedEmail = ToSha256String(digest, ToNormalizedEmail(EMAILS[i]))
                        };
                        memberList.Add(member);
                    }

                    ;

                    // Add a user by first and last name.
                    AddressInfo addressInfo = new AddressInfo
                    {
                        // First and last name must be normalized and hashed.
                        hashedFirstName = ToSha256String(digest, FIRST_NAME),
                        hashedLastName = ToSha256String(digest, LAST_NAME),
                        // Country code and zip code are sent in plaintext.
                        zipCode = ZIP_CODE,
                        countryCode = COUNTRY_CODE
                    };

                    Member memberByAddress = new Member
                    {
                        addressInfo = addressInfo
                    };
                    memberList.Add(memberByAddress);

                    // Create operation to add members to the user list based on email
                    // addresses.
                    MutateMembersOperation mutateMembersOperation = new MutateMembersOperation()
                    {
                        operand = new MutateMembersOperand()
                        {
                            userListId = userListId,
                            membersList = memberList.ToArray()
                        },
                        @operator = Operator.ADD
                    };

                    // Add members to the user list based on email addresses.
                    MutateMembersReturnValue mutateMembersResult = userListService.mutateMembers(
                        new MutateMembersOperation[]
                        {
                            mutateMembersOperation
                        });

                    // Display results.
                    // Reminder: it may take several hours for the list to be populated
                    // with members.
                    foreach (UserList userListResult in mutateMembersResult.userLists)
                    {
                        Console.WriteLine(
                            "Email addresses were added to user list with " +
                            "name '{0}' and id '{1}'.", userListResult.name, userListResult.id);
                    }
                }
                catch (Exception e)
                {
                    throw new System.ApplicationException(
                        "Failed to add user lists " +
                        "(a.k.a. audiences) and upload email addresses.", e);
                }
            }
        }

        /// <summary>
        /// Hash email address using SHA-256 hashing algorithm.
        /// </summary>
        /// <param name="digest">Provides the algorithm for SHA-256.</param>
        /// <param name="email">The email address to hash.</param>
        /// <returns>Hash email address using SHA-256 hashing algorithm.</returns>
        private static string ToSha256String(SHA256 digest, string email)
        {
            byte[] digestBytes = digest.ComputeHash(Encoding.UTF8.GetBytes(email));
            // Convert the byte array into an unhyphenated hexadecimal string.
            return BitConverter.ToString(digestBytes).Replace("-", string.Empty);
        }

        /// <summary>
        /// Removes leading and trailing whitespace and converts all characters to
        /// lower case.
        /// </summary>
        /// <param name="email">The email address to normalize.</param>
        /// <returns>A normalized copy of the string.</returns>
        private static string ToNormalizedEmail(string email)
        {
            return email.Trim().ToLower();
        }
    }
}

Create rule-based user lists

// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Google.Api.Ads.AdWords.Lib;
using Google.Api.Ads.AdWords.v201809;

using System;
using System.Collections.Generic;

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201809
{
    /// <summary>
    /// This code example adds two rule-based remarketing user lists: one with no
    /// site visit date restrictions, and another that will only include users
    /// who visit your site in the next six months. See
    /// https://developers.google.com/adwords/api/docs/guides/rule-based-remarketing
    /// to learn more about rule based remarketing.
    /// </summary>
    public class AddRuleBasedRemarketingList : ExampleBase
    {
        private const string DATE_FORMAT_STRING = "yyyyMMdd";

        /// <summary>
        /// Main method, to run this code example as a standalone application.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        public static void Main(string[] args)
        {
            AddRuleBasedRemarketingList codeExample = new AddRuleBasedRemarketingList();
            Console.WriteLine(codeExample.Description);
            try
            {
                codeExample.Run(new AdWordsUser());
            }
            catch (Exception e)
            {
                Console.WriteLine("An exception occurred while running this code example. {0}",
                    ExampleUtilities.FormatException(e));
            }
        }

        /// <summary>
        /// Returns a description about the code example.
        /// </summary>
        public override string Description
        {
            get
            {
                return "This code example adds two rule-based remarketing user lists: one with " +
                    "no site visit date restrictions, and another that will only include users " +
                    "who visit your site in the next six months. See " +
                    "https://developers.google.com/adwords/api/docs/guides/rule-based-remarketing" +
                    " to learn more about rule based remarketing.";
            }
        }

        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="user">The AdWords user.</param>
        public void Run(AdWordsUser user)
        {
            using (AdwordsUserListService userListService =
                (AdwordsUserListService) user.GetService(AdWordsService.v201809
                    .AdwordsUserListService))
            {
                // First rule item group - users who visited the checkout page and had
                // more than one item in their shopping cart.
                StringRuleItem checkoutStringRuleItem = new StringRuleItem
                {
                    key = new StringKey
                    {
                        name = "ecomm_pagetype"
                    },
                    op = StringRuleItemStringOperator.EQUALS,
                    value = "checkout"
                };

                RuleItem checkoutRuleItem = new RuleItem
                {
                    Item = checkoutStringRuleItem
                };

                NumberRuleItem cartSizeNumberRuleItem = new NumberRuleItem
                {
                    key = new NumberKey
                    {
                        name = "cartsize"
                    },
                    op = NumberRuleItemNumberOperator.GREATER_THAN,
                    value = 1
                };

                RuleItem cartSizeRuleItem = new RuleItem
                {
                    Item = cartSizeNumberRuleItem
                };

                // Combine the two rule items into a RuleItemGroup so AdWords will AND
                // their rules together.
                RuleItemGroup checkoutMultipleItemGroup = new RuleItemGroup
                {
                    items = new RuleItem[]
                    {
                        checkoutRuleItem,
                        cartSizeRuleItem
                    }
                };

                // Second rule item group - users who check out within the next 3 months.
                DateRuleItem startDateDateRuleItem = new DateRuleItem
                {
                    key = new DateKey
                    {
                        name = "checkoutdate"
                    },
                    op = DateRuleItemDateOperator.AFTER,
                    value = DateTime.Now.ToString(DATE_FORMAT_STRING)
                };
                RuleItem startDateRuleItem = new RuleItem
                {
                    Item = startDateDateRuleItem
                };

                DateRuleItem endDateDateRuleItem = new DateRuleItem
                {
                    key = new DateKey
                    {
                        name = "checkoutdate"
                    },
                    op = DateRuleItemDateOperator.BEFORE,
                    value = DateTime.Now.AddMonths(3).ToString(DATE_FORMAT_STRING)
                };
                RuleItem endDateRuleItem = new RuleItem
                {
                    Item = endDateDateRuleItem
                };

                // Combine the date rule items into a RuleItemGroup.
                RuleItemGroup checkedOutNextThreeMonthsItemGroup = new RuleItemGroup
                {
                    items = new RuleItem[]
                    {
                        startDateRuleItem,
                        endDateRuleItem
                    }
                };

                // Combine the rule item groups into a Rule so AdWords knows how to apply the rules.
                Rule rule = new Rule
                {
                    groups = new RuleItemGroup[]
                    {
                        checkoutMultipleItemGroup,
                        checkedOutNextThreeMonthsItemGroup
                    },

                    // ExpressionRuleUserLists can use either CNF Or DNF For matching. CNF means
                    // 'at least one item in each rule item group must match', and DNF means 'at
                    // least one entire rule item group must match'.
                    // DateSpecificRuleUserList only supports DNF. You can also omit the rule
                    // type altogether To Default To DNF.
                    ruleType = UserListRuleTypeEnumsEnum.DNF
                };

                // Third and fourth rule item groups.
                // Visitors of a page who visited another page. See
                // https://developers.google.com/adwords/api/docs/reference/latest/AdwordsUserListService.StringKey
                // for more details.
                StringKey urlStringKey = new StringKey()
                {
                    name = "url__"
                };

                StringRuleItem site1StringRuleItem = new StringRuleItem
                {
                    key = urlStringKey,
                    op = StringRuleItemStringOperator.EQUALS,
                    value = "example.com/example1"
                };
                RuleItem site1RuleItem = new RuleItem
                {
                    Item = site1StringRuleItem
                };

                StringRuleItem site2StringRuleItem = new StringRuleItem
                {
                    key = (urlStringKey),
                    op = (StringRuleItemStringOperator.EQUALS),
                    value = ("example.com/example2")
                };
                RuleItem site2RuleItem = new RuleItem
                {
                    Item = (site2StringRuleItem)
                };

                // Create two RuleItemGroups to show that a visitor browsed two sites.
                RuleItemGroup site1RuleItemGroup = new RuleItemGroup
                {
                    items = new RuleItem[]
                    {
                        site1RuleItem
                    }
                };
                RuleItemGroup site2RuleItemGroup = new RuleItemGroup
                {
                    items = new RuleItem[]
                    {
                        site2RuleItem
                    }
                };

                // Create two rules to show that a visitor browsed two sites.
                Rule userVisitedSite1Rule = new Rule
                {
                    groups = new RuleItemGroup[]
                    {
                        site1RuleItemGroup
                    }
                };

                Rule userVisitedSite2Rule = new Rule
                {
                    groups = new RuleItemGroup[]
                    {
                        site2RuleItemGroup
                    }
                };

                // Create the user list with no restrictions on site visit date.
                ExpressionRuleUserList expressionUserList = new ExpressionRuleUserList();
                string creationTimeString = DateTime.Now.ToString("yyyyMMdd_HHmmss");
                expressionUserList.name =
                    "Expression based user list created at " + creationTimeString;
                expressionUserList.description = "Users who checked out in three month window OR " +
                    "visited the checkout page with more than one item in their cart.";
                expressionUserList.rule = rule;

                // Optional: Set the prepopulationStatus to REQUESTED to include past users
                // in the user list.
                expressionUserList.prepopulationStatus =
                    RuleBasedUserListPrepopulationStatus.REQUESTED;

                // Create the user list restricted to users who visit your site within
                // the next six months.
                DateTime startDate = DateTime.Now;
                DateTime endDate = startDate.AddMonths(6);

                DateSpecificRuleUserList dateUserList = new DateSpecificRuleUserList
                {
                    name = "Date rule user list created at " + creationTimeString,
                    description = string.Format(
                        "Users who visited the site between {0} and " +
                        "{1} and checked out in three month window OR visited the checkout page " +
                        "with more than one item in their cart.",
                        startDate.ToString(DATE_FORMAT_STRING),
                        endDate.ToString(DATE_FORMAT_STRING)),
                    rule = rule,

                    // Set the start and end dates of the user list.
                    startDate = startDate.ToString(DATE_FORMAT_STRING),
                    endDate = endDate.ToString(DATE_FORMAT_STRING)
                };

                // Create the user list where "Visitors of a page who did visit another page".
                // To create a user list where "Visitors of a page who did not visit another
                // page", change the ruleOperator from AND to AND_NOT.
                CombinedRuleUserList combinedRuleUserList = new CombinedRuleUserList
                {
                    name = "Combined rule user list created at " + creationTimeString,
                    description = "Users who visited two sites.",
                    leftOperand = userVisitedSite1Rule,
                    rightOperand = userVisitedSite2Rule,
                    ruleOperator = CombinedRuleUserListRuleOperator.AND
                };

                // Create operations to add the user lists.
                List<UserListOperation> operations = new List<UserListOperation>();
                foreach (UserList userList in new UserList[]
                {
                    expressionUserList,
                    dateUserList,
                    combinedRuleUserList
                })
                {
                    UserListOperation operation = new UserListOperation
                    {
                        operand = userList,
                        @operator = Operator.ADD
                    };
                    operations.Add(operation);
                }

                try
                {
                    // Submit the operations.
                    UserListReturnValue result = userListService.mutate(operations.ToArray());

                    // Display the results.
                    foreach (UserList userListResult in result.value)
                    {
                        Console.WriteLine(
                            "User list added with ID {0}, name '{1}', status '{2}', " +
                            "list type '{3}', accountUserListStatus '{4}', description '{5}'.",
                            userListResult.id, userListResult.name, userListResult.status,
                            userListResult.listType, userListResult.accountUserListStatus,
                            userListResult.description);
                    }
                }
                catch (Exception e)
                {
                    throw new System.ApplicationException("Failed to add rule based user lists.",
                        e);
                }
            }
        }
    }
}

Import conversion adjustments for existing conversions

// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Google.Api.Ads.AdWords.Lib;
using Google.Api.Ads.AdWords.v201809;

using System;

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201809
{
    /// <summary>
    /// This code example demonstrates adjusting one conversion, but you can add more than one
    /// operation in a single mutate request.
    /// </summary>
    public class UploadOfflineConversionAdjustments : ExampleBase
    {
        /// <summary>
        /// Main method, to run this code example as a standalone application.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        public static void Main(string[] args)
        {
            UploadOfflineConversionAdjustments codeExample =
                new UploadOfflineConversionAdjustments();
            Console.WriteLine(codeExample.Description);
            try
            {
                string conversionName = "INSERT_CONVERSION_NAME_HERE";
                string gclid = "INSERT_GOOGLE_CLICK_ID_HERE";
                string conversionTime = "INSERT_CONVERSION_TIME_HERE";
                OfflineConversionAdjustmentType adjustmentType =
                    (OfflineConversionAdjustmentType) Enum.Parse(
                        typeof(OfflineConversionAdjustmentType), "INSERT_ADJUSTMENT_TYPE_HERE");
                string adjustmentTime = "INSERT_ADJUSTMENT_TIME_HERE";
                double adjustedValue = double.Parse("INSERT_ADJUSTED_VALUE_HERE");

                codeExample.Run(new AdWordsUser(), conversionName, gclid, conversionTime,
                    adjustmentType, adjustmentTime, adjustedValue);
            }
            catch (Exception e)
            {
                Console.WriteLine("An exception occurred while running this code example. {0}",
                    ExampleUtilities.FormatException(e));
            }
        }

        /// <summary>
        /// Returns a description about the code example.
        /// </summary>
        public override string Description
        {
            get
            {
                return "This code example demonstrates adjusting one conversion, but you can add " +
                    "more than one operation in a single mutate request.";
            }
        }

        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="user">The AdWords user.</param>
        /// <param name="conversionName">Name of the conversion to make adjustments.</param>
        /// <param name="gclid">The google click ID for the adjustment.</param>
        /// <param name="conversionTime">The conversion time.</param>
        /// <param name="adjustmentType">The type of conversion adjustment.</param>
        /// <param name="adjustmentTime">The conversion adjustment time.</param>
        /// <param name="adjustedValue">The conversion adjustment value.</param>
        public void Run(AdWordsUser user, string conversionName, string gclid,
            string conversionTime, OfflineConversionAdjustmentType adjustmentType,
            string adjustmentTime, double adjustedValue)
        {
            using (OfflineConversionAdjustmentFeedService service =
                (OfflineConversionAdjustmentFeedService) user.GetService(AdWordsService.v201809
                    .OfflineConversionAdjustmentFeedService))
            {
                // Associate conversion adjustments with the existing named conversion
                // tracker. The GCLID should have been uploaded before with a
                // conversion.
                GclidOfflineConversionAdjustmentFeed feed =
                    new GclidOfflineConversionAdjustmentFeed()
                    {
                        conversionName = conversionName,
                        googleClickId = gclid,
                        conversionTime = conversionTime,
                        adjustmentType = adjustmentType,
                        adjustmentTime = adjustmentTime,
                        adjustedValue = adjustedValue
                    };

                // Create the operation.
                var operation = new OfflineConversionAdjustmentFeedOperation()
                {
                    @operator = Operator.ADD,
                    operand = feed
                };

                try
                {
                    // Issue a request to the servers for adjustments of the conversion.
                    OfflineConversionAdjustmentFeedReturnValue retval = service.mutate(
                        new OfflineConversionAdjustmentFeedOperation[]
                        {
                            operation
                        });
                    GclidOfflineConversionAdjustmentFeed updatedFeed =
                        (GclidOfflineConversionAdjustmentFeed) retval.value[0];
                    Console.WriteLine(
                        "Uploaded conversion adjustment value of '{0}' for Google " +
                        "Click ID '{1}'.", updatedFeed.conversionName, updatedFeed.googleClickId);
                }
                catch (Exception e)
                {
                    throw new System.ApplicationException("Failed to update conversion adjustment.",
                        e);
                }
            }
        }
    }
}

Import offline call conversions

// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Google.Api.Ads.AdWords.Lib;
using Google.Api.Ads.AdWords.v201809;

using System;

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201809
{
    /// <summary>
    /// This code example imports offline call conversion values for calls related to the
    /// ads in your account.
    /// </summary>
    public class UploadOfflineCallConversions : ExampleBase
    {
        /// <summary>
        /// Main method, to run this code example as a standalone application.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        public static void Main(string[] args)
        {
            string conversionName = "INSERT_CONVERSION_NAME_HERE";

            // For times use the format yyyyMMdd HHmmss tz. For more details on formats, see:
            // https://developers.google.com/adwords/api/docs/appendix/codes-formats#date-and-time-formats
            // For time zones, see:
            // https://developers.google.com/adwords/api/docs/appendix/codes-formats#timezone-ids

            //  The conversion time should be after the call start time.
            string conversionTime = "INSERT_CONVERSION_TIME_HERE";
            string callStartTime = "INSERT_CALL_START_TIME_HERE";

            string callerId = "INSERT_CALLER_ID_HERE";
            double conversionValue = double.Parse("INSERT_CONVERSION_VALUE_HERE");

            UploadOfflineCallConversions codeExample = new UploadOfflineCallConversions();
            Console.WriteLine(codeExample.Description);
            try
            {
                codeExample.Run(new AdWordsUser(), conversionName, callStartTime, callerId,
                    conversionTime, conversionValue);
            }
            catch (Exception e)
            {
                Console.WriteLine("An exception occurred while running this code example. {0}",
                    ExampleUtilities.FormatException(e));
            }
        }

        /// <summary>
        /// Returns a description about the code example.
        /// </summary>
        public override string Description
        {
            get
            {
                return
                    "This code example imports offline call conversion values for calls related " +
                    "to the ads in your account.";
            }
        }

        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="user">The AdWords user.</param>
        /// <param name="conversionName">The name of the call conversion to be updated.</param>
        /// <param name="callStartTime">The call start time.</param>
        /// <param name="conversionValue">The conversion value to be uploaded.</param>
        /// <param name="callerId">The caller ID to be uploaded.</param>
        /// <param name="conversionTime">The conversion time, in yyyymmdd hhmmss
        /// format.</param>
        public void Run(AdWordsUser user, string conversionName, string callStartTime,
            string callerId, string conversionTime, double conversionValue)
        {
            using (OfflineCallConversionFeedService offlineCallConversionFeedService =
                (OfflineCallConversionFeedService) user.GetService(AdWordsService.v201809
                    .OfflineCallConversionFeedService))
            {
                // Associate offline call conversions with the existing named conversion tracker.
                // If this tracker was newly created, it may be a few hours before it can accept
                // conversions.
                OfflineCallConversionFeed feed = new OfflineCallConversionFeed
                {
                    callerId = callerId,
                    callStartTime = callStartTime,
                    conversionName = conversionName,
                    conversionTime = conversionTime,
                    conversionValue = conversionValue
                };

                OfflineCallConversionFeedOperation offlineCallConversionOperation =
                    new OfflineCallConversionFeedOperation
                    {
                        @operator = Operator.ADD,
                        operand = feed
                    };

                try
                {
                    // This example uploads only one call conversion, but you can upload
                    // multiple call conversions by passing additional operations.
                    OfflineCallConversionFeedReturnValue offlineCallConversionReturnValue =
                        offlineCallConversionFeedService.mutate(
                            new OfflineCallConversionFeedOperation[]
                            {
                                offlineCallConversionOperation
                            });

                    // Display results.
                    foreach (OfflineCallConversionFeed feedResult in
                        offlineCallConversionReturnValue.value)
                    {
                        Console.WriteLine(
                            "Uploaded offline call conversion value of {0} for caller ID '{1}'.",
                            feedResult.conversionValue, feedResult.callerId);
                    }
                }
                catch (Exception e)
                {
                    throw new System.ApplicationException(
                        "Failed to upload offline call conversions.", e);
                }
            }
        }

    }
}

Import offline click conversions

// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Google.Api.Ads.AdWords.Lib;
using Google.Api.Ads.AdWords.v201809;

using System;

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201809
{
    /// <summary>
    /// This code example imports offline conversion values for specific clicks to
    /// your account. To get Google Click ID for a click, run
    /// CLICK_PERFORMANCE_REPORT. To set up a conversion tracker, run the
    /// AddConversionTrackers.cs example.
    /// </summary>
    public class UploadOfflineConversions : ExampleBase
    {
        /// <summary>
        /// Main method, to run this code example as a standalone application.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        public static void Main(string[] args)
        {
            string conversionName = "INSERT_CONVERSION_NAME_HERE";
            // GCLID needs to be newer than 30 days.
            string gClId = "INSERT_GOOGLE_CLICK_ID_HERE";
            //  The conversion time should be higher than the click time.
            string conversionTime = "INSERT_CONVERSION_TIME_HERE";
            double conversionValue = double.Parse("INSERT_CONVERSION_VALUE_HERE");

            UploadOfflineConversions codeExample = new UploadOfflineConversions();
            Console.WriteLine(codeExample.Description);
            try
            {
                codeExample.Run(new AdWordsUser(), conversionName, gClId, conversionTime,
                    conversionValue);
            }
            catch (Exception e)
            {
                Console.WriteLine("An exception occurred while running this code example. {0}",
                    ExampleUtilities.FormatException(e));
            }
        }

        /// <summary>
        /// Returns a description about the code example.
        /// </summary>
        public override string Description
        {
            get
            {
                return "This code example imports offline conversion values for specific clicks " +
                    "to your account. To get Google Click ID for a click, run " +
                    "CLICK_PERFORMANCE_REPORT.  To set up a conversion tracker, run the " +
                    "AddConversionTrackers.cs example.";
            }
        }

        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="user">The AdWords user.</param>
        /// <param name="conversionName">The name of the upload conversion to be
        /// created.</param>
        /// <param name="gClid">The Google Click ID of the click for which offline
        /// conversions are uploaded.</param>
        /// <param name="conversionValue">The conversion value to be uploaded.
        /// </param>
        /// <param name="conversionTime">The conversion time, in yyyymmdd hhmmss
        /// format.</param>
        public void Run(AdWordsUser user, string conversionName, string gClid,
            string conversionTime, double conversionValue)
        {
            using (OfflineConversionFeedService offlineConversionFeedService =
                (OfflineConversionFeedService) user.GetService(AdWordsService.v201809
                    .OfflineConversionFeedService))
            {
                try
                {
                    // Associate offline conversions with the existing named conversion tracker. If
                    // this tracker was newly created, it may be a few hours before it can accept
                    // conversions.
                    OfflineConversionFeed feed = new OfflineConversionFeed
                    {
                        conversionName = conversionName,
                        conversionTime = conversionTime,
                        conversionValue = conversionValue,
                        googleClickId = gClid
                    };

                    // Optional: To upload fractional conversion credits, set the external
                    // attribution model and credit. To use this feature, your conversion tracker
                    // should be marked as externally attributed. See
                    // https://developers.google.com/adwords/api/docs/guides/conversion-tracking#importing_externally_attributed_conversions
                    // to learn more about importing externally attributed conversions.

                    // feed.externalAttributionModel = "Linear";
                    // feed.externalAttributionCredit = 0.3;

                    OfflineConversionFeedOperation offlineConversionOperation =
                        new OfflineConversionFeedOperation
                        {
                            @operator = Operator.ADD,
                            operand = feed
                        };

                    OfflineConversionFeedReturnValue offlineConversionRetval =
                        offlineConversionFeedService.mutate(new OfflineConversionFeedOperation[]
                        {
                            offlineConversionOperation
                        });

                    OfflineConversionFeed newFeed = offlineConversionRetval.value[0];

                    Console.WriteLine(
                        "Uploaded offline conversion value of {0} for Google Click ID = " +
                        "'{1}' to '{2}'.", newFeed.conversionValue, newFeed.googleClickId,
                        newFeed.conversionName);
                }
                catch (Exception e)
                {
                    throw new System.ApplicationException("Failed upload offline conversions.", e);
                }
            }
        }

    }
}

Upload offline data for store sales transactions

// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Google.Api.Ads.AdWords.Lib;
using Google.Api.Ads.AdWords.v201809;

using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201809
{
    /// <summary>
    /// This code example shows how to upload offline data for store sales transactions.
    /// </summary>
    public class UploadOfflineData : ExampleBase
    {
        private SHA256 digest = SHA256.Create();

        /// <summary>
        /// Main method, to run this code example as a standalone application.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        public static void Main(string[] args)
        {
            // The external upload ID can be any number that you use to keep track of your uploads.
            long externalUploadId = long.Parse("INSERT_EXTERNAL_UPLOAD_ID");

            // Insert the conversion type name that you'd like to attribute this upload to.
            string conversionName = "INSERT_CONVERSION_NAME";

            // Insert email addresses below for creating user identifiers.
            string[] emailAddresses =
            {
                "EMAIL_ADDRESS_1",
                "EMAIL_ADDRESS_2"
            };

            // Insert advertiser upload time. // For times, use the format yyyyMMdd HHmmss tz. For
            // more details on formats, see:
            // https://developers.google.com/adwords/api/docs/appendix/codes-formats#date-and-time-formats
            // For time zones, see:
            // https://developers.google.com/adwords/api/docs/appendix/codes-formats#timezone-ids
            string advertiserUploadTime = "INSERT_ADVERTISER_UPLOAD_TIME";

            // Insert bridge map version ID.
            string bridgeMapVersionId = "INSERT_BRIDGEMAP_VERSION_ID";

            // Insert partner ID.
            int partnerId = int.Parse("INSERT_PARTNER_ID");

            // Specify the upload type (STORE_SALES_UPLOAD_FIRST_PARTY or
            // STORE_SALES_UPLOAD_THIRD_PARTY)
            OfflineDataUploadType uploadType =
                (OfflineDataUploadType) Enum.Parse(typeof(OfflineDataUploadType),
                    "INSERT_UPLOAD_TYPE");

            UploadOfflineData codeExample = new UploadOfflineData();
            Console.WriteLine(codeExample.Description);
            try
            {
                codeExample.Run(new AdWordsUser(), conversionName, externalUploadId, emailAddresses,
                    advertiserUploadTime, bridgeMapVersionId, uploadType, partnerId);
            }
            catch (Exception e)
            {
                Console.WriteLine("An exception occurred while running this code example. {0}",
                    ExampleUtilities.FormatException(e));
            }
        }

        /// <summary>
        /// Returns a description about the code example.
        /// </summary>
        public override string Description
        {
            get
            {
                return "This code example shows how to upload offline data for store sales " +
                    "transactions.";
            }
        }

        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="user">The AdWords user.</param>
        /// <param name="conversionName">The conversion type name that you'd like to attribute this
        /// upload to.</param>
        /// <param name="externalUploadId">The external upload ID can be any number that you use to
        /// keep track of your uploads.</param>
        /// <param name="emailAddresses">The email addresses for creating user identifiers.</param>
        /// <param name="advertiserUploadTime">The advertiser upload time. For times, use the format
        /// yyyyMMdd HHmmss tz. For more details on formats, see:
        /// https://developers.google.com/adwords/api/docs/appendix/codes-formats#date-and-time-formats
        /// For time zones, see:
        /// https://developers.google.com/adwords/api/docs/appendix/codes-formats#timezone-ids
        /// </param>
        /// <param name="bridgeMapVersionId">The version ID of the bridge map.</param>
        /// <param name="uploadType">The type of data upload.</param>
        /// <param name="partnerId">The partner ID</param>
        public void Run(AdWordsUser user, string conversionName, long externalUploadId,
            string[] emailAddresses, string advertiserUploadTime, string bridgeMapVersionId,
            OfflineDataUploadType uploadType, int partnerId)
        {
            using (OfflineDataUploadService offlineDataUploadService =
                (OfflineDataUploadService) user.GetService(AdWordsService.v201809
                    .OfflineDataUploadService))
            {
                offlineDataUploadService.RequestHeader.partialFailure = true;

                // Create the first offline data row for upload.
                // This transaction occurred 7 days ago with amount of 200 USD.
                DateTime transactionTime1 = DateTime.Now;
                transactionTime1.AddDays(-7);
                long transactionAmount1 = 200000000;
                string transactionCurrencyCode1 = "USD";
                UserIdentifier[] userIdentifierList1 = new UserIdentifier[]
                {
                    CreateUserIdentifier(OfflineDataUploadUserIdentifierType.HASHED_EMAIL,
                        emailAddresses[0]),
                    CreateUserIdentifier(OfflineDataUploadUserIdentifierType.STATE, "New York")
                };
                OfflineData offlineData1 = CreateOfflineDataRow(transactionTime1,
                    transactionAmount1, transactionCurrencyCode1, conversionName,
                    userIdentifierList1);

                // Create the second offline data row for upload.
                // This transaction occurred 14 days ago with amount of 450 EUR.
                DateTime transactionTime2 = DateTime.Now;
                transactionTime2.AddDays(-14);
                long transactionAmount2 = 450000000;
                string transactionCurrencyCode2 = "EUR";
                UserIdentifier[] userIdentifierList2 = new UserIdentifier[]
                {
                    CreateUserIdentifier(OfflineDataUploadUserIdentifierType.HASHED_EMAIL,
                        emailAddresses[1]),
                    CreateUserIdentifier(OfflineDataUploadUserIdentifierType.STATE, "California")
                };
                OfflineData offlineData2 = CreateOfflineDataRow(transactionTime2,
                    transactionAmount2, transactionCurrencyCode2, conversionName,
                    userIdentifierList2);

                // Create offline data upload object.
                OfflineDataUpload offlineDataUpload = new OfflineDataUpload
                {
                    externalUploadId = externalUploadId,
                    offlineDataList = new OfflineData[]
                    {
                        offlineData1,
                        offlineData2
                    },

                    // Set the type and metadata of this upload.
                    uploadType = uploadType
                };
                StoreSalesUploadCommonMetadata storeSalesMetaData = null;

                switch (uploadType)
                {
                    case OfflineDataUploadType.STORE_SALES_UPLOAD_FIRST_PARTY:
                        storeSalesMetaData = new FirstPartyUploadMetadata()
                        {
                            loyaltyRate = 1,
                            transactionUploadRate = 1
                        };
                        break;

                    case OfflineDataUploadType.STORE_SALES_UPLOAD_THIRD_PARTY:
                        storeSalesMetaData = new ThirdPartyUploadMetadata()
                        {
                            loyaltyRate = 1.0,
                            transactionUploadRate = 1.0,
                            advertiserUploadTime = advertiserUploadTime,
                            validTransactionRate = 1.0,
                            partnerMatchRate = 1.0,
                            partnerUploadRate = 1.0,
                            bridgeMapVersionId = bridgeMapVersionId,
                            partnerId = partnerId
                        };
                        break;
                }

                UploadMetadata uploadMetadata = new UploadMetadata
                {
                    Item = storeSalesMetaData
                };
                offlineDataUpload.uploadMetadata = uploadMetadata;

                // Create an offline data upload operation.
                OfflineDataUploadOperation offlineDataUploadOperation =
                    new OfflineDataUploadOperation
                    {
                        @operator = Operator.ADD,
                        operand = offlineDataUpload
                    };

                // Keep the operations in an array, so it may be reused later for error processing.
                List<OfflineDataUploadOperation>
                    operations = new List<OfflineDataUploadOperation>();
                operations.Add(offlineDataUploadOperation);

                try
                {
                    // Upload offline data to the server.
                    OfflineDataUploadReturnValue result =
                        offlineDataUploadService.mutate(operations.ToArray());
                    offlineDataUpload = result.value[0];

                    // Print the upload ID and status.
                    Console.WriteLine(
                        "Uploaded offline data with external upload ID {0}, " +
                        "and upload status {1}.", offlineDataUpload.externalUploadId,
                        offlineDataUpload.uploadStatus);

                    // Print any partial failure errors from the response.
                    if (result.partialFailureErrors != null)
                    {
                        foreach (ApiError apiError in result.partialFailureErrors)
                        {
                            // Get the index of the failed operation from the error's field path
                            // elements.
                            int operationIndex = apiError.GetOperationIndex();
                            if (operationIndex != -1)
                            {
                                OfflineDataUpload failedOfflineDataUpload =
                                    operations[operationIndex].operand;
                                // Get the index of the entry in the offline data list from the
                                // error's field path elements.
                                int offlineDataListIndex =
                                    apiError.GetFieldPathIndex("offlineDataList");
                                Console.WriteLine(
                                    "Offline data list entry {0} in operation {1} with external " +
                                    "upload ID {2} and type '{3}' has triggered a failure for " +
                                    "the following reason: '{4}'.",
                                    offlineDataListIndex, operationIndex,
                                    failedOfflineDataUpload.externalUploadId,
                                    failedOfflineDataUpload.uploadType, apiError.errorString);
                            }
                            else
                            {
                                Console.WriteLine(
                                    "A failure has occurred for the following reason: {0}",
                                    apiError.errorString);
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    throw new System.ApplicationException("Failed upload offline data conversions.",
                        e);
                }
            }
        }

        /// <summary>
        /// Creates the offline data row from the specified transaction time, transaction micro
        /// amount, transaction currency, conversion name and user identifier list.
        /// </summary>
        /// <param name="transactionTime">The transaction time.</param>
        /// <param name="transactionMicroAmount">The transaction micro amount.</param>
        /// <param name="transactionCurrency">The transaction currency.</param>
        /// <param name="conversionName">Name of the conversion.</param>
        /// <param name="userIdentifierList">The user identifier list.</param>
        /// <returns>The offline data row.</returns>
        private OfflineData CreateOfflineDataRow(DateTime transactionTime,
            long transactionMicroAmount, string transactionCurrency, string conversionName,
            UserIdentifier[] userIdentifierList)
        {
            StoreSalesTransaction storeSalesTransaction = new StoreSalesTransaction
            {
                // For times use the format yyyyMMdd HHmmss [tz].
                // For details, see
                // https://developers.google.com/adwords/api/docs/appendix/codes-formats#date-and-time-formats
                transactionTime = transactionTime.ToString("yyyyMMdd HHmmss"),
                conversionName = conversionName,
                userIdentifiers = userIdentifierList
            };

            Money money = new Money
            {
                microAmount = transactionMicroAmount
            };
            RemarketingMoneyWithCurrency moneyWithCurrency = new RemarketingMoneyWithCurrency
            {
                money = money,
                currencyCode = transactionCurrency
            };
            storeSalesTransaction.transactionAmount = moneyWithCurrency;

            OfflineData offlineData = new OfflineData
            {
                Item = storeSalesTransaction
            };

            return offlineData;
        }

        /// <summary>
        /// Hash a string value using SHA-256 hashing algorithm.
        /// </summary>
        /// <param name="digest">Provides the algorithm for SHA-256.</param>
        /// <param name="value">The string value (e.g. an email address) to hash.</param>
        /// <returns>The hashed value.</returns>
        private static string ToSha256String(SHA256 digest, string value)
        {
            byte[] digestBytes = digest.ComputeHash(Encoding.UTF8.GetBytes(value));
            // Convert the byte array into an unhyphenated hexadecimal string.
            return BitConverter.ToString(digestBytes).Replace("-", string.Empty);
        }

        /// <summary>
        /// Creates the user identifier.
        /// </summary>
        /// <param name="type">The user identifier type.</param>
        /// <param name="value">The user identifier value.</param>
        /// <returns></returns>
        private UserIdentifier CreateUserIdentifier(OfflineDataUploadUserIdentifierType type,
            string value)
        {
            // If the user identifier type is a hashed type, also call hash function
            // on the value.
            if (type.ToString().StartsWith("HASHED_"))
            {
                value = ToSha256String(digest, ToNormalizedValue(value));
            }

            UserIdentifier userIdentifier = new UserIdentifier
            {
                userIdentifierType = type,
                value = value
            };

            return userIdentifier;
        }

        /// <summary>
        /// Removes leading and trailing whitespace and converts all characters to
        /// lower case.
        /// </summary>
        /// <param name="value">The value to normalize.</param>
        /// <returns>The normalized value.</returns>
        private static string ToNormalizedValue(string value)
        {
            return value.Trim().ToLower();
        }
    }
}

Send feedback about...

AdWords API
AdWords API
Need help? Visit our support page.