The [customer API](https://developers.google.com/zero-touch/reference/customer/rest) 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](https://www.android.com/enterprise/management/zero-touch/).

## 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](https://enterprise.google.com/android/zero-touch/resellers).

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](https://developers.google.com/static/zero-touch/images/guides/customer/how-it-works/resources.svg)

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](https://developers.google.com/zero-touch/guides/customer/how-it-works#configs) 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](https://developers.google.com/zero-touch/guides/customer/how-it-works#devices) below.

DPC
:   A read-only reference to an EMM's DPC ([device policy
    controller](https://developer.android.com/work/dpc/build-dpc.html)). 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`](https://developers.google.com/zero-touch/reference/customer/rest/v1/customers.dpcs).

To list all the API methods and resources your app can use, see the
[API Reference](https://developers.google.com/zero-touch/reference/customer/rest).

## Configurations

The [`Configuration`](https://developers.google.com/zero-touch/reference/customer/rest/v1/customers.configurations) 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

```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

```c#
// 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

```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](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#fieldmask)---or a
value for every field you don't want to be `null`. See [Default
configurations](https://developers.google.com/zero-touch/guides/customer/how-it-works#default_config) (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`](https://developers.google.com/zero-touch/reference/customer/rest/v1/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

```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

```c#
// 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

```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`](https://developers.google.com/zero-touch/reference/customer/rest/v1/customers.devices#methods) 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](https://developers.google.com/zero-touch/guides/customer/how-it-works#paged) below.

### Configure devices

Applying a configuration to a device registers the device for zero-touch
enrollment. To apply a configuration, call
[`customers.devices.applyConfiguration`](https://developers.google.com/zero-touch/reference/customer/rest/v1/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

```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

```c#
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

```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`](https://developers.google.com/zero-touch/reference/customer/rest/v1/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`](https://developers.google.com/zero-touch/reference/customer/rest/v1/customers.devices/unclaim) to unclaim a device
from an organization.

> [!CAUTION]
> **Caution:** Consider prompting your app's user to confirm that they have the correct device before calling unclaim APIs. Unclaiming a device removes the organization's provisioning setup and the device becomes unmanaged after a factory reset. The IT admin must contact their reseller if they want to register the device into zero-touch enrollment again.

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

### Java

```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

```c#
// 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

```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](https://developers.google.com/zero-touch/guides/metadata) guide for resellers.

## Paged results

The [`customers.devices.list`](https://developers.google.com/zero-touch/reference/customer/rest/v1/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`](https://developers.google.com/zero-touch/reference/customer/rest/v1/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`](https://developers.google.com/zero-touch/reference/customer/rest/v1/customers.devices/list#response-body). 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

```c#
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

```c#
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

```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](https://developers.google.com/zero-touch/guides/customer/auth). If you want to
explore the APIs, take a look at the quickstart guides for
[Java](https://developers.google.com/zero-touch/guides/customer/quickstart/java),
[.NET](https://developers.google.com/zero-touch/guides/customer/quickstart/dotnet), and
[Python](https://developers.google.com/zero-touch/guides/customer/quickstart/python). You can use a
[colab](https://colab.research.google.com/github/googlesamples/zero-touch-enrollment-colabs/blob/main/Zero_touch_enrollment_customer_API.ipynb) to view
examples of API calls and experiment with calling the API yourself.