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.v201806;

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

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201806 {

  /// <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.v201806.AdwordsUserListService))
      using (ConversionTrackerService conversionTrackerService =
          (ConversionTrackerService) user.GetService(AdWordsService.v201806.
              ConversionTrackerService)) {

        BasicUserList userList = new BasicUserList();
        userList.name = "Mars cruise customers #" + ExampleUtilities.GetRandomString();
        userList.description = "A list of mars cruise customers in the last year.";
        userList.status = UserListMembershipStatus.OPEN;
        userList.membershipLifeSpan = 365;

        UserListConversionType conversionType = new UserListConversionType();
        conversionType.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();
        operation.operand = userList;
        operation.@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.v201806;

using System;
using System.Collections.Generic;

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201806 {

  /// <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.v201806.
              ConversionTrackerService)) {

        List<ConversionTracker> conversionTrackers = new List<ConversionTracker>();

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

        // Set optional fields.
        adWordsConversionTracker.status = ConversionTrackerStatus.ENABLED;
        adWordsConversionTracker.viewthroughLookbackWindow = 15;
        adWordsConversionTracker.defaultRevenueValue = 23.41;
        adWordsConversionTracker.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.
        uploadConversion.category = ConversionTrackerCategory.LEAD;
        uploadConversion.name = "Upload Conversion #" + ExampleUtilities.GetRandomString();
        uploadConversion.viewthroughLookbackWindow = 30;
        uploadConversion.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.
        uploadConversion.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.
        uploadConversion.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.v201806;

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

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201806 {

  /// <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.v201806.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();
            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.
          addressInfo.hashedFirstName = ToSha256String(digest, FIRST_NAME);
          addressInfo.hashedLastName = ToSha256String(digest, LAST_NAME);
          // Country code and zip code are sent in plaintext.
          addressInfo.zipCode = ZIP_CODE;
          addressInfo.countryCode = COUNTRY_CODE;

          Member memberByAddress = new Member();
          memberByAddress.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.v201806;

using System;
using System.Collections.Generic;

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201806 {

  /// <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.v201806.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();
        checkoutStringRuleItem.key = new StringKey();
        checkoutStringRuleItem.key.name = "ecomm_pagetype";
        checkoutStringRuleItem.op = StringRuleItemStringOperator.EQUALS;
        checkoutStringRuleItem.value = "checkout";

        RuleItem checkoutRuleItem = new RuleItem();
        checkoutRuleItem.Item = checkoutStringRuleItem;

        NumberRuleItem cartSizeNumberRuleItem = new NumberRuleItem();
        cartSizeNumberRuleItem.key = new NumberKey();
        cartSizeNumberRuleItem.key.name = "cartsize";
        cartSizeNumberRuleItem.op = NumberRuleItemNumberOperator.GREATER_THAN;
        cartSizeNumberRuleItem.value = 1;

        RuleItem cartSizeRuleItem = new RuleItem();
        cartSizeRuleItem.Item = cartSizeNumberRuleItem;

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

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

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

        // Combine the date rule items into a RuleItemGroup.
        RuleItemGroup checkedOutNextThreeMonthsItemGroup = new RuleItemGroup();
        checkedOutNextThreeMonthsItemGroup.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();
        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.
        rule.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();
        site1StringRuleItem.key = urlStringKey;
        site1StringRuleItem.op = StringRuleItemStringOperator.EQUALS;
        site1StringRuleItem.value = "example.com/example1";
        RuleItem site1RuleItem = new RuleItem();
        site1RuleItem.Item = site1StringRuleItem;

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

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

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

        Rule userVisitedSite2Rule = new Rule();
        userVisitedSite2Rule.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();
        dateUserList.name = "Date rule user list created at " + creationTimeString;
        dateUserList.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));
        dateUserList.rule = rule;

        // Set the start and end dates of the user list.
        dateUserList.startDate = startDate.ToString(DATE_FORMAT_STRING);
        dateUserList.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();
        combinedRuleUserList.name = "Combined rule user list created at " + creationTimeString;
        combinedRuleUserList.description = "Users who visited two sites.";
        combinedRuleUserList.leftOperand = userVisitedSite1Rule;
        combinedRuleUserList.rightOperand = userVisitedSite2Rule;
        combinedRuleUserList.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();
          operation.operand = userList;
          operation.@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.v201806;

using System;

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201806 {

  /// <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.v201806.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.v201806;

using System;

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201806 {

  /// <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.v201806.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();
        feed.callerId = callerId;
        feed.callStartTime = callStartTime;
        feed.conversionName = conversionName;
        feed.conversionTime = conversionTime;
        feed.conversionValue = conversionValue;

        OfflineCallConversionFeedOperation offlineCallConversionOperation =
            new OfflineCallConversionFeedOperation();
        offlineCallConversionOperation.@operator = Operator.ADD;
        offlineCallConversionOperation.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.v201806;

using System;

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201806 {

  /// <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.v201806.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();
          feed.conversionName = conversionName;
          feed.conversionTime = conversionTime;
          feed.conversionValue = conversionValue;
          feed.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();
          offlineConversionOperation.@operator = Operator.ADD;
          offlineConversionOperation.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.v201806;

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

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201806 {

  /// <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.v201806.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();
        offlineDataUpload.externalUploadId = externalUploadId;
        offlineDataUpload.offlineDataList = new OfflineData[] { offlineData1, offlineData2 };

        // Set the type and metadata of this upload.
        offlineDataUpload.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();
        uploadMetadata.Item = storeSalesMetaData;
        offlineDataUpload.uploadMetadata = uploadMetadata;

        // Create an offline data upload operation.
        OfflineDataUploadOperation offlineDataUploadOperation = new OfflineDataUploadOperation();
        offlineDataUploadOperation.@operator = Operator.ADD;
        offlineDataUploadOperation.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
      storeSalesTransaction.transactionTime = transactionTime.ToString("yyyyMMdd HHmmss");
      storeSalesTransaction.conversionName = conversionName;
      storeSalesTransaction.userIdentifiers = userIdentifierList;

      Money money = new Money();
      money.microAmount = transactionMicroAmount;
      RemarketingMoneyWithCurrency moneyWithCurrency = new RemarketingMoneyWithCurrency();
      moneyWithCurrency.money = money;
      moneyWithCurrency.currencyCode = transactionCurrency;
      storeSalesTransaction.transactionAmount = moneyWithCurrency;

      OfflineData offlineData = new OfflineData();
      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();
      userIdentifier.userIdentifierType = type;
      userIdentifier.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.