Skip navigation
Documentation

Duo Device API for Trusted Endpoints with Device Health

Last Updated: May 10th, 2022

Contents

This API documentation is for Trusted Endpoints Generic Integrations which use the Duo Device Health App to establish trust.

Overview

Duo's Trusted Endpoints feature secures your sensitive applications by ensuring that only known devices can access Duo protected services. When a user authenticates via the Duo Prompt, we'll compare device identifiers collected by the Duo Device Health application installed on that endpoint with the identifiers of known Windows and macOS devices stored in Duo. You can monitor access to your applications from managed and unmanaged devices, and optionally block access from devices not managed by your organization.

Trusted Endpoints and the Device API are part of the Duo Beyond plan.

Before enabling the Trusted Endpoints policy on your applications, you'll need to create a device cache with the identifying information for your known devices in Duo's service using the Device API. Be sure to read the Trusted Endpoints Generic with Device Health management integration instructions in full before using Device API.

Review the API Details to see how to construct your Device API request.

About the Device API

This API is automatically available to paying Duo Beyond plan customers and new customers with a Beyond trial who are using Trusted Endpoints Generic management integrations with the Duo Device Health App to establish trust.

The typical usage of this API will be to start by creating a new device cache. At this point, the device cache is in a pending status and devices added to the device cache are not trusted. If an existing active device cache exists, it is not affected. Next, add devices to the pending cache. This may require more than one operation depending on the number of devices to be added. When all devices have been added to the pending device cache, activate the device cache. Activating the pending device cache will replace any existing active device and the device identifiers contained within will be trusted.

Documented properties will not be removed within a stable version of the API. Once a given API endpoint is documented to return a given property, a property with that name will always appear (although certain properties may only appear under certain conditions, like if the customer is using a specific edition). When Duo deprecates a property, the API continues to accept that property in requests, although it no longer has any effect.

Properties that enumerate choices may gain new values at any time. Duo may cease to return legacy values for properties as well. Duo will update our API documentation with changes to property values in a timely fashion, adding new property values or indicating changes to existing property values.

New, undocumented properties may also appear at any time. For instance, Duo may make available a beta feature involving extra information returned by an API endpoint. Until the property is documented here its format may change or it may even be entirely removed from our API.

First Steps

  1. Log in to the Duo Admin Panel and navigate to Trusted Endpoints.

  2. Create a Trusted Endpoints Generic with Device Health integration that uses the Duo Device Health App to establish trust. Please see the instructions for creating a Generic Device Health integration in the Generic Device Health Trust documentation.

    The API credentials for use with the Device API are provided within the context of that specific Trusted Endpoints integration.

Treat your secret key like a password
The security of your Duo device trust integration is tied to the security of your API secret key (skey). Secure it as you would any sensitive credential. Don't share it with unauthorized individuals or email it to anyone under any circumstances!

API Clients

Duo Security has demonstration clients available on Github to call the Duo API methods. Examples are available in: Python, Java, C#, Ruby, Perl, and PHP.

API Details

Base URL

All API methods use your API hostname, https://api-XXXXXXXX.duosecurity.com. Obtain this value from the Duo Admin Panel and use it exactly as shown there.

Methods always use HTTPS. Unsecured HTTP is not supported.

Request Format

All requests must have "Authorization" and "Date" headers.

If the request method is GET or DELETE, URL-encode parameters and send them in the URL query string like this: /device/v1/management_systems/[mkey]/device_cache?status=active. They still go on a separate line when creating the string to sign for an Authorization header.

Send parameters for POST requests in the body as URL-encoded key-value pairs (the same request format used by browsers to submit form data). The header "Content-Type: application/x-www-form-urlencoded" must also be present.

When URL-encoding, all bytes except ASCII letters, digits, underscore ("_"), period ("."), tilde ("~"), and hyphen ("-") are replaced by a percent sign ("%") followed by two hexadecimal digits containing the value of the byte. For example, a space is replaced with "%20" and an at-sign ("@") becomes "%40". Use only upper-case A through F for hexadecimal digits.

A request with parameters, as a complete URL, would look like this: https://api-XXXXXXXX.duosecurity.com/device/v1/management_systems/[mkey]/device_cache?status=active.

Response Format

Responses are formatted as a JSON object with a top-level stat key.

Successful responses will have a stat value of "OK" and a response key. The response will either be a single object or a sequence of other JSON types, depending on which endpoint is called.

{
  "stat": "OK",
  "response": {
    "key": "value"
  }
}

Values are returned as strings unless otherwise documented.

Unsuccessful responses will have a stat value of "FAIL", an integer code, and a message key that further describes the failure. A message_detail key may be present if additional information is available (like the specific parameter that caused the error).

{
  "stat": "FAIL",
  "code": 40002,
  "message": "Invalid request parameters",
  "message_detail": "username"
}

The HTTP response code will be the first three digits of the more specific code found inside the JSON object. Each endpoint's documentation lists HTTP response codes it can return. Additionally, all API endpoints that require a signed request can return the following HTTP response codes:

Response Meaning
200 The request completed successfully.
401 The "Authorization", "Date", and/or "Content-Type" headers were missing or invalid.
403

This integration is not authorized for this endpoint or the ikey was created for a different integration type (for example, using an Auth API ikey with Device API endpoints).

405 The request's HTTP verb is not valid for this endpoint (for example, POST when only GET is supported).
429 The account has made too many requests of this type recently. Try again later.

Authentication

The API uses HTTP Basic Authentication to authenticate requests. Use your Duo application's integration key as the HTTP Username.

Generate the HTTP Password as an HMAC signature of the request. This will be different for each request and must be re-generated each time.

To construct the signature, first build an ASCII string from your request, using the following components:

Component Description Example
date The current time, formatted as RFC 2822. This must be the same string as the "Date" header. Tue, 21 Aug 2012 17:29:18 -0000
method The HTTP method (uppercase) POST
host Your API hostname (lowercase) api-xxxxxxxx.duosecurity.com
path

The specific API method's path

/device/v1/management_systems/[mkey]/device_cache
params

The URL-encoded list of key=value pairs, lexicographically sorted by key. These come from the request parameters (the URL query string for GET and DELETE requests or the request body for POST requests).

If the request does not have any parameters one must still include a blank line in the string that is signed.

Do not encode unreserved characters. Use upper-case hexadecimal digits A through F in escape sequences.

An example params list:

status=active

Then concatenate these components with (line feed) newlines. For example:

Tue, 21 Aug 2012 17:29:18 -0000
POST
api-xxxxxxxx.duosecurity.com
/device/v1/management_systems/[mkey]/device_cache
status=active

GET requests also use this five-line format:

Tue, 21 Aug 2012 17:29:18 -0000
GET
api-xxxxxxxx.duosecurity.com
/device/v1/management_systems/[mkey]/device_cache

Lastly, compute the HMAC-SHA1 of this canonical representation, using your Duo Device API application's secret key as the HMAC key. Send this signature as hexadecimal ASCII (i.e. not raw binary data). Use HTTP Basic Authentication for the request, using your integration key as the username and the HMAC-SHA1 signature as the password. Signature validation is case-insensitive, so the signature may be upper or lowercase.

For example, here are the headers for the above POST request to api-xxxxxxxx.duosecurity.com/device/v1/management_systems/[mkey]/device_cache, using DME0XUC77ATL3J05HSTB as the mkey, DIWJ8X6AEYOR5OMC6TQ1 as the integration key, and Zh5eGmUq9zpfQnyUIu5OL9iWoMMv5ZNmk3zLJ4Ep as the secret key:

Date: Tue, 21 Aug 2012 17:29:18 -0000
Authorization: Basic RElXSjhYNkFFWU9SNU9NQzZUUTE6OTU3YTRhOTJkYWRlOWUyYWYzYmEwNWQ0ZjE4YjI0ZmY1M2MyOTRmZQ==
Host: api-xxxxxxxx.duosecurity.com
Content-Length: 35
Content-Type: application/x-www-form-urlencoded

Separate HTTP request header lines with CRLF newlines.

The following Python function can be used to construct the "Authorization" and "Date" headers:

import base64, email.utils, hmac, hashlib, urllib
  ​ ​
def sign(method, host, path, params, skey, ikey):
    """
    Return HTTP Basic Authentication ("Authorization" and "Date") headers.
    method, host, path: strings from request
    params: dict of request parameters
    skey: secret key
    ikey: integration key
    """
  ​
    # create canonical string
    now = email.utils.formatdate()
    canon = [now, method.upper(), host.lower(), path]
    args = []
    for key in sorted(params.keys()):
        val = params[key].encode("utf-8")
        args.append(
            '%s=%s' % (urllib.parse.
                       quote(key, '~'), urllib.parse.quote(val, '~')))
    canon.append('&'.join(args))
    canon = '\n'.join(canon)
  ​
    # sign canonical string
    sig = hmac.new(bytes(skey, encoding='utf-8'),
                   bytes(canon, encoding='utf-8'),
                   hashlib.sha1)
    auth = '%s:%s' % (ikey, sig.hexdigest())
  ​
    # return headers
    return {'Date': now, 'Authorization': 'Basic %s' % base64.b64encode(bytes(auth, encoding="utf-8")).decode()}
import base64, email, hmac, hashlib, urllib

def sign(method, host, path, params, skey, ikey):
    """
    Return HTTP Basic Authentication ("Authorization" and "Date") headers.
    method, host, path: strings from request
    params: dict of request parameters
    skey: secret key
    ikey: integration key
    """

    # create canonical string
    now = email.Utils.formatdate()
    canon = [now, method.upper(), host.lower(), path]
    args = []
    for key in sorted(params.keys()):
        val = params[key]
        if isinstance(val, unicode):
            val = val.encode("utf-8")
        args.append(
            '%s=%s' % (urllib.quote(key, '~'), urllib.quote(val, '~')))
    canon.append('&'.join(args))
    canon = '\n'.join(canon)

    # sign canonical string
    sig = hmac.new(skey, canon, hashlib.sha1)
    auth = '%s:%s' % (ikey, sig.hexdigest())

    # return headers
    return {'Date': now, 'Authorization': 'Basic %s' % base64.b64encode(auth)}

Device API Endpoints

Create New Device Cache

Create a new management system device cache in pending status.

POST /device/v1/management_systems/[mkey]/device_cache

Parameters

None.

Response Codes

Response Meaning
200

Success. New device upload cache was created successfully.

401

Invalid identity in request credentials.

403

The target management system does not support creation of a cache.

404

No management system was found with the given mkey.

409

Cannot create new cache with existing pending cache. You may wish to DELETE the pending cache.

500

Device cache creation failed.

Response Format

Key Value
cache_key

Device cache unique identifier.

url

URL representing the new device cache.

Example Response

{
    "stat": "OK",
    "response": {
        "cache_key": "DCBWLKD6JO8U9A0N3G6Q",
        "url": "https://api-XXXXXXXX.duosecurity.com/device/v1/management_systems/DM2J8BJ480EK85TIMONU/device_cache/DCBWLKD6JO8U9A0N3G6Q"
    }
}

Add Devices to a Device Cache

Add devices to a device cache that has not yet been activated.

POST /device/v1/management_systems/[mkey]/device_cache/[cache_key]/devices

Object limits

1,000 device IDs per request; 250,000 device IDs per cache.

Parameters

Parameter Required? Description
devices Required

List of devices to be added in JSON format.

LimitEx: [{"device_id": "" },
{"device_id": "" }, ...]

Response Codes

Response Meaning
200

Success. Devices added to the device cache.

400

Invalid parameters.

401

Invalid identity in request credentials.

404

No cache key cache_key for the management system with mkey exists or no management system was found with the given mkey.

409

Cache is active. Devices could not be added because <count of device IDs> would exceed the limit of <max device IDs per cache>.

413

<num devices> devices are more than the limit of <num devices limit> devices.

500

Device cache creation failed.

Response Format

Key Value
cache_key

Device cache unique identifier.

date_created

Date and time of device cache created creation.

device_count

Total number of devices added to this cache.

Example Response

{
    "stat": "OK",
    "response": {
        "cache_key": "DCOIV2VVMX5IFX1OW8S2",
        "date_created": "2022-02-15T17:02:22",
        "device_count": 103
    }
}

Activate a Device Cache

Delete the existing activated device cache and activate this device cache.

POST /device/v1/management_systems/[mkey]/device_cache/[cache_key]/activate

Parameters

None.

Response Codes

Response Meaning
200

Success. New device upload cache was created successfully.

401

Invalid identity in request credentials.

404

No cache key cache_key for the management system with mkey exists or no management system was found with the given mkey.

409

Cannot activate already active cache cache_key for management system mkey.

500

Device cache activation failed.

Response Format

Empty string.

Example Response

{
  "stat": "OK",
  "response": ""
}

Delete a pending device cache

Deletes a pending device cache.

DELETE /device/v1/management_systems/[mkey]/device_cache/[cache_key]

Parameters

None.

Response Codes

Response Meaning
200

Success. Device cache deleted.

401

Invalid identity in request credentials.

403

The target management system does not support cache deletion.

404

No cache key cache_key for the management system with mkey exists or no management system was found with the given mkey.

409

Cannot delete active cache cache_key for management system mkey.

500

Device cache deletion failed.

Response Format

Empty string.

Example Response

{
  "stat": "OK",
  "response": ""
}

Retrieve All Existing Caches

Retrieve all caches with provided status, or all caches with any status if none specified.

GET /device/v1/management_systems/[mkey]/device_cache

Query Parameters

Parameter Required? Description
status Required Device cache status as “active” or “pending”.

Response Codes

Response Meaning
200

Success. Device cache retrieved.

401

Invalid identity in request credentials.

404

No management system was found with the given mkey.

500

Failed to retrieve device cache.

Response Format

Key Value
cache_key

Device cache unique identifier.

date_created

Date and time of device cache created creation.

device_count

Total number of devices added to this cache.

status

Status of the cache as either “active” or “pending”.

url

Device cache URL.

Example Response

{
    "stat": "OK",
    "response": [{
        "cache_key": "DC91GTT7V1FE1PTFNBUW",
        "date_created": "2022-02-14T16:48:08",
        "device_count": 123,
        "status": "active",
        "url": "https://api-XXXXXXXX.duosecurity.com/device/v1/management_systems/DMYTA3HRS9YVB96CORJ6/device_cache/DC91GTT7V1FE1PTFNBUW"
    },
    {
        "cache_key": "DCIRGVRCMUYLHBLII0OR",
        "date_created": "2022-02-15T16:48:12",
        "device_count": 125,
        "status": "pending",
        "url": "https://api-XXXXXXXX.duosecurity.com/device/v1/management_systems/DMYTA3HRS9YVB96CORJ6/device_cache/DCIRGVRCMUYLHBLII0OR"
    }
]}

Retrieve Cache

Retrieve a device cache.

GET /device/v1/management_systems/[mkey]/device_cache/[cache_key]

Parameters

None.

Response Codes

Response Meaning
200

Success. Device upload cache retrieved.

401

Invalid identity in request credentials.

404

No cache key cache_key for the management system with mkey exists or no management system was found with the given mkey.

500

Failed to retrieve device cache.

Response Format

Key Value
cache_key

Device cache unique identifier.

date_created

Date and time the device cache was created.

device_count

Total number of devices added to this cache.

status

Status of the cache as either “active” or “pending”.

url

URL representing the new device cache.

Example Response

{
    "stat": "OK",
    "response": {
        "cache_key": "DC91GTT7V1FE1PTFNBUW",
        "date_created": "2022-02-14T16:48:08",        
        "device_count": 123,
        "status": "active",        
        "url": "https://api-XXXXXXXX.duosecurity.com/device/v1/management_systems/DMYTA3HRS9YVB96CORJ6/device_cache/DC91GTT7V1FE1PTFNBUW"
    }
}

Troubleshooting

Need some help? Take a look at our Device API Knowledge Base articles or Community discussions. For further assistance, contact Support.