Share your feedback about the Google Ads (AdWords) API. Take the 2021 AdWords API and Google Ads API Annual Survey.

The AdWords API will sunset on April 27, 2022. Migrate to the Google Ads API to take advantage of the latest Google Ads features.

Remarketing Samples

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

Create a remarketing user list (audience)

<?php
/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * 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.
 */

namespace Google\AdsApi\Examples\AdWords\v201809\Remarketing;

require __DIR__ . '/../../../../vendor/autoload.php';

use Google\AdsApi\AdWords\AdWordsServices;
use Google\AdsApi\AdWords\AdWordsSession;
use Google\AdsApi\AdWords\AdWordsSessionBuilder;
use Google\AdsApi\AdWords\v201809\cm\ConversionTrackerService;
use Google\AdsApi\AdWords\v201809\cm\Operator;
use Google\AdsApi\AdWords\v201809\cm\Predicate;
use Google\AdsApi\AdWords\v201809\cm\PredicateOperator;
use Google\AdsApi\AdWords\v201809\cm\Selector;
use Google\AdsApi\AdWords\v201809\rm\AdwordsUserListService;
use Google\AdsApi\AdWords\v201809\rm\BasicUserList;
use Google\AdsApi\AdWords\v201809\rm\UserListConversionType;
use Google\AdsApi\AdWords\v201809\rm\UserListMembershipStatus;
use Google\AdsApi\AdWords\v201809\rm\UserListOperation;
use Google\AdsApi\Common\OAuth2TokenBuilder;

/**
 * This example adds a new remarketing list audience to the account and
 * retrieves the associated remarketing tag code.
 */
class AddAudience
{

    public static function runExample(
        AdWordsServices $adWordsServices,
        AdWordsSession $session
    ) {
        $userListService = $adWordsServices->get($session, AdwordsUserListService::class);
        $conversionTrackerService = $adWordsServices->get($session, ConversionTrackerService::class);

        // Create a conversion type (tag).
        $conversionType = new UserListConversionType();
        $conversionType->setName('Mars cruise customers #' . uniqid());

        // Create a basic user list.
        $userList = new BasicUserList();
        $userList->setName('Mars cruise customers #' . uniqid());
        $userList->setConversionTypes([$conversionType]);

        // Set additional settings (optional).
        $userList->setDescription(
            'A list of mars cruise customers in the last year'
        );
        $userList->setStatus(UserListMembershipStatus::OPEN);
        $userList->setMembershipLifeSpan(365);

        // Create a user list operation and add it to the list.
        $operations = [];
        $operation = new UserListOperation();
        $operation->setOperand($userList);
        $operation->setOperator(Operator::ADD);
        $operations[] = $operation;

        // Create the user list on the server.
        $userList = $userListService->mutate($operations)->getValue()[0];

        // Create the selector.
        $selector = new Selector();
        $selector->setFields([
            'Id', 'GoogleGlobalSiteTag', 'GoogleEventSnippet']);
        $selector->setPredicates(
            [
                new Predicate(
                    'Id',
                    PredicateOperator::IN,
                    [$userList->getConversionTypes()[0]->getId()]
                )
            ]
        );

        // Retrieve the conversion tracker and print out its information.
        $conversionTracker = $conversionTrackerService->get($selector)->getEntries()[0];
        printf(
            "Audience with name '%s' and ID %d was added.\n",
            $userList->getName(),
            $userList->getId()
        );
        printf(
            "Google global site tag:\n%s\n\n",
            $conversionTracker->getGoogleGlobalSiteTag()
        );
        printf(
            "Google event snippet:\n%s\n\n",
            $conversionTracker->getGoogleEventSnippet()
        );
    }

    public static function main()
    {
        // Generate a refreshable OAuth2 credential for authentication.
        $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();

        // Construct an API session configured from a properties file and the
        // OAuth2 credentials above.
        $session = (new AdWordsSessionBuilder())->fromFile()->withOAuth2Credential($oAuth2Credential)->build();
        self::runExample(new AdWordsServices(), $session);
    }
}

AddAudience::main();

Create an AdWords conversion tracker and add to it upload conversions

<?php
/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * 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.
 */

namespace Google\AdsApi\Examples\AdWords\v201809\Remarketing;

require __DIR__ . '/../../../../vendor/autoload.php';

use Google\AdsApi\AdWords\AdWordsServices;
use Google\AdsApi\AdWords\AdWordsSession;
use Google\AdsApi\AdWords\AdWordsSessionBuilder;
use Google\AdsApi\AdWords\v201809\cm\AdWordsConversionTracker;
use Google\AdsApi\AdWords\v201809\cm\ConversionTrackerCategory;
use Google\AdsApi\AdWords\v201809\cm\ConversionTrackerOperation;
use Google\AdsApi\AdWords\v201809\cm\ConversionTrackerService;
use Google\AdsApi\AdWords\v201809\cm\ConversionTrackerStatus;
use Google\AdsApi\AdWords\v201809\cm\Operator;
use Google\AdsApi\AdWords\v201809\cm\UploadConversion;
use Google\AdsApi\Common\OAuth2TokenBuilder;

/**
 * This example adds an AdWords conversion tracker and an upload conversion
 * tracker.
 */
class AddConversionTrackers
{

    public static function runExample(
        AdWordsServices $adWordsServices,
        AdWordsSession $session
    ) {
        $conversionTrackerService = $adWordsServices->get($session, ConversionTrackerService::class);
        $conversionTrackers = [];

        // Create an AdWords conversion tracker.
        $adWordsConversionTracker = new AdWordsConversionTracker();
        $adWordsConversionTracker->setName(
            'Interplanetary Cruise Conversion #' . uniqid()
        );

        // Set additional settings (optional).
        $adWordsConversionTracker->setStatus(ConversionTrackerStatus::ENABLED);
        $adWordsConversionTracker->setCategory(
            ConversionTrackerCategory::DEFAULT_VALUE
        );
        $adWordsConversionTracker->setViewthroughLookbackWindow(15);
        $adWordsConversionTracker->setDefaultRevenueValue(23.41);
        $adWordsConversionTracker->setAlwaysUseDefaultRevenueValue(true);
        $conversionTrackers[] = $adWordsConversionTracker;

        // Create an upload conversion for offline conversion imports.
        $uploadConversion = new UploadConversion();
        // Set an appropriate category. This field is optional, and will be set to
        // DEFAULT if not mentioned.
        $uploadConversion->setCategory(ConversionTrackerCategory::LEAD);
        $uploadConversion->setName('Upload Conversion # ' . uniqid());
        $uploadConversion->setViewthroughLookbackWindow(30);
        $uploadConversion->setCtcLookbackWindow(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->setDefaultRevenueCurrencyCode('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->setDefaultRevenueValue(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->setIsExternallyAttributed(true);

        $conversionTrackers[] = $uploadConversion;

        // Create conversion tracker operations and add them to the list.
        $operations = [];
        foreach ($conversionTrackers as $conversionTracker) {
            $operation = new ConversionTrackerOperation();
            $operation->setOperand($conversionTracker);
            $operation->setOperator(Operator::ADD);
            $operations[] = $operation;
        }

        // Create the conversion trackers on the server.
        $result = $conversionTrackerService->mutate($operations);

        // Print out some information about created trackers.
        foreach ($result->getValue() as $conversionTracker) {
            printf(
                "Conversion with ID %d, name '%s', status '%s' and category '%s' was added.\n",
                $conversionTracker->getId(),
                $conversionTracker->getName(),
                $conversionTracker->getStatus(),
                $conversionTracker->getCategory()
            );
            if ($conversionTracker instanceof AdWordsConversionTracker) {
                printf(
                    "Google global site tag:\n%s\n\n",
                    $conversionTracker->getGoogleGlobalSiteTag()
                );
                printf(
                    "Google event snippet:\n%s\n\n",
                    $conversionTracker->getGoogleEventSnippet()
                );
            }
        }
    }

    public static function main()
    {
        // Generate a refreshable OAuth2 credential for authentication.
        $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();

        // Construct an API session configured from a properties file and the
        // OAuth2 credentials above.
        $session = (new AdWordsSessionBuilder())->fromFile()->withOAuth2Credential($oAuth2Credential)->build();
        self::runExample(new AdWordsServices(), $session);
    }
}

AddConversionTrackers::main();

Create and populate a user list

<?php
/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * 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.
 */

namespace Google\AdsApi\Examples\AdWords\v201809\Remarketing;

require __DIR__ . '/../../../../vendor/autoload.php';

use Google\AdsApi\AdWords\AdWordsServices;
use Google\AdsApi\AdWords\AdWordsSession;
use Google\AdsApi\AdWords\AdWordsSessionBuilder;
use Google\AdsApi\AdWords\v201809\cm\Operator;
use Google\AdsApi\AdWords\v201809\rm\AddressInfo;
use Google\AdsApi\AdWords\v201809\rm\AdwordsUserListService;
use Google\AdsApi\AdWords\v201809\rm\CrmBasedUserList;
use Google\AdsApi\AdWords\v201809\rm\CustomerMatchUploadKeyType;
use Google\AdsApi\AdWords\v201809\rm\Member;
use Google\AdsApi\AdWords\v201809\rm\MutateMembersOperand;
use Google\AdsApi\AdWords\v201809\rm\MutateMembersOperation;
use Google\AdsApi\AdWords\v201809\rm\UserListOperation;
use Google\AdsApi\Common\OAuth2TokenBuilder;

/**
 * This example adds a user list (a.k.a. audience) and uploads members 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 1,000 members. After that, the size will be rounded to the two most
 * significant digits.
 */
class AddCrmBasedUserList
{

    private static $EMAILS = [
        'customer1@example.com',
        'customer2@example.com',
        ' Client3@example.com '
    ];

    public static function runExample(
        AdWordsServices $adWordsServices,
        AdWordsSession $session,
        array $emails
    ) {
        $userListService =
            $adWordsServices->get($session, AdwordsUserListService::class);

        // Create a CRM based user list.
        $userList = new CrmBasedUserList();
        $userList->setName(
            'Customer relationship management list #' . uniqid()
        );
        $userList->setDescription(
            '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.
        // Sets the membership life span to 30 days.
        $userList->setMembershipLifeSpan(30);
        $userList->setUploadKeyType(CustomerMatchUploadKeyType::CONTACT_INFO);

        // Create a user list operation and add it to the list.
        $operations = [];
        $operation = new UserListOperation();
        $operation->setOperand($userList);
        $operation->setOperator(Operator::ADD);
        $operations[] = $operation;

        // Create the user list on the server and print out some information.
        $userList = $userListService->mutate($operations)->getValue()[0];
        printf(
            "User list with name '%s' and ID %d was added.\n",
            $userList->getName(),
            $userList->getId()
        );

        // Create operation to add members to the user list based on email
        // addresses.
        $mutateMembersOperations = [];
        $mutateMembersOperation = new MutateMembersOperation();
        $operand = new MutateMembersOperand();
        $operand->setUserListId($userList->getId());

        $members = [];
        // Hash normalized email addresses based on SHA-256 hashing algorithm.
        foreach ($emails as $email) {
            $memberByEmail = new Member();
            $memberByEmail->setHashedEmail(self::normalizeAndHash($email));
            $members[] = $memberByEmail;
        }

        $firstName = 'John';
        $lastName = 'Doe';
        $countryCode = 'US';
        $zipCode = '10011';

        $addressInfo = new AddressInfo();
        // First and last name must be normalized and hashed.
        $addressInfo->setHashedFirstName(self::normalizeAndHash($firstName));
        $addressInfo->setHashedLastName(self::normalizeAndHash($lastName));
        // Country code and zip code are sent in plain text.
        $addressInfo->setCountryCode($countryCode);
        $addressInfo->setZipCode($zipCode);

        $memberByAddress = new Member();
        $memberByAddress->setAddressInfo($addressInfo);
        $members[] = $memberByAddress;

        // Add members to the operand and add the operation to the list.
        $operand->setMembersList($members);
        $mutateMembersOperation->setOperand($operand);
        $mutateMembersOperation->setOperator(Operator::ADD);
        $mutateMembersOperations[] = $mutateMembersOperation;

        // Add members to the user list based on email addresses.
        $result = $userListService->mutateMembers($mutateMembersOperations);

        // Print out some information about the added user list.
        // Reminder: it may take several hours for the list to be populated with
        // members.
        foreach ($result->getUserLists() as $userList) {
            printf(
                "%d email addresses were uploaded to user list with name '%s'"
                . " and ID %d and are scheduled for review.\n",
                count($emails),
                $userList->getName(),
                $userList->getId()
            );
        }
    }

    private static function normalizeAndHash($value)
    {
        return hash('sha256', strtolower(trim($value)));
    }

    public static function main()
    {
        // Generate a refreshable OAuth2 credential for authentication.
        $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();

        // Construct an API session configured from a properties file and the
        // OAuth2 credentials above.
        $session = (new AdWordsSessionBuilder())
            ->fromFile()
            ->withOAuth2Credential($oAuth2Credential)
            ->build();
        self::runExample(new AdWordsServices(), $session, self::$EMAILS);
    }
}

AddCrmBasedUserList::main();

Create rule-based user lists

<?php
/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * 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.
 */

namespace Google\AdsApi\Examples\AdWords\v201809\Remarketing;

require __DIR__ . '/../../../../vendor/autoload.php';

use DateTime;
use Google\AdsApi\AdWords\AdWordsServices;
use Google\AdsApi\AdWords\AdWordsSession;
use Google\AdsApi\AdWords\AdWordsSessionBuilder;
use Google\AdsApi\AdWords\v201809\cm\Operator;
use Google\AdsApi\AdWords\v201809\rm\AdwordsUserListService;
use Google\AdsApi\AdWords\v201809\rm\CombinedRuleUserList;
use Google\AdsApi\AdWords\v201809\rm\CombinedRuleUserListRuleOperator;
use Google\AdsApi\AdWords\v201809\rm\DateKey;
use Google\AdsApi\AdWords\v201809\rm\DateRuleItem;
use Google\AdsApi\AdWords\v201809\rm\DateRuleItemDateOperator;
use Google\AdsApi\AdWords\v201809\rm\DateSpecificRuleUserList;
use Google\AdsApi\AdWords\v201809\rm\ExpressionRuleUserList;
use Google\AdsApi\AdWords\v201809\rm\NumberKey;
use Google\AdsApi\AdWords\v201809\rm\NumberRuleItem;
use Google\AdsApi\AdWords\v201809\rm\NumberRuleItemNumberOperator;
use Google\AdsApi\AdWords\v201809\rm\Rule;
use Google\AdsApi\AdWords\v201809\rm\RuleBasedUserListPrepopulationStatus;
use Google\AdsApi\AdWords\v201809\rm\RuleItem;
use Google\AdsApi\AdWords\v201809\rm\RuleItemGroup;
use Google\AdsApi\AdWords\v201809\rm\StringKey;
use Google\AdsApi\AdWords\v201809\rm\StringRuleItem;
use Google\AdsApi\AdWords\v201809\rm\StringRuleItemStringOperator;
use Google\AdsApi\AdWords\v201809\rm\UserListOperation;
use Google\AdsApi\AdWords\v201809\rm\UserListRuleTypeEnumsEnum;
use Google\AdsApi\Common\OAuth2TokenBuilder;

/**
 * This 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.
 */
class AddRuleBasedUserLists
{

    public static function runExample(
        AdWordsServices $adWordsServices,
        AdWordsSession $session
    ) {
        $userListService = $adWordsServices->get($session, AdwordsUserListService::class);

        // Create the user list with no restrictions on site visit date.
        $expressionUserList = new ExpressionRuleUserList();
        $expressionUserList->setName(
            sprintf('Expression based user list created at %s', date('Y-m-d_His'))
        );
        $expressionUserList->setDescription(
            'Users who checked out in three month ' . 'window OR visited the checkout page with more than one item in '
            . 'their cart'
        );
        $expressionUserList->setRule(self::createUserListRule());

        // Optional: Set the prepopulationStatus to REQUESTED to include past users
        // in the user list.
        $expressionUserList->setPrepopulationStatus(
            RuleBasedUserListPrepopulationStatus::REQUESTED
        );

        // Create the user list restricted to users who visit your site within the
        // next six months.
        $startDate = new DateTime();
        $endDate = new DateTime('+6 month');

        $dateUserList = new DateSpecificRuleUserList();
        $dateUserList->setName(
            sprintf('Date rule user list created at %s', date('Y-m-d_His'))
        );
        $dateUserList->setDescription(
            sprintf(
                'Users who visited the site between %s '
                . 'and %s and checked out in three month window OR visited the '
                . 'checkout page with more than one item in their cart',
                $startDate->format('Ymd'),
                $endDate->format('Ymd')
            )
        );
        $dateUserList->setRule(self::createUserListRule());

        // Set the start and end dates of the user list.
        $dateUserList->setStartDate($startDate->format('Ymd'));
        $dateUserList->setEndDate($endDate->format('Ymd'));

        // 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.
        $combinedUserList = new CombinedRuleUserList();
        $combinedUserList->setName(
            sprintf('Combined rule user list created at %s', date('Y-m-d_His'))
        );
        $combinedUserList->setDescription('Users who visited two sites.');
        $operands = self::createCombinedUserListRules();
        $combinedUserList->setLeftOperand($operands[0]);
        $combinedUserList->setRightOperand($operands[1]);
        $combinedUserList->setRuleOperator(
            CombinedRuleUserListRuleOperator::AND_VALUE
        );

        // Create operations to add the user lists.
        $operations = [];
        foreach ([$expressionUserList, $dateUserList, $combinedUserList] as $userList) {
            $operation = new UserListOperation();
            $operation->setOperand($userList);
            $operation->setOperator(Operator::ADD);
            $operations[] = $operation;
        }

        // Create the user lists on the server.
        $result = $userListService->mutate($operations);

        // Print out some information about created user lists.
        foreach ($result->getValue() as $userListResult) {
            printf(
                "User list added with ID %d, name '%s', status '%s', list type '%s'"
                . ", account user list status '%s', description '%s'.\n",
                $userListResult->getId(),
                $userListResult->getName(),
                $userListResult->getStatus(),
                $userListResult->getListType(),
                $userListResult->getAccountUserListStatus(),
                $userListResult->getDescription()
            );
        }
    }

    /**
     * Create a user list rule composed of two rule item groups.
     *
     * @return Rule the created rule
     */
    private static function createUserListRule()
    {
        // First rule item group - users who visited the checkout page and had more
        // than one item in their shopping cart.
        $checkoutStringRuleItem = new StringRuleItem();
        $checkoutStringKey = new StringKey();
        $checkoutStringKey->setName('ecomm_pagetype');
        $checkoutStringRuleItem->setKey($checkoutStringKey);
        $checkoutStringRuleItem->setOp(StringRuleItemStringOperator::EQUALS);
        $checkoutStringRuleItem->setValue('checkout');
        $checkoutRuleItem = new RuleItem();
        $checkoutRuleItem->setStringRuleItem($checkoutStringRuleItem);

        $cartSizeNumberRuleItem = new NumberRuleItem();
        $cartSizeNumberKey = new NumberKey();
        $cartSizeNumberKey->setName('cartsize');
        $cartSizeNumberRuleItem->setKey($cartSizeNumberKey);
        $cartSizeNumberRuleItem->setOp(NumberRuleItemNumberOperator::GREATER_THAN);
        $cartSizeNumberRuleItem->setValue(1.0);
        $cartSizeRuleItem = new RuleItem();
        $cartSizeRuleItem->setNumberRuleItem($cartSizeNumberRuleItem);

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

        // Second rule item group - users who checked out within the next 3 months.
        $today = new DateTime();
        $startDateDateRuleItem = new DateRuleItem();
        $startDateDateKey = new DateKey();
        $startDateDateKey->setName('checkoutdate');
        $startDateDateRuleItem->setKey($startDateDateKey);
        $startDateDateRuleItem->setOp(DateRuleItemDateOperator::AFTER);
        $startDateDateRuleItem->setValue($today->format('Ymd'));
        $startDateRuleItem = new RuleItem();
        $startDateRuleItem->setDateRuleItem($startDateDateRuleItem);

        $threeMonthsLater = new DateTime('+3 month');
        $endDateDateRuleItem = new DateRuleItem();
        $endDateDateKey = new DateKey();
        $endDateDateKey->setName('checkoutdate');
        $endDateDateRuleItem->setKey($endDateDateKey);
        $endDateDateRuleItem->setOp(DateRuleItemDateOperator::BEFORE);
        $endDateDateRuleItem->setValue($threeMonthsLater->format('Ymd'));
        $endDateRuleItem = new RuleItem();
        $endDateRuleItem->setDateRuleItem($endDateDateRuleItem);

        // Combine the date rule items into a RuleItemGroup.
        $checkedOutDateRangeItemGroup = new RuleItemGroup();
        $checkedOutDateRangeItemGroup->setItems(
            [$startDateRuleItem, $endDateRuleItem]
        );

        // Combine the rule item groups into a Rule so AdWords knows how to apply
        // the rules.
        $rule = new Rule();
        $rule->setGroups(
            [$checkoutMultipleItemGroup, $checkedOutDateRangeItemGroup]
        );
        // 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->setRuleType(UserListRuleTypeEnumsEnum::DNF);

        return $rule;
    }

    /**
     * Create rules to be used as left and right operands of
     * a combined user list.
     *
     * @return Rule[] the array of created rules
     */
    private static function createCombinedUserListRules()
    {
        // Third and fourth rule item groups
        // Visitors of a page who visited another page.
        $site1StringRuleItem = new StringRuleItem();
        $site1StringKey = new StringKey();
        $site1StringKey->setName('url__');
        $site1StringRuleItem->setKey($site1StringKey);
        $site1StringRuleItem->setOp(StringRuleItemStringOperator::EQUALS);
        $site1StringRuleItem->setValue('example.com/example1');
        $site1RuleItem = new RuleItem();
        $site1RuleItem->setStringRuleItem($site1StringRuleItem);

        $site2StringRuleItem = new StringRuleItem();
        $site2StringKey = new StringKey();
        $site2StringKey->setName('url__');
        $site2StringRuleItem->setKey($site2StringKey);
        $site2StringRuleItem->setOp(StringRuleItemStringOperator::EQUALS);
        $site2StringRuleItem->setValue('example.com/example2');
        $site2RuleItem = new RuleItem();
        $site2RuleItem->setStringRuleItem($site2StringRuleItem);

        // Create two RuleItemGroups to show that a visitor browsed two sites.
        $site1ItemGroup = new RuleItemGroup();
        $site1ItemGroup->setItems([$site1RuleItem]);
        $site2ItemGroup = new RuleItemGroup();
        $site2ItemGroup->setItems([$site2RuleItem]);

        // Create two rules to show that a visitor browsed two sites.
        $userVisitedSite1Rule = new Rule();
        $userVisitedSite1Rule->setGroups([$site1ItemGroup]);
        $userVisitedSite2Rule = new Rule();
        $userVisitedSite2Rule->setGroups([$site2ItemGroup]);


        return [$userVisitedSite1Rule, $userVisitedSite2Rule];
    }

    public static function main()
    {
        // Generate a refreshable OAuth2 credential for authentication.
        $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();

        // Construct an API session configured from a properties file and the
        // OAuth2 credentials above.
        $session = (new AdWordsSessionBuilder())->fromFile()->withOAuth2Credential($oAuth2Credential)->build();
        self::runExample(new AdWordsServices(), $session);
    }
}

AddRuleBasedUserLists::main();

Import conversion adjustments for existing conversions

<?php
/**
 * 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.
 */

namespace Google\AdsApi\Examples\AdWords\v201809\Remarketing;

require __DIR__ . '/../../../../vendor/autoload.php';

use Google\AdsApi\AdWords\AdWordsServices;
use Google\AdsApi\AdWords\AdWordsSession;
use Google\AdsApi\AdWords\AdWordsSessionBuilder;
use Google\AdsApi\AdWords\v201809\cm\GclidOfflineConversionAdjustmentFeed;
use Google\AdsApi\AdWords\v201809\cm\OfflineConversionAdjustmentFeedOperation;
use Google\AdsApi\AdWords\v201809\cm\OfflineConversionAdjustmentFeedService;
use Google\AdsApi\AdWords\v201809\cm\Operator;
use Google\AdsApi\Common\OAuth2TokenBuilder;

/**
 * This example demonstrates adjusting one conversion, but you can add more
 * than one operation in a single mutate request.
 */
class UploadOfflineConversionAdjustments
{

    const CONVERSION_NAME = 'INSERT_CONVERSION_NAME_HERE';
    const GCLID = 'INSERT_GOOGLE_CLICK_ID_HERE';
    const CONVERSION_TIME = 'INSERT_CONVERSION_TIME_HERE';
    const ADJUSTMENT_TYPE = 'INSERT_ADJUSTMENT_TYPE_HERE';
    const ADJUSTMENT_TIME = 'INSERT_ADJUSTMENT_TIME_HERE';
    const ADJUSTED_VALUE = 'INSERT_ADJUSTED_VALUE_HERE';

    public static function runExample(
        AdWordsServices $adWordsServices,
        AdWordsSession $session,
        $conversionName,
        $gclid,
        $conversionTime,
        $adjustmentType,
        $adjustmentTime,
        $adjustedValue
    ) {
        $offlineConversionService = $adWordsServices->get(
            $session,
            OfflineConversionAdjustmentFeedService::class
        );

        // Associate conversion adjustments with the existing named conversion
        // tracker. The GCLID should have been uploaded before with a
        // conversion.
        $feed = new GclidOfflineConversionAdjustmentFeed();
        $feed->setConversionName($conversionName);
        $feed->setGoogleClickId($gclid);
        $feed->setConversionTime($conversionTime);
        $feed->setAdjustmentType($adjustmentType);
        $feed->setAdjustmentTime($adjustmentTime);
        $feed->setAdjustedValue($adjustedValue);

        $offlineConversionAdjustmentFeedOperation =
            new OfflineConversionAdjustmentFeedOperation();
        $offlineConversionAdjustmentFeedOperation->setOperator(Operator::ADD);
        $offlineConversionAdjustmentFeedOperation->setOperand($feed);

        // Issues a request to the servers for adjustments of the conversion.
        $result = $offlineConversionService->mutate(
            [$offlineConversionAdjustmentFeedOperation]
        );

        $feed = $result->getValue()[0];
        printf(
            "Uploaded conversion adjustment value of '%s' for Google "
            . "Click ID '%s'.%s",
            $feed->getConversionName(),
            $feed->getGoogleClickId(),
            PHP_EOL
        );
    }

    public static function main()
    {
        // Generate a refreshable OAuth2 credential for authentication.
        $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();

        // Construct an API session configured from a properties file and the
        // OAuth2 credentials above.
        $session = (new AdWordsSessionBuilder())->fromFile()
            ->withOAuth2Credential($oAuth2Credential)
            ->build();
        self::runExample(
            new AdWordsServices(),
            $session,
            self::CONVERSION_NAME,
            self::GCLID,
            self::CONVERSION_TIME,
            self::ADJUSTMENT_TYPE,
            self::ADJUSTMENT_TIME,
            floatval(self::ADJUSTED_VALUE)
        );
    }
}

UploadOfflineConversionAdjustments::main();

Import offline call conversions

<?php
/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * 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.
 */

namespace Google\AdsApi\Examples\AdWords\v201809\Remarketing;

require __DIR__ . '/../../../../vendor/autoload.php';

use Google\AdsApi\AdWords\AdWordsServices;
use Google\AdsApi\AdWords\AdWordsSession;
use Google\AdsApi\AdWords\AdWordsSessionBuilder;
use Google\AdsApi\AdWords\v201809\cm\OfflineCallConversionFeed;
use Google\AdsApi\AdWords\v201809\cm\OfflineCallConversionFeedOperation;
use Google\AdsApi\AdWords\v201809\cm\OfflineCallConversionFeedService;
use Google\AdsApi\AdWords\v201809\cm\Operator;
use Google\AdsApi\Common\OAuth2TokenBuilder;

/**
 * This code example imports offline call conversion values for calls related
 * to the ads in your account.
 */
class UploadOfflineCallConversions
{

    const CALLER_ID = 'INSERT_CALLER_ID_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
    const CALL_START_TIME = 'INSERT_CALL_START_TIME_HERE';
    const CONVERSION_NAME = 'INSERT_CONVERSION_NAME_HERE';
    const CONVERSION_TIME = 'INSERT_CONVERSION_TIME_HERE';
    const CONVERSION_VALUE = 'INSERT_CONVERSION_VALUE_HERE';

    public static function runExample(
        AdWordsServices $adWordsServices,
        AdWordsSession $session,
        $callerId,
        $callStartTime,
        $conversionName,
        $conversionTime,
        $conversionValue
    ) {
        $offlineCallConversionService = $adWordsServices->get(
            $session,
            OfflineCallConversionFeedService::class
        );

        // 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.
        $feed = new OfflineCallConversionFeed();
        $feed->setCallerId($callerId);
        $feed->setCallStartTime($callStartTime);
        $feed->setConversionName($conversionName);
        $feed->setConversionTime($conversionTime);
        $feed->setConversionValue($conversionValue);

        $offlineCallConversionOperations = [];
        $offlineCallConversionOperation = new OfflineCallConversionFeedOperation();
        $offlineCallConversionOperation->setOperator(Operator::ADD);
        $offlineCallConversionOperation->setOperand($feed);
        $offlineCallConversionOperations[] = $offlineCallConversionOperation;

        // This example uploads only one call conversion, but you can upload
        // multiple call conversions by passing additional operations.
        $result = $offlineCallConversionService->mutate($offlineCallConversionOperations);

        $feed = $result->getValue()[0];
        printf(
            "Uploaded offline call conversion value of '%s' for caller ID '%s'.\n",
            $feed->getConversionValue(),
            $feed->getCallerId()
        );
    }


    public static function main()
    {
        // Generate a refreshable OAuth2 credential for authentication.
        $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();

        // Construct an API session configured from a properties file and the
        // OAuth2 credentials above.
        $session = (new AdWordsSessionBuilder())->fromFile()->withOAuth2Credential($oAuth2Credential)->build();
        self::runExample(
            new AdWordsServices(),
            $session,
            self::CALLER_ID,
            self::CALL_START_TIME,
            self::CONVERSION_NAME,
            self::CONVERSION_TIME,
            floatval(self::CONVERSION_VALUE)
        );
    }
}

UploadOfflineCallConversions::main();

Import offline click conversions

<?php
/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * 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.
 */

namespace Google\AdsApi\Examples\AdWords\v201809\Remarketing;

require __DIR__ . '/../../../../vendor/autoload.php';

use Google\AdsApi\AdWords\AdWordsServices;
use Google\AdsApi\AdWords\AdWordsSession;
use Google\AdsApi\AdWords\AdWordsSessionBuilder;
use Google\AdsApi\AdWords\v201809\cm\OfflineConversionFeed;
use Google\AdsApi\AdWords\v201809\cm\OfflineConversionFeedOperation;
use Google\AdsApi\AdWords\v201809\cm\OfflineConversionFeedService;
use Google\AdsApi\AdWords\v201809\cm\Operator;
use Google\AdsApi\Common\OAuth2TokenBuilder;

/**
 * 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.
 */
class UploadOfflineConversions
{

    const CONVERSION_NAME = 'INSERT_CONVERSION_NAME_HERE';
    const GCLID = 'INSERT_GOOGLE_CLICK_ID_HERE';
    const CONVERSION_TIME = 'INSERT_CONVERSION_TIME_HERE';
    const CONVERSION_VALUE = 'INSERT_CONVERSION_VALUE_HERE';

    public static function runExample(
        AdWordsServices $adWordsServices,
        AdWordsSession $session,
        $conversionName,
        $gclid,
        $conversionTime,
        $conversionValue
    ) {
        $offlineConversionService = $adWordsServices->get($session, OfflineConversionFeedService::class);

        // 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.
        $feed = new OfflineConversionFeed();
        $feed->setConversionName($conversionName);
        $feed->setConversionTime($conversionTime);
        $feed->setConversionValue($conversionValue);
        $feed->setGoogleClickId($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->setExternalAttributionModel('Linear');
        // $feed->setExternalAttributionCredit(0.3);

        $offlineConversionOperation = new OfflineConversionFeedOperation();
        $offlineConversionOperation->setOperator(Operator::ADD);
        $offlineConversionOperation->setOperand($feed);
        $offlineConversionOperations = [$offlineConversionOperation];

        $result = $offlineConversionService->mutate($offlineConversionOperations);

        $feed = $result->getValue()[0];
        printf(
            "Uploaded offline conversion value of %d for Google Click ID = '%s' to '%s'.\n",
            $feed->getConversionValue(),
            $feed->getGoogleClickId(),
            $feed->getConversionName()
        );
    }

    public static function main()
    {
        // Generate a refreshable OAuth2 credential for authentication.
        $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();

        // Construct an API session configured from a properties file and the
        // OAuth2 credentials above.
        $session = (new AdWordsSessionBuilder())->fromFile()->withOAuth2Credential($oAuth2Credential)->build();
        self::runExample(
            new AdWordsServices(),
            $session,
            self::CONVERSION_NAME,
            self::GCLID,
            self::CONVERSION_TIME,
            floatval(self::CONVERSION_VALUE)
        );
    }
}

UploadOfflineConversions::main();

Upload offline data for store sales transactions

<?php
/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * 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.
 */

namespace Google\AdsApi\Examples\AdWords\v201809\Remarketing;

require __DIR__ . '/../../../../vendor/autoload.php';

use Google\AdsApi\AdWords\AdWordsServices;
use Google\AdsApi\AdWords\AdWordsSession;
use Google\AdsApi\AdWords\AdWordsSessionBuilder;
use Google\AdsApi\AdWords\v201809\cm\ApiError;
use Google\AdsApi\AdWords\v201809\cm\Money;
use Google\AdsApi\AdWords\v201809\cm\Operator;
use Google\AdsApi\AdWords\v201809\rm\FirstPartyUploadMetadata;
use Google\AdsApi\AdWords\v201809\rm\MoneyWithCurrency;
use Google\AdsApi\AdWords\v201809\rm\OfflineData;
use Google\AdsApi\AdWords\v201809\rm\OfflineDataUpload;
use Google\AdsApi\AdWords\v201809\rm\OfflineDataUploadOperation;
use Google\AdsApi\AdWords\v201809\rm\OfflineDataUploadService;
use Google\AdsApi\AdWords\v201809\rm\OfflineDataUploadType;
use Google\AdsApi\AdWords\v201809\rm\OfflineDataUploadUserIdentifierType;
use Google\AdsApi\AdWords\v201809\rm\StoreSalesTransaction;
use Google\AdsApi\AdWords\v201809\rm\ThirdPartyUploadMetadata;
use Google\AdsApi\AdWords\v201809\rm\UploadMetadata;
use Google\AdsApi\AdWords\v201809\rm\UserIdentifier;
use Google\AdsApi\Common\OAuth2TokenBuilder;

/**
 * This code example shows how to upload offline data for store sales
 * transactions.
 */
class UploadOfflineData
{

    // The external upload ID can be any number that you use to keep track of
    // your uploads.
    const EXTERNAL_UPLOAD_ID = 'INSERT_EXTERNAL_UPLOAD_ID';

    // Insert the conversion type name that you'd like to attribute this upload
    // to.
    const CONVERSION_NAME = 'INSERT_CONVERSION_NAME';

    // Change the below constant to ThirdPartyUploadMetadata::class if uploading
    // third party data.
    const STORE_SALES_UPLOAD_COMMON_METADATA_TYPE =
        FirstPartyUploadMetadata::class;

    // The three constants below are needed when uploading third party data.
    // You can safely ignore them if you are uploading first party data.
    // 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
    const ADVERTISER_UPLOAD_TIME = 'INSERT_ADVERTISER_UPLOAD_TIME';
    const BRIDGE_MAP_VERSION_ID = 'INSERT_BRIDGE_MAP_VERSION_ID';
    const PARTNER_ID = 'INSERT_PARTNER_ID';

    // Insert email addresses below for creating user identifiers.
    private static $EMAIL_ADDRESSES = ['EMAIL_ADDRESS_1', 'EMAIL_ADDRESS_2'];

    public static function runExample(
        AdWordsServices $adWordsServices,
        AdWordsSession $session,
        $conversionName,
        $externalUploadId,
        $storeSalesUploadCommonMetadataType,
        array $emailAddresses,
        $advertiserUploadTime,
        $bridgeMapVersionId,
        $partnerId
    ) {
        $session->setPartialFailure(true);
        $offlineDataUploadService =
            $adWordsServices->get($session, OfflineDataUploadService::class);

        // Create the first offline data for upload.
        // This transaction occurred 7 days ago with amount of 200 USD.
        $transactionTime1 = new \DateTime('-7d');
        $transactionAmount1 = 200000000;
        $transactionCurrencyCode1 = 'USD';
        $userIdentifierList1 = [
            self::createUserIdentifier(
                OfflineDataUploadUserIdentifierType::HASHED_EMAIL,
                $emailAddresses[0]
            ),
            self::createUserIdentifier(
                OfflineDataUploadUserIdentifierType::STATE,
                'New York'
            )
        ];
        $offlineData1 = self::createOfflineData(
            $transactionTime1,
            $transactionAmount1,
            $transactionCurrencyCode1,
            $conversionName,
            $userIdentifierList1
        );

        // Create the second offline data for upload.
        // This transaction occurred 14 days ago with amount of 450 EUR.
        $transactionTime2 = new \DateTime('-14d');
        $transactionAmount2 = 450000000;
        $transactionCurrencyCode2 = 'EUR';
        $userIdentifierList2 = [
            self::createUserIdentifier(
                OfflineDataUploadUserIdentifierType::HASHED_EMAIL,
                $emailAddresses[1]
            ),
            self::createUserIdentifier(
                OfflineDataUploadUserIdentifierType::STATE,
                'California'
            )
        ];
        $offlineData2 = self::createOfflineData(
            $transactionTime2,
            $transactionAmount2,
            $transactionCurrencyCode2,
            $conversionName,
            $userIdentifierList2
        );

        // Create offline data upload object.
        $offlineDataUpload = new OfflineDataUpload();
        $offlineDataUpload->setExternalUploadId($externalUploadId);
        $offlineDataUpload->setOfflineDataList([$offlineData1, $offlineData2]);

        // Set the type and metadata of this upload.
        $reflectionClass =
            new \ReflectionClass($storeSalesUploadCommonMetadataType);
        $storeSalesUploadCommonMetadata = $reflectionClass->newInstance();
        $storeSalesUploadCommonMetadata->setLoyaltyRate(1.0);
        $storeSalesUploadCommonMetadata->setTransactionUploadRate(1.0);

        if ($storeSalesUploadCommonMetadataType
            === FirstPartyUploadMetadata::class) {
            $offlineDataUpload->setUploadType(
                OfflineDataUploadType::STORE_SALES_UPLOAD_FIRST_PARTY
            );
        } elseif ($storeSalesUploadCommonMetadataType
            === ThirdPartyUploadMetadata::class) {
            $offlineDataUpload->setUploadType(
                OfflineDataUploadType::STORE_SALES_UPLOAD_THIRD_PARTY
            );
            $storeSalesUploadCommonMetadata->setAdvertiserUploadTime(
                $advertiserUploadTime
            );
            $storeSalesUploadCommonMetadata->setValidTransactionRate(1.0);
            $storeSalesUploadCommonMetadata->setPartnerMatchRate(1.0);
            $storeSalesUploadCommonMetadata->setPartnerUploadRate(1.0);
            $storeSalesUploadCommonMetadata->setBridgeMapVersionId(
                $bridgeMapVersionId
            );
            $storeSalesUploadCommonMetadata->setPartnerId($partnerId);
        }

        $uploadMetadata = new UploadMetadata();
        $uploadMetadata->setStoreSalesUploadCommonMetadata(
            $storeSalesUploadCommonMetadata
        );
        $offlineDataUpload->setUploadMetadata($uploadMetadata);

        // Create an offline data upload operation.
        $offlineDataUploadOperation = new OfflineDataUploadOperation();
        $offlineDataUploadOperation->setOperator(Operator::ADD);
        $offlineDataUploadOperation->setOperand($offlineDataUpload);

        $operations = [];
        $operations[] = $offlineDataUploadOperation;
        // Upload offline data on the server and print some information.
        $result =
            $offlineDataUploadService->mutate($operations);
        $offlineDataUpload = $result->getValue()[0];
        printf(
            "Uploaded offline data with external upload ID %d and upload"
            . " status %s\n",
            $offlineDataUpload->getExternalUploadId(),
            $offlineDataUpload->getUploadStatus()
        );

        // Print any partial failure errors from the response.
        if (empty($result->getPartialFailureErrors())) {
            return;
        }
        foreach ($result->getPartialFailureErrors() as $apiError) {
            // Gets the index of the failed operation from the error's field
            // path elements.
            $operationIndex =
                self::getFieldPathElementIndex($apiError, 'operations');

            if (!is_null($operationIndex)) {
                $failedOfflineDataUpload =
                    $operations[$operationIndex]->getOperand();
                // Gets the index of the entry in the offline data list from the
                // error's field path elements.
                $offlineDataListIndex = self::getFieldPathElementIndex(
                    $apiError,
                    'offlineDataList'
                );

                printf(
                    "Offline data list entry %d in operation %d with "
                    . "external upload ID %d and type '%s' has triggered a "
                    . "failure for the following reason: '%s'.\n",
                    $offlineDataListIndex,
                    $operationIndex,
                    $failedOfflineDataUpload->getExternalUploadId(),
                    $failedOfflineDataUpload->getUploadType(),
                    $apiError->getErrorString()
                );
            } else {
                printf(
                    "A failure for the following reason: '%s' has "
                    . "occurred.\n",
                    $apiError->getErrorString()
                );
            }
        }
    }

    /**
     * Creates the offline data from the specified transaction time, transaction
     * micro amount, transaction currency, conversion name and user identifier
     * list.
     */
    private static function createOfflineData(
        \DateTime $transactionTime,
        $transactionMicroAmount,
        $transactionCurrency,
        $conversionName,
        array $userIdentifierList
    ) {
        $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->setTransactionTime(
            $transactionTime->format('Ymd His')
        );
        $storeSalesTransaction->setConversionName($conversionName);
        $storeSalesTransaction->setUserIdentifiers($userIdentifierList);

        $money = new Money();
        $money->setMicroAmount($transactionMicroAmount);
        $moneyWithCurrency = new MoneyWithCurrency();
        $moneyWithCurrency->setMoney($money);
        $moneyWithCurrency->setCurrencyCode($transactionCurrency);
        $storeSalesTransaction->setTransactionAmount($moneyWithCurrency);

        $offlineData = new OfflineData();
        $offlineData->setStoreSalesTransaction($storeSalesTransaction);

        return $offlineData;
    }

    /**
     * Creates a user identifier from the specified type and value.
     */
    private static function createUserIdentifier($type, $value)
    {
        // If the user identifier type is a hashed type, normalize and hash the
        // value.
        if (0 === strpos($type, 'HASHED_')) {
            $value = hash('sha256', strtolower(trim($value)));
        }
        $userIdentifier = new UserIdentifier();
        $userIdentifier->setUserIdentifierType($type);
        $userIdentifier->setValue($value);

        return $userIdentifier;
    }

    /**
     * Returns the FieldPathElement#getIndex() for the specified field name, if
     * present in the error's field path elements. If not, returns null.
     */
    private static function getFieldPathElementIndex(ApiError $apiError, $field)
    {
        $fieldPathElements = $apiError->getFieldPathElements();
        if (is_null($fieldPathElements) || count($fieldPathElements) === 0) {
            return null;
        }
        for ($i = 0; $i < count($fieldPathElements); $i++) {
            $fieldPathElement = $fieldPathElements[$i];
            if ($field === $fieldPathElement->getField()) {
                return $fieldPathElement->getIndex();
            }
        }
        return null;
    }

    public static function main()
    {
        // Generate a refreshable OAuth2 credential for authentication.
        $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();

        // Construct an API session configured from a properties file and the
        // OAuth2 credentials above.
        $session = (new AdWordsSessionBuilder())->fromFile()->withOAuth2Credential($oAuth2Credential)->build();
        self::runExample(
            new AdWordsServices(),
            $session,
            self::CONVERSION_NAME,
            intval(self::EXTERNAL_UPLOAD_ID),
            self::STORE_SALES_UPLOAD_COMMON_METADATA_TYPE,
            self::$EMAIL_ADDRESSES,
            self::ADVERTISER_UPLOAD_TIME,
            self::BRIDGE_MAP_VERSION_ID,
            intval(self::PARTNER_ID)
        );
    }
}

UploadOfflineData::main();