Loyalty Card Concepts

The Save to Android Pay API (S2AP) enables you to engage with users through loyalty cards. The concepts discussed in this guide should help you better understand the capabilities of saved loyalty cards.


In this guide we will cover some key concepts regarding the API and address common use-cases.

Classes and Objects

The Save to Android Pay API uses the concept of a LoyaltyObject to represent a user's loyalty card. Each loyalty card a user has in their Android Pay app is represented by one LoyaltyObject.

Each LoyaltyObject references a LoyaltyClass so that it is easier to make changes that should be applied to all loyalty cards. Before you can create a LoyaltyObject, a LoyaltyClass must be created first. Changes made to the LoyaltyClass will be propagated within immediately. Clients will be able to see these changes when they sync.

A LoyaltyClass contains data common for every user of the loyalty card type, such as the ID, program logo, and template used to display loyalty card information in the Android Pay app. For example, you may create a separate LoyaltyClass for every loyalty tier you want to offer your users. Any updates to a LoyaltyClass will be propagated to all objects that reference the class. Each LoyaltyClass is represented by the class resource.

A LoyaltyClass or LoyaltyObject contains data specific to a user such as a specific loyalty card number. A LoyaltyObject is represented by the LoyaltyObject resource. Any updates to LoyaltyClass or LoyaltyObject will be pushed to the affected users' Android Pay app.

Objects and Linking

The Save to Android Pay API uses the concept of a LoyaltyObject to represent a loyalty card. Each loyalty card a user has in their Android Pay app is represented by one LoyaltyObject.

Typically, when a user saves a loyalty card via the Save to Android Pay button, the API first inserts a LoyaltyObject and then establishes a link between the object and the user. This means if the user clicks the button again, a new LoyaltyObject is not inserted. The loyalty card is also not re-linked since a link already exists.

If a user removes their loyalty card, the LoyaltyObject they were linked to is not removed. Instead it is simply de-linked. If they were to click the Save to Android Pay button again, it would only reestablish the link.

While developing your loyalty cards, a new LoyaltyObject id should be used between saves since the API will not re-insert an object that matches an existing id. Remember, removing the loyalty card from your account doesn't actually remove the object. The object is only de-linked from your account.

Typical API Flows

The communication flow for inserting a class, saving an object, and updating an object are illustrated in the following image. The text in bold represents each API flow. All actions between "Your Server" are your responsibility to implement.

Detailed API flows.

  1. Loyalty card issuer creates LoyaltyClass:
    1. Your server defines the LoyaltyClass.
    2. Your server makes a POST request to save the LoyaltyClass.
  2. User clicks Save to Android Pay button on loyalty card issuer's website:
    1. Website is rendered with JWT representing the user's object.
    2. User clicks Save to Android Pay button on issuer's website to save the LoyaltyObject.
    3. LoyaltyObject is pushed to the Android Pay app.
  3. Card issuer updates card data:
    1. Loyalty card issuer gets the LoyaltyObject using the object ID.
    2. Loyalty card issuer updates LoyaltyObject.
    3. Loyalty card issuer makes a PUT request to save the updated LoyaltyObject.
    4. The LoyaltyObject is pushed to the Android Pay app.

Use cases

Save to Android Pay via Web

For details on how to integrate the Save to Android Pay button on your website, refer to our Get Started.

Save to Android Pay via Email

To save your loyalty card to Android Pay via an email, include this deep link with a button in the email, filling in your generated JWT and class ID.

The safe length of a deep link URL is 2000 characters. Your deep links should remain below this limit. Objects encoded in JWTs should be small, containing only data that is specific to the user. Try to keep most data in the object's class, creating it before making the JWT. For larger objects that do not fit the limit, consider creating the object in the S2AP API and sending only the class ID and object ID in the JWT.


Note: Make sure to include the trailing '/' in the URL before the parameters.

Note: JWTs are unencrypted. Only include data in the deep link that is safe to share as plain text.

Consult the UX Guidelines to download and correctly place the Save to Android Pay button image with your link. For more information on deep links and Save to Android Pay, watch this video tutorial.

Gmail also allows top-level buttons to be placed in the subject line of emails via Go-To Actions. For example, you may want to include a button labeled "Save to Android Pay" which will appear in the subject line of your email and allow the user to save their loyalty card directly from their inbox. To enable this, include a View Action in your email with the above deep link.

Save to Android Pay via an Android App

If the only method to save your Android Pay valuables is from your app, the easiest solution is to use the wallet sdk package found in Google's Android APIs. The guide for using the API can be found here.

To save your loyalty card to Android Pay from your App, follow the above instructions for saving via email, using an ACTION_VIEW intent to open the deep link from the Save to Android Pay button.

Save within the Android Pay App

Users can add a loyalty card to their Android Pay app by either scanning or manually adding the loyalty card details. The loyaltyObject that is subsequently created does not reference any LoyaltyClass you may have defined. No work is required by you to enable this use case.

Update Points Balance

Updating the loyalty card points balance is an important way to engage with your customer.

Any update should begin with a GET request to retrieve the LoyaltyObject. This ensures the latest version of the object is used. The version of the object must be incremented when the balance is changed. Save the updated object by making a PUT request.

The following REST URIs are used to get an object and to put (update) an object:

GET https://www.googleapis.com/walletobjects/v1/loyaltyObject/resourceId
PUT https://www.googleapis.com/walletobjects/v1/loyaltyObject/resourceId

The following code samples provide examples of how to update an object in different languages:


// Get the specific Loyalty Object
LoyaltyObject obj = client.loyaltyobject().get("2945482443380251551.ExampleObject1").execute();
// Update the version and points
obj.setVersion(obj.getVersion() + 1L);
// Update the Loyalty Object
LoyaltyObject returnObj = client.loyaltyobject().update(obj.getId(), obj).execute();


# Get the specific Loyalty Object
result = api_client.execute(
  :api_method => walletobjects.loyaltyobject.get,
  :parameters => {'resourceId' => '2945482443380251551.ExampleObject1' }
# Update the version and points
obj_toupdate["version"] = obj_toupdate["version"].to_i + 1
obj_toupdate["loyaltyPoints"]["balance"]["string"] = "1000"
# Update the Loyalty Object
update_result = api_client.execute(
  :api_method => walletobjects.loyaltyobject.update,
  :parameters => {'resourceId' =>  '2945482443380251551.ExampleObject1'},
  :body_object => obj_toupdate


// Get the specific Loyalty Object
Google_LoyaltyObject loyaltyObj = $service->loyaltyobject->get('2945482443380251551.ExampleObject1');
// Update the version and points
loyaltyObj.setVersion(loyaltyObj.getVersion() + 1);
// Update the Loyalty Object
Google_LoyaltyObject loyaltyObj = $service->loyaltyobject->update('2945482443380251551.ExampleObject1',loyaltyObj);


# Get the specific Loyalty Object
loyalty_object = service.loyaltyobject().get(resourceId='2945482443380251551.ExampleObject1')
# Update the version and points
loyalty_object['version'] = str(int(loyalty_object['version']) + 1)
loyalty_object['points'] = points
# Update the Loyalty Object
api_request = service.loyaltyobject().update(resourceId='2945482443380251551.ExampleObject1',body=loyalty_object)
api_response = api_request.execute()

Link Offers to a Loyalty Card

Loyalty linked offers appear in a user's loyalty card view, making it easier to surface relevant offers. The writable list field linkedOfferIds in a LoyaltyObject indicates which offers are associated with the loyalty card. The offer must already be created, and then can be linked using the REST calls insert, update, or modifylinkedofferobjects used exclusively for this purpose. For more information on how to create loyalty linked offers, refer to this guide.

Redeem via NFC Tap

Your users can seamlessly redeem their loyalty cards via an NFC tap. Sign up to learn more.

Redeem via Show and Scan

Help your customers scan their loyalty cards by attaching a barcode. The API provides a variety of barcode types. Please refer to the reference for more details.

Determine if a user has removed their Loyalty Card

To know if a user has removed their Loyalty Card from Android Pay simply retrieve the user's object with the following call and check its hasUsers attribute.

GET https://www.googleapis.com/walletobjects/v1/loyaltyObject/objectId

If the Loyalty Card was removed, Google does not actually delete the object, but only flips the hasUsers attribute to false.

Checking the hasUsers attribute can be done either in real time, when the user logs in to your website or app, or in a batch process for large number of users at once.

The object ID used to retrieve the LoyaltyObject was defined by you at the moment of object creation. You must store these IDs in your own repository to have the ability to provide personalized information to your users about their current tier and points balances.

Google does not provide real time notifications when an object is deleted because that would require you to implement a listener service and to comply with a certain SLA.


Google can trigger notifications related to a consumer's loyalty card when they are within 150 meters of a location you have specified in the LoyaltyObject. This document provides instructions on adding geolocation information to your LoyaltyObject.

There are two ways to add geolocation information:

  1. Reference a Google Places ID
  2. Add coordinates directly to the object or class

Add geolocation information through the Google Places ID if you have one (or want to sign up for a Google Places account to take advantage of location-based search results). Otherwise, add geolocation information through the REST API.

Add geolocation information using a Places ID

The Save to Android Pay support team can add addresses to your Places ID. To contact the Save to Android Pay Objects support team:

  1. Visit our support forum
  2. Click Contact Us.
  3. Click Email.
  4. In the dropdown, select Contact a specialist.
  5. Click Go to Form. The Contact Save to Android Pay Support form appears.
  6. Fill in the fields and click on Issues with Your account.
  7. Click Next.
  8. Explain in the Please describe your issue field that you would like your Places ID tied to your Save to Android Pay issuer account. Be sure to specify your Places email address.
  9. Click Submit. Our Save to Android Pay support team will tie your Places ID to your Save to Android Pay account so your consumers can receive geolocation messages.

Note: Locations in the object will be used over locations in the class which will be used over locations in the Google Places ID. Also, please allow at least 24 hours for your locations associated with your places ID to become associated with your objects.

Add geolocation information using the REST API

You can specify an array of locations (latitudes and longitudes) in your classes or objects. Google checks the user's current geolocation against the list of locations associated with a class or object and notifies the user if they are within 150 meters from one of the locations. The following are code samples showing how to specify locations in your classes or objects:

Note: A class will have to be reviewed again if you update the locations in the class. Refer to the Class Approval Process for further information.


  ... //Class or Object content

  "locations": [{
    "kind": "walletobjects#latLongPoint",
    "latitude": 37.422087,
    "longitude": -161446
  }, {
    "kind": "walletobjects#latLongPoint",
    "latitude": 37.429379,
    "longitude": -121.12272999999999
  }, {
    "kind": "walletobjects#latLongPoint",
    "latitude": 37.333646,
    "longitude": -122.884853


List<LatLongPoint> locations = new ArrayList<LatLongPoint>();
locations.add(new LatLongPoint().setLatitude(37.422087).setLongitude(
locations.add(new LatLongPoint().setLatitude(37.429379).setLongitude(
locations.add(new LatLongPoint().setLatitude(37.333646).setLongitude(



offer_class = { 
  //class or object content	 
   "kind" => "walletobjects#latLongPoint",
     "latitude" => 37.442087,
     "longitude" => -122.161446
   "kind" => "walletobjects#latLongPoint",
     "latitude" => 37.429379,
     "longitude" => -122.12272999999999
   "kind" => "walletobjects#latLongPoint",
     "latitude" => 37.333646,
     "longitude" => -121.884853


$locations = array(
    'kind' => 'walletobjects#latLongPoint',
    'latitude' => 37.442087,
    'longitude' => -122.161446
    'kind' => 'walletobjects#latLongPoint',
    'latitude' => 37.429379,
    'longitude' => -122.12272999999999
    'kind' => 'walletobjects#latLongPoint',
    'latitude' => 37.333646,
    'longitude' => -121.884853


offer_class_object = {
  # class or object content
  'locations': [{
    'kind': 'walletobjects#latLongPoint',
    'latitude': 37.442087,
    'longitude': -122.161446
    'kind': 'walletobjects#latLongPoint',
    'latitude': 37.429379,
    'longitude': -122.12272999999999
    'kind': 'walletobjects#latLongPoint',
    'latitude': 37.333646,
    'longitude': -121.884853


Save to Android Pay allows merchants to provide localized content that will be served to the user based on their locale. Additional fields in the API are included to provide this capability. Every localized field is a LocalizedString nested object of the form:

  "kind": "walletobjects#localizedString",
  "translatedValues": [
      "kind": "walletobjects#translatedString",
      "language": string,
      "value": string
  "defaultValue": [
      "kind": "walletobjects#translatedString",
      "language": string,
      "value": string

The defaultValue is a required field for all LocalizedStrings. Both language and value are required in all translatedStrings.

The language field must refer to a BCP 47 language tag. (e.g. “en-US”, “en-GB”, “es-419”, etc). Value is the translated value of the string and is the string the user will see if their locale is matched.

Localization strings will be served to the user based on a best match of the user's locale. DefaultValue will be used if no appropriate translatedValue is provided. The non-localized fields will not be used if the corresponding localized field is set.

Send feedback about...

Save to Android Pay API