Google Apps Platform

Licensing API

Note: There's a new Google Apps Marketplace experience! Beginning November 19, 2013, new listings may only be created using the new version: existing developers may need to create a new Chrome Web Store account to publish new listings. Refer to the new documentation for more information.

This document explains how to use the Licensing API to get updates on a domain's license for a Marketplace application. This includes querying for notifications when the domain's license to the Marketplace application has been activated, unlicensed, or is still pending.

Contents

Audience

This document is intended for application developers who write client applications to query for notifications of when a Marketplace application has been enabled or disabled for a domain. It provides examples of the basic Licensing API interactions using raw XML, HTTP, and Java (Apache, v 2.0).

This document assumes you understand the general ideas behind the Google Data APIs protocol and the Google Feed Server.

How the API Works

To execute an operation using the API, you need to submit an HTTP GET request to the URL that corresponds to the type of licensing information you'd like to receive. Each URL includes variables that identify your Marketplace application, and potentially the domain whose information you are querying.

The tables in the subsections below identify the URLs associated with each API operation.

The Licensing API is based on the Atom Publishing Protocol and all data is published in the Atom format. The API will look very familiar to developers who already have experience building on Google Data APIs. However, the Licensing API is hosted by the Google Feed Server which uses Payload in Content to place all data elements related to license notifications as descendants of atom:content (which is itself a descendant of atom:entry) instead of making them direct descendants of atom:entry. See the Java example for more detail.

Authentication

All requests to the API must be signed using 2-legged OAuth, using the consumer key and secret obtained while creating a listing.

Client Libraries

One of the aims of serving the data in the format used by Google Feed Server is to lessen the need for custom classes for each feed. However, handling authentication and the XML parsing can still be made easier with client libraries. The Feed Server team has published Java classes which extend the Google Data APIs Java Client Library to make it easier to work with feeds produced by the Feed Server.

Operation and Element Limits

Operation/Element Limit
Pagination of responses with several results Batched in groups of 100 entries
The notification query's maxResults limit 100 notifications

Java Example

The following example provides the ability to both check whether an individual user is licensed and get the list of new domains and users that are licensed. It makes use of the Google Data APIs Java client library as well as Google Feed Server.

To use Google Feed Server:

  • Download and install the newest version of the Java client library.
  • Download and unzip Google Feed Server.
  • Add fsct/lib/*.jar and fsct/dist/*.jar to your BUILD path.

If you use the following sample code, make sure to enter your values for keys, secrets, and IDs, over the sample values given below.

package licensefeedsample;

import com.google.feedserver.client.FeedServerClient;
import com.google.feedserver.client.FeedServerEntry;
import com.google.feedserver.client.FeedServerFeed;
import com.google.gdata.client.GoogleService;
import com.google.gdata.client.authn.oauth.GoogleOAuthParameters;
import com.google.gdata.client.authn.oauth.OAuthHmacSha1Signer;
import com.google.gdata.client.authn.oauth.OAuthParameters;
import com.google.gdata.client.authn.oauth.OAuthParameters.OAuthType;
import com.google.gdata.data.Link;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class LicenseFeedClient {
  public static class LicenseState {
    private boolean enabled;
    private String state;

    public boolean isEnabled() { return enabled; }
    public void setEnabled(boolean enabled) { this.enabled = enabled; }

    public String getState() { return state; }
    public void setState(String state) { this.state = state; }
  }

  public static class LicenseNotification {
    private String domainname;
    private long productconfigid;
    private String state;
    private String installeremail;
    private String tosacceptancetime;
    private String lastchangetime;

    public String getDomainname() { return domainname; }
    public void setDomainname(String domainName) { this.domainname = domainName; }

    public long getProductconfigid() { return productconfigid; }
    public void setProductconfigid(long productConfigId) { this.productconfigid = productConfigId; }

    public String getState() { return state; }
    public void setState(String state) { this.state = state; }
    
    public String getInstalleremail() { return installeremail; }
    public void setInstalleremail(String installeremail) { this.installeremail = installeremail; }
    
    public String getTosacceptancetime() { return tosacceptancetime; }
    public void setTosacceptancetime(String tosacceptancetime) { this.tosacceptancetime = tosacceptancetime; }
    
    public String getLastchangetime() { return lastchangetime; }
    public void setLastchangetime(String lastchangetime) { this.lastchangetime = lastchangetime; }
  }

  // Replace these with correct values.
  private static final String OAUTH_CONSUMER_KEY = "123456789.apps.googleusercontent.com";
  private static final String OAUTH_CONSUMER_SECRET = "dasgjkljlkjj3l22xjkljl";
  private static final String APP_ID = "appid=123456789";
  private static final String URL_BASE = "http://feedserver-enterprise.googleusercontent.com/license?bq=";
  private static final String OPEN_SQUARE = "%5B"; // "["
  private static final String CLOSE_BRACE = "%5D"; // "]"

  /**
   * @param args
   */
  public static void main(String[] args) throws Exception {
    getLicenseState();
    getLicenseNotifications();
  }

  // This function should be called from the application when the user tries to access the app.
  private static void getLicenseState() throws Exception {
    // User domain is obtained from the end user request.
    String USER_DOMAIN = "domain=domaintocheck.com";

    String urlStr = URL_BASE +
        OPEN_SQUARE + APP_ID + CLOSE_BRACE +
        OPEN_SQUARE + USER_DOMAIN + CLOSE_BRACE;
    URL url = new URL(urlStr);

    GoogleService service = new GoogleService("License", "LicenseFeedClient");
    OAuthParameters params = new GoogleOAuthParameters();

    params.setOAuthConsumerKey(OAUTH_CONSUMER_KEY);
    params.setOAuthConsumerSecret(OAUTH_CONSUMER_SECRET);
    params.setOAuthType(OAuthType.TWO_LEGGED_OAUTH);

    service.setOAuthCredentials(params, new OAuthHmacSha1Signer());

    FeedServerClient<LicenseState> fsct = new FeedServerClient<LicenseState>(service, LicenseState.class);
    FeedServerFeed feed = fsct.getFeed(url);
    List<FeedServerEntry> entries = feed.getEntries();
    assert(entries.size() == 1);
    LicenseState licenseState = entries.get(0).getEntity(LicenseState.class);

    if (!licenseState.getState().equals("ACTIVE")) {
      // ServeYouDoNotHaveAValidLicensePage();
    } else if (!licenseState.isEnabled()) {
      // ServeYourAdminHasNotEnabledYourAppPage();
    } else {
      // ServeNormalApplicationPage();
    }
  }

  // This should be run as a batch job in the background monitoring for new licenses.
  private static void getLicenseNotifications() throws Exception {
    String START_DATE_TIME = "startdatetime=2009-01-05TZ";
    String MAX_RESULTS = "max-results=10";
    String OPEN_SQUARE = "%5B"; // "["
    String CLOSE_BRACE = "%5D"; // "]"
    long SLEEP_TIME_IN_MILLIS = 10 * 60 * 1000;

    String urlStr = URL_BASE +
        OPEN_SQUARE + APP_ID + CLOSE_BRACE +
        OPEN_SQUARE + START_DATE_TIME + CLOSE_BRACE +
        OPEN_SQUARE + MAX_RESULTS + CLOSE_BRACE;
    URL url = new URL(urlStr);

    while (true) {
      GoogleService service = new GoogleService("LicenseNotification", "LicenseNotificationFeedClient");
      OAuthParameters params = new GoogleOAuthParameters();

      params.setOAuthConsumerKey(OAUTH_CONSUMER_KEY);
      params.setOAuthConsumerSecret(OAUTH_CONSUMER_SECRET);
      params.setOAuthType(OAuthType.TWO_LEGGED_OAUTH);

      service.setOAuthCredentials(params, new OAuthHmacSha1Signer());

      FeedServerClient<LicenseNotification> fsct = new FeedServerClient<LicenseNotification>(service, LicenseNotification.class);
      FeedServerFeed feed = fsct.getFeed(url);
      List<FeedServerEntry> entries = feed.getEntries();
      List<LicenseNotification> entities = new ArrayList<LicenseNotification>();
      for (FeedServerEntry entry : entries) {
        LicenseNotification licenseNotification = entry.getEntity(LicenseNotification.class);
        if (licenseNotification.getState().equals("ACTIVE")) {
          // HandleActive(entity.getDomainname(), entity.getProductconfigid());
        } else if (licenseNotification.getState().equals("UNLICENSED")) {
          // HandleUnlicensed(entity.getDomainname(), entity.getProductconfigid());
        } else if (licenseNotification.getState().equals("EXPIRED")) {
          // HandleExpired(entity.getDomainname(), entity.getProductconfigid());
        } else if (licenseNotification.getState().equals("DELINQUENT")) {
          // HandleDelinquent(entity.getDomainname(), entity.getProductconfigid());
        } else {
          // HandleUnknownState(entity.getDomainname(), entity.getProductconfigid());
        }
      }

      Link nextLink = feed.getNextLink();
      url = new URL(nextLink.getHref());
      // SaveUrlInDbForRecovery(url);
      if (entities.size() == 0) {
        Thread.sleep(SLEEP_TIME_IN_MILLIS);
      }
    }
  }
}

Check that a Domain is Licensed

Operation HTTP Request Type and URL
Check that a domain is licensed GET http://feedserver-enterprise.googleusercontent.com/license?bq=[appid=applicationId][domain=domain]

Example Result

<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'>
  <id>http://feedserver-enterprise.googleusercontent.com/license</id>
  <updated>2009-11-30T21:24:22.560Z</updated>
  <link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://feedserver-enterprise.googleusercontent.com/license'/>
  <link rel='http://schemas.google.com/g/2005#post' type='application/atom+xml' href='http://feedserver-enterprise.googleusercontent.com/license'/>
  <link rel='self' type='application/atom+xml' href='http://feedserver-enterprise.googleusercontent.com/license?bq=%5Bappid%3D872939636068%5D%5Bdomain%3Dtestapp.com%5D'/>
  <openSearch:startIndex>1</openSearch:startIndex>
  <entry>
    <id>http://feedserver-enterprise.googleusercontent.com/license/PACKAGE_GAIAID-123456789-example.com-</id>
    <updated>2009-11-30T21:24:22.557Z</updated>
    <content type='application/xml'>
      <entity>
        <id>PACKAGE_GAIAID-123456789-example.com-</id>
          <enabled>true</enabled>
          <state>ACTIVE</state>
      </entity>
    </content>
    <link rel='self' type='application/atom+xml' href='http://feedserver-enterprise.googleusercontent.com/license/PACKAGE_GAIAID-123456789-example.com-'/>
    <link rel='edit' type='application/atom+xml' href='http://feedserver-enterprise.googleusercontent.com/license/PACKAGE_GAIAID-123456789-example.com-'/>
  </entry>
</feed>

Check for License Notifications

Operation HTTP Request Type and URL
Check for license notifications GET http://feedserver-enterprise.googleusercontent.com/licensenotification?bq=[appid=applicationId][startdatetime=time][max-results=maxResults]

Example Result

<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'>
  <id>http://feedserver-enterprise.googleusercontent.com/licensenotification</id>
  <updated>2009-11-30T21:24:19.982Z</updated>
  <link rel='next' type='application/atom+xml' href='http://feedserver-enterprise.googleusercontent.com/licensenotification?bq=%5Bmax-results=10%5D%5Bcontinuationtoken=CM3IzKHRJBIOUEFDS0FHRV9HQUlBSUQaDDg3MjkzOTYzNjA2OCCzCQ%5D'/>
  <link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://feedserver-enterprise.googleusercontent.com/licensenotification'/>
  <link rel='http://schemas.google.com/g/2005#post' type='application/atom+xml' href='http://feedserver-enterprise.googleusercontent.com/licensenotification'/>
  <link rel='self' type='application/atom+xml' href='http://feedserver-enterprise.googleusercontent.com/licensenotification?bq=%5Bappid%3D872939636068%5D%5Bstartdatetime%3D2009-01-05TZ%5D%5Bmax-results%3D10%5D'/>
  <openSearch:startIndex>1</openSearch:startIndex>
  <entry>
    <id>http://feedserver-enterprise.googleusercontent.com/licensenotification/CID42qDqIxIOUEFDS0FHRV9HQUlBSUQaDDg3MjkzOTYzNjA2OCAA-0</id>
    <updated>2009-11-30T21:24:19.976Z</updated>
    <content type='application/xml'>
      <entity>
        <id>CID42qDqIxIOUEFDS0FHRV9HQUlBSUQaDDg3MjkzOTYzNjA2OCAA-0</id>
        <domainname>example.com</domainname>
        <installeremail>root@example.com</installeremail>
        <tosacceptancetime>2010-02-10T21:18:31.395Z</tosacceptancetime>
        <lastchangetime>2010-02-10T21:13:53.227Z</lastchangetime>
        <productconfigid>502</productconfigid>
        <state>UNLICENSED</state>
      </entity>
    </content><link rel='self' type='application/atom+xml' href='http://feedserver-enterprise.googleusercontent.com/licensenotification/CID42qDqIxIOUEFDS0FHRV9HQUlBSUQaDDg3MjkzOTYzNjA2OCAA-0'/>
    <link rel='edit' type='application/atom+xml' href='http://feedserver-enterprise.googleusercontent.com/licensenotification/CID42qDqIxIOUEFDS0FHRV9HQUlBSUQaDDg3MjkzOTYzNjA2OCAA-0'/>
  </entry>
  <entry>
    <id>http://feedserver-enterprise.googleusercontent.com/licensenotification/CID42qDqIxIOUEFDS0FHRV9HQUlBSUQaDDg3MjkzOTYzNjA2OCAA-1</id>
    <updated>2009-11-30T21:24:19.977Z</updated>
    <content type='application/xml'>
      <entity>
        <id>CID42qDqIxIOUEFDS0FHRV9HQUlBSUQaDDg3MjkzOTYzNjA2OCAA-1</id>
        <domainname>example2.com</domainname>
        <installeremail>root@example2.com</installeremail>
        <tosacceptancetime>2010-02-10T21:18:31.395Z</tosacceptancetime>
        <lastchangetime>2010-02-10T21:13:53.227Z</lastchangetime>
        <productconfigid>502</productconfigid>
        <state>ACTIVE</state>
      </entity>
    </content>
    <link rel='self' type='application/atom+xml' href='http://feedserver-enterprise.googleusercontent.com/licensenotification/CID42qDqIxIOUEFDS0FHRV9HQUlBSUQaDDg3MjkzOTYzNjA2OCAA-1'/>
    <link rel='edit' type='application/atom+xml' href='http://feedserver-enterprise.googleusercontent.com/licensenotification/CID42qDqIxIOUEFDS0FHRV9HQUlBSUQaDDg3MjkzOTYzNjA2OCAA-1'/>
  </entry>
</feed>

Results Pagination

If you request licensing notifications, the server will return an Atom XML feed containing a maximum of 100 entries. If there are more than 100 entries in the feed, the API response will contain a <link> tag for which the rel attribute has a value of next. The URL identified in that <link> tag points to the next page of results for the request.

In the XML excerpt below, the <link> tag that identifies the next page of results is highlighted in bold text:

<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'>
  <id>http://feedserver-enterprise.googleusercontent.com/licensenotification</id>
  <updated>2009-11-30T21:24:19.982Z</updated>
  <link rel='next' type='application/atom+xml' href='http://feedserver-enterprise.googleusercontent.com/licensenotification?bq=%5Bmax-results=10%5D%5Bcontinuationtoken=CM3IzKHRJBIOUEFDS0FHRV9HQUlBSUQaDDg3MjkzOTYzNjA2OCCzCQ%5D'/>

XML Tag Reference

This section contains definitions for the XML tags used in the responses from the Licensing API.

feed
Definition

The <feed> tag encapsulates an API response to a request to retrieve whether an individual domain is licensed to use the application, or the recent notifications for updates to enablement status.

Example <feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'>
Subtags id, link, openSearch:startIndex, entry
Content Format Container
entry
Definition

The <entry> tag encapsulates an API response.

Subtag of feed
Subtags id, updated, content
Content Format Container
id
Definition

The <id> tag's value identifies a permanent, unique identifier for a feed, entry, or payload entity.

Example <id>http://feedserver-enterprise.googleusercontent.com/licensenotification/CID42qDqIxIOUEFDS0FHRV9HQUlBSUQaDDg3MjkzOTYzNjA2OCAA-0</id>
Subtag of feed, entry, entity
Content Format String (IRI)
link
Definition

The <link> tag provides an IRI reference related to an API results feed or a resource in the feed.

Attributes
Name Format Description
rel Text

The rel attribute identifies the relationship of the link to the API response feed.

  • If the value of the rel attribute is self, then the href attribute value will be a link to the URL you would use to request the feed.

  • If the value of the rel attribute is next, then the href attribute value contains the URL that you would use to retrieve the next page of results for the request. Please see the Results Pagination section of this document for more information.

href Text The href attribute contains an IRI reference that indicates how to retrieve the information in an API response.
Example <link rel='self' type='application/atom+xml' href='http://feedserver-enterprise.googleusercontent.com/licensenotification?bq=%5Bappid%3D12345678%5D%5Bstartdatetime%3D2009-01-05TZ%5D%5Bmax-results%3D10%5D'/>
Subtag of entry
Content Format Empty
updated
Definition

The <updated> tag identifies the date and time that an entry in an Atom feed was updated.

Example <updated>2009-11-30T21:24:19.982Z</updated>
Subtag of feed, entry
Content Format Date (RFC 3339)
openSearch:startIndex
Definition

The <openSearch:startIndex> value identifies the one-based index of the first item returned in the result.

Example <openSearch:startIndex>1</openSearch:startIndex>
Subtag of feed
Content Format Integer
content
Definition

The <content> tag contains the payload of the API response.

Attributes
Name Format Description
type Text

The type attribute declares the mime type of the content payload.

Example <content type='application/xml'>
Subtag of entry
Subtags entity
Content Format Complex
entity
Definition

The <entity> tag wraps an individual entity from the payload.

Example <entity>
Subtag of content
Subtags id, domainname, installeremail, tosacceptancetime, lastchangetime, productconfigid, state
Content Format Complex
domainname
Definition

The <domainname> tag supplies the domain whose licensing attributes are described by the current entity.

Example <domainname>example.com</domainname>
Subtag of entity
Content Format Text
installeremail
Definition

The <installeremail> tag supplies the email address of the administrator of the domain that installed the application.

Example <installeremail>root@example.com</installeremail>
Subtag of entity
Content Format Text
tosacceptancetime
Definition

The <tosacceptancetime> tag indicates when the domain administrator accepted the application's terms of service.

Example <tosacceptancetime>2010-02-10T21:18:31.395Z</tosacceptancetime>
Subtag of entity
Content Format Date (RFC 3339)
lastchangetime
Definition

The <lastchangetime> tag indicates when the domain's licensing status was last modified.

Example <lastchangetime>2010-02-10T21:13:53.227Z</lastchangetime>
Subtag of entity
Content Format Date (RFC 3339)
productconfigid
Definition

The <productconfigid> tag is not used externally and can be safely ignored.

Example <productconfigid>502</productconfigid>
Subtag of entity
Content Format Integer
state
Definition

The <state> tag indicates the current enablement state of the application for the given domain. The state can be one of three values. ACTIVE means that the application is licensed and enabled. UNLICENSED means that the application is no longer licensed, which usually corresponds to an application deletion. PENDING means that the application has been ordered (usually upon installation via the Marketplace), but has not yet reached the license API server. A domain/application pair will usually only be in this state for a few seconds.

Details on how domain administrators install, enable, and disable applications are available in the lifecycle documentation.

Example <state>ACTIVE</state>
Subtag of entity
Content Format String

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.