How it works

The customer API gives programmatic control of devices and configuration for Android zero-touch enrollment. This document introduces the API to enterprise mobility management (EMM) providers and enterprise IT developers. After reading this document, you should understand the core resources used in the API and how they interact. If you're new to zero-touch enrollment, read the short android.com introduction.

 Overview

The customer API helps organizations that purchase Android zero-touch enrollment devices. Your app or tool can help IT admins do the following:

  • Create, edit, and delete provisioning configurations.
  • Apply or remove a configuration to a device.
  • Select a default configuration for any devices added to zero-touch enrollment going forward.

Through the API, IT admins can also unregister devices from zero-touch enrollment. To manage their organization's users or accept the Terms of Service, IT admins use the zero-touch enrollment portal.

Typical users of this API might be:

  • EMM providers adding support for zero-touch enrollment to their console.
  • Enterprise IT developers creating tools to automate zero-touch enrollment tasks.

Core resources

Configurations and devices are the core resources you use in the API. An organization can also create configurations and devices using the zero-touch enrollment portal.

Device and customer resource relationship

Configuration
IT admins set provisioning options for devices using a configuration. Configurations include EMM mobile policies and contact info that's displayed to help users. Configurations are central to the API, so you use them in many methods. To learn more, see Configurations below.
Device
A zero-touch enrollment-capable Android device an organization purchased from their reseller. Apply a configuration to include the device in zero-touch enrollment. Devices have hardware IDs and attached-metadata. To learn more, see Devices below.
DPC
A read-only reference to an EMM's DPC (device policy controller). Add a DPC to a configuration to select the EMM solution for devices. All the DPCs listed by the API support zero-touch enrollment and are available in Google Play. To learn more, see Dpc.

To list all the API methods and resources your app can use, see the API Reference.

Configurations

The Configuration API resource combines the following:

  • The EMM's DPC installed on the devices.
  • EMM policies enforced on the devices.
  • Contact information displayed on the device to help users during setup.

Using the API, your app can manage configurations for IT admins. Call the API to fetch, create, update, and delete configurations. The example below shows how to create a new configuration:

Java

// Add metadata to help the device user during provisioning.
Configuration configuration = new Configuration();
configuration.setConfigurationName("Sales team");
configuration.setCompanyName("XYZ Corp.");
configuration.setContactEmail("it-support@example.com");
configuration.setContactPhone("+1 (800) 555-0112");
configuration.setCustomMessage("We're setting up your phone. Call or email for help.");

// Set the DPC that zero-touch enrollment downloads and installs from Google Play.
configuration.setDpcResourcePath(dpc.getName());

// Set the JSON-formatted EMM provisioning extras that are passed to the DPC.
configuration.setDpcExtras("{"
      + "\"android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED\":true,"
      + "\"android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE\":{"
      + "\"default_min_password_length\":6,"
      + "\"company_name\":\"XYZ Corp\","
      + "\"management_server\":\"emm.example.com\","
      + "\"terms_url\":\"https://www.example.com/policies/terms/\","
      + "\"allowed_user_domains\":\"[\\\"example.com\\\", \\\"example.org\\\"]\""
      + "}"
      + "}");

// Create the new configuration on the server.
AndroidProvisioningPartner.Customers.Configurations.Create request =
      service.customers().configurations().create(customerAccount, configuration);
Configuration response = request.execute();

.NET

// Add metadata to help the device user during provisioning.
Configuration configuration = new Configuration
{
    ConfigurationName = "Sales team",
    CompanyName = "XYZ Corp.",
    ContactEmail = "it-support@example.com",
    ContactPhone = "+1 (800) 555-0112",
    CustomMessage = "We're setting up your phone. Call or email for help."
};

// Set the DPC that zero-touch enrollment downloads and installs from Google Play.
configuration.DpcResourcePath = dpc.Name;

// Set the JSON-formatted EMM provisioning extras that are passed to the DPC.
configuration.DpcExtras = @"{
    ""android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED"":true,
    ""android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE"":{
    ""default_min_password_length"":6,
    ""company_name"":""XYZ Corp"",
    ""management_server"":""emm.example.com"",
    ""terms_url"":""https://www.example.com/policies/terms/"",
    ""allowed_user_domains"":""[\""example.com\"", \""example.org\""]""
  }
}";

// Create the new configuration on the server.
var request = service.Customers.Configurations.Create(configuration, customerAccount);
var response = request.Execute();

Python

# Add metadata to help the device user during provisioning.
configuration = {
    'configurationName': 'Sales team',
    'companyName': 'XYZ Corp.',
    'contactEmail': 'it-support@example.com',
    'contactPhone': '+1 (800) 555-0112',
    'customMessage': 'We\'re setting up your phone. Call or email for help.'}

# Set the DPC that zero-touch enrollment installs from Google Play.
configuration['dpcResourcePath'] = dpc['name']

# Set the JSON-formatted EMM provisioning extras that are passed to the DPC.
configuration['dpcExtras'] = '''{
    "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED":true,
    "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE":{
      "default_min_password_length":6,
      "company_name":"XYZ Corp",
      "management_server":"emm.example.com",
      "terms_url":"https://www.example.com/policies/terms/",
      "allowed_user_domains":"[\\"example.com\\", \\"example.org\\"]"}
}'''

# Create the new configuration on the server.
response = service.customers().configurations().create(
    parent=customer_account, body=configuration).execute()

When you update a configuration using the patch API, remember to include the field mask—or a value for every field you don't want to be null. See Default configurations (below) for an example that shows how to efficiently update a configuration.

Delete configurations

You can't delete a configuration if it's still applied to devices. If you try to delete an in-use configuration, the API method returns an HTTP 400 Bad Request status code and a message explaining how many devices use the configuration. Call customers.devices.removeConfiguration to remove the configuration from the devices before trying again.

Default configurations

Zero-touch enrollment works best when an organization sets a default configuration that's applied to any new devices the organization purchases. Consider prompting IT admins to set a default configuration if one isn't set. The example below shows how to make an existing configuration the default by setting isDefault to true:

Java

// Send minimal data with the request. Just the 2 required fields.
// targetConfiguration is an existing configuration that we want to make the default.
Configuration configuration = new Configuration();
configuration.setIsDefault(true);
configuration.setConfigurationId(targetConfiguration.getConfigurationId());

// Call the API, including the FieldMask to avoid setting other fields to null.
AndroidProvisioningPartner.Customers.Configurations.Patch request = service
      .customers()
      .configurations()
      .patch(targetConfiguration.getName(), configuration);
request.setUpdateMask("isDefault");
Configuration results = request.execute();

.NET

// Send minimal data with the request. Just the 2 required fields.
// targetConfiguration is an existing configuration that we want to make the default.
Configuration configuration = new Configuration
{
    IsDefault = true,
    ConfigurationId = targetConfiguration.ConfigurationId,
};

// Call the API, including the FieldMask to avoid setting other fields to null.
var request = service.Customers.Configurations.Patch(configuration,
                                                     targetConfiguration.Name);
request.UpdateMask = "IsDefault";
Configuration results = request.Execute();

Python

# Send minimal data with the request. Just the 2 required fields.
# target_configuration is an existing configuration we'll make the default.
configuration = {
    'isDefault': True,
    'configurationId': target_configuration['configurationId']}

# Call the API, including the FieldMask to avoid setting other fields to null.
response = service.customers().configurations().patch(
    name=target_configuration['name'],
    body=configuration, updateMask='isDefault').execute()

There can be only one default configuration. Making a new default configuration, sets a previous configuration's isDefault field to false. You might need to refresh any cached Configuration instances to see correct values in the isDefault fields.

Guide device users

Zero-touch configuration displays customized user guidance in the device Setup Wizard to help users. You need to include a contact telephone number and email address along with the name of the organization that manages the device in a configuration. We also recommend including one or two sentences in the customMessage field to give more details about what’s happening to a user's device.

Because the user won't be able to call or email from the device they're setting up, format the telephone number and email address to make it easier to glance at the information.

Devices

Resellers create devices when a customer purchases them for zero-touch enrollment—IT admins can't create devices. To work with devices, call methods on the Device API resource. If you need to search for devices, list all the devices and filter each batch locally in your app. For an example, see Paged results below.

Configure devices

Applying a configuration to a device registers the device for zero-touch enrollment. To apply a configuration, call customers.devices.applyConfiguration. After applying a configuration, the device automatically provisions itself on first boot, or next factory reset. The example below shows how you might apply a configuration to a collection of devices:

Java

List<Device> devices = getDevicesToConfigure(service);
Configuration configurationToApply = getConfigurationToApply(service);

// Loop through the collection and apply the configuration to each device. This might
// take some time if the collection contains many devices.
for (Device device : devices) {
    System.out.println(device.getDeviceIdentifier().getImei());

    // Wrap the device ID in a DeviceReference.
    DeviceReference deviceRef = new DeviceReference();
    deviceRef.setDeviceId(device.getDeviceId());

    // Build and send the request to the API.
    CustomerApplyConfigurationRequest body = new CustomerApplyConfigurationRequest();
    body.setConfiguration(configurationToApply.getName());
    body.setDevice(deviceRef);

    AndroidProvisioningPartner.Customers.Devices.ApplyConfiguration request = service
          .customers()
          .devices()
          .applyConfiguration(customerAccount, body);
    request.execute();
}

.NET

IList<Device> devices = GetDevicesToConfigure(service);
Configuration configurationToApply = GetConfigurationToApply(service);

// Loop through the collection and apply the configuration to each device. This might
// take some time if the collection contains many devices.
foreach (Device device in devices)
{
    Console.WriteLine(device.DeviceIdentifier.Imei);

    // Wrap the device ID in a DeviceReference.
    var deviceRef = new DeviceReference
    {
        DeviceId = device.DeviceId
    };

    // Build and send the request to the API.
    CustomerApplyConfigurationRequest body = new CustomerApplyConfigurationRequest
    {
        Configuration = configurationToApply.Name,
        Device = deviceRef
    };
    var request = service.Customers.Devices.ApplyConfiguration(body,
                                                               customerAccount);
    request.Execute();
}

Python

devices = get_devices_to_configure(service)
configuration = get_configuration_to_apply(service)

# Loop through the collection and apply the configuration to each device.
# This might take some time if the collection contains many devices.
for device in devices:
  print(device['deviceIdentifier']['imei'])

  # Wrap the device ID in a DeviceReference.
  device_ref = {'deviceId': device['deviceId']}

  # Build and send the request to the API.
  body = {'configuration': configuration['name'], 'device': device_ref}
  service.customers().devices().applyConfiguration(
      parent=customer_account, body=body).execute()

To remove the configuration from a device, call customers.devices.removeConfiguration. The change takes effect after factory resetting the device.

Unclaim devices

IT admins can unclaim a device to remove it from zero-touch enrollment. An IT admin might unclaim a device that they want migrated to another account, sold, or returned to the reseller. Call the method customers.devices.unclaim to unclaim a device from an organization.

The example below shows how to unclaim a device from an IMEI number and manufacturer name:

Java

// Wrap the hardware ID and manufacturer values in a DeviceIdentifier.
// Then wrap the DeviceIdentifier in a DeviceReference.
DeviceIdentifier identifier = new DeviceIdentifier();
identifier.setImei("123456789012347");
identifier.setManufacturer("Google");
DeviceReference reference = new DeviceReference();
reference.setDeviceIdentifier(identifier);

// Create the body of the request.
CustomerUnclaimDeviceRequest body = new CustomerUnclaimDeviceRequest();
body.setDevice(reference);

// Call the API method to unclaim the device from the organization.
service.customers().devices().unclaim(customerAccount, body).execute();

.NET

// Wrap the hardware ID and manufacturer values in a DeviceIdentifier.
// Then wrap the DeviceIdentifier in a DeviceReference.
DeviceIdentifier identifier = new DeviceIdentifier
{
    Imei = "123456789012347",
    Manufacturer = "Google"
};
DeviceReference reference = new DeviceReference();
reference.DeviceIdentifier = identifier;

// Create the body of the request.
CustomerUnclaimDeviceRequest body = new CustomerUnclaimDeviceRequest();
body.Device = reference;

// Call the API method to unclaim the device from the organization.
service.Customers.Devices.Unclaim(body, customerAccount).Execute();

Python

# Wrap the hardware ID and manufacturer values in a DeviceIdentifier.
# Then wrap the DeviceIdentifier in a DeviceReference.
identifier = {'imei': '123456789012347', 'manufacturer': 'Google'}
reference = {'deviceIdentifier': identifier}

# Create the body of the request.
body = {'device': reference}

# Call the API method to unclaim the device from the organization.
service.customers().devices().unclaim(
    parent=customer_account, body=body).execute()

Device metadata

An IT admin can see metadata attached to the device by the reseller. Display this device metadata in your app to help IT admins recognize devices.

To learn more about the metadata you might see, read the Device metadata guide for resellers.

Paged results

The customers.devices.list API method might return very large lists of devices. To reduce the response size, this and other API methods (such as customers.list) support paged results. With paged results, your application can iteratively request and process large lists one page at a time.

After calling the API method, check if the response includes a value for nextPageToken. If nextPageToken isn't null, your app can use it to fetch another page of devices by calling the method again. You need to set an upper limit for the number of devices in the pageSize parameter. If nextPageToken is null, your app has requested the last page.

The example method below shows how your app might print a list of devices, one page at a time:

Java

private void printDevices(AndroidProvisioningPartner service, String customerAccount,
      String pageToken) throws IOException {

    // Call the API to get a page of Devices. Send a page token from the method argument.
    // If the page token is null, the API returns the first page.
    AndroidProvisioningPartner.Customers.Devices.List request =
          service.customers().devices().list(customerAccount);
    request.setPageSize(50L);
    request.setPageToken(pageToken);
    CustomerListDevicesResponse response = request.execute();

    // Print the devices included in this page of results.
    for (Device device : response.getDevices()) {
        System.out.format("Device: %s\n", device.getName());
    }
    System.out.println("---");

    // Check to see if another page of devices is available. If yes, fetch & print the devices.
    if (response.getNextPageToken() != null) {
        this.printDevices(service, customerAccount, response.getNextPageToken());
    }
}

.NET

private void PrintDevices(AndroidProvisioningPartnerService service, String customerAccount,
                          String pageToken)
{
    // Call the API to get a page of Devices. Send a page token from the method argument.
    // If the page token is null, the API returns the first page.
    var request = service.Customers.Devices.List(customerAccount);
    request.PageSize = 50;
    request.PageToken = pageToken;
    var response = request.Execute();

    // Print the devices included in this page of results.
    foreach (Device device in response.Devices)
    {
        Console.WriteLine("Device: {0}", device.Name);
    }
    Console.WriteLine("---");

    // Check to see if another page of devices is available. If yes, fetch and print the devices.
    if (response.NextPageToken != null)
    {
        this.PrintDevices(service, customerAccount, response.NextPageToken);
    }
}

Python

def print_devices(service, customer_account, page_token):
  """Demonstrates how to loop through paginated lists of devices."""

  # Call the API to get a page of Devices. Send a page token from the method
  # argument. If the page token is None, the API returns the first page.
  response = service.customers().devices().list(
      parent=customer_account, pageSize=50, pageToken=page_token).execute()

  # Print the devices included in this page of results.
  for device in response['devices']:
    print('Device: {0}'.format(device['name']))
  print('---')

  # Check to see if another page of devices is available. If yes,
  # fetch and print the devices.
  if 'nextPageToken' in response:
    print_devices(service, customer_account, response['nextPageToken'])

Get started

Next, read how to authorize API calls in Authorization. If you want to explore the APIs, take a look at the quickstart guides for Java, .NET, and Python. You can use a colab to view examples of API calls and experiment with calling the API yourself.