Risk API


The Risk API provides you with an Application Program Interface (API) to query risk states of your enrolled devices. With this API, you can integrate current risk states of your devices into your applications and scripts.

The API is RESTful and HTTP-based. This means that the communication is made through normal HTTP requests.

If at any point you require any further assistance, please reach out to your Jamf support representative.

Getting Started

  1. From RADAR get your Application ID and Secret.
  2. Authenticate with the following terminal command replacing <Application ID> and <Application Secret> with the ones retrieved on the previous step:
    • curl -X POST -u '<Application ID>:<Application Secret>' <https://api.wandera.com/v1/login>
  3. Copy the JWT value from the token field in the response without double quotations.
  4. Get your devices’ risk with following terminal command replacing the<JWT> from previous step:
    • curl -X GET -H 'Authorization: Bearer <JWT>' https://api.wandera.com/risk/v1/devices


Authentication is needed in order to use the Risk API

The authentication used is in the form of a JSON Web Token (JWT). Each request to the API needs to contain a Bearer type Authorization header containing a valid JWT. A JWT can be obtained by calling the /v1/login endpoint via POST request using a valid Application ID and Application secret as HTTP Basic Authorization credentials. These credentials can be created in the Security Integrations part of RADAR Settings. Each JWT has a limited validity of 15 minutes, during which it can be used.

You can find out more about JWT and decode a token on https://jwt.io/.


Using curl

Once you have your Application ID and secret from RADAR you can add them into the following in a terminal or console:

$ curl \
-u '<Application ID>:<Application secret>' \

This returns the encoded JWT, which has the following format:

{"token":"<JWT header (base 64)>.<JWT payload (base 64)>.<JWT signature>"}

Using jwt.io link above you can decode the payload of this particular JWT:

  "customer_id": "4744defa-1042-4a85-9fff-763ae00c7584",
  "iat": 1578318013,
  "exp": 1578318913,
  "aud": "RISK_API",
  "client_id": "df3e7a9a-585e-475a-87d5-aa1e83c0cbf1"

Note the iat and exp fields in the decoded token where you can check when a JWT was issued and when it expires.


Before we get to an actual example of getting data, let’s explain how pagination works.
All non-login request responses support pagination. Requests can include the number of entries per page and the requested page number in the pageSize and page query parameters respectively. The maximum number of entries on a page is limited to 100 and the default is set to 20. Page number 0 (i.e. the first one) is returned by default.


By using JWT obtained in the previous authentication examples and query parameters pageSize = 1 and page = 0 we can get just the status of the first device, leaving the output minimal for example purposes:

$ curl \
-X GET \
-H 'Authorization: Bearer <JWT token>' \

The JSON response is compacted, this is how it would look expanded for better readability:

            "deviceId": "523f1637-7275-4039-a5e7-da708ba6d7bb",
            "externalId": "912eea6c-7550-40cc-8f1e-1bed218f59db",
            "customerId": "d6777e3c-be7b-44a4-8cde-722b671cd2fb",
            "risk": "SECURE",
                "active": true,
                "lastStatusUpdate": "2021-08-31T16:20:08Z",
                "lastNetworkTraffic": "2021-08-31T16:25:01Z",
                "currentLocation": "GB",
                "lastActivationState": "ACTIVE"
                "enrolled": true,
                "lastUemSync": "2021-08-31T02:00:02.686Z"
                "name": "John Doe's iPhone",
                "osType": "IOS",
                "osVersion": "14.7.1",
                "phoneNumber": "+15555555555"
        "totalRecords": 350,
        "totalPages": 350,
        "currentPage": 0,
        "pageSize": 1

Rate Limiting

The Risk API has rate limits to avoid applications degrading the user experience. There are individual limits for requests obtaining JWTs and for requests querying the device statuses.

The current values of the limits are the same for both types – 5 per second and 10,000 per day. Requests are counted for each application integration individually so one application won’t block other applications integrated into the same account.

When a request is blocked, the body is empty and time (in milliseconds) until the next possible non-limited request is returned via X-Rate-Limit-Retry-After-Milliseconds response header field. Blocked requests count up to the limits too.

Example of headers of a blocked request:

$ curl \
-D - \
-X GET \
-H 'Authorization: Bearer <JWT token>' \

HTTP/2 429
referrer-policy: no-referrer
strict-transport-security: max-age=315360000
vary: Accept-Encoding
x-rate-limit-retry-after-milliseconds: 40298409
content-length: 0
date: Mon, 06 Jan 2020 13:48:21 GMT

Error Messages

When an error occurs during an API call, the user is notified about it with a well-defined error response.

Error codes

Error responses contain machine-parseable error codes. While the error messages may change in the future, the error codes stay the same all the time.

NEGATIVE_PAGE400Page number cannot be negative.
NEGATIVE_PAGE_SIZE400Page size number cannot be negative.
MAX_LIMIT_PER_PAGE400The maximum limit of records per page is 100.
UNKNOWN_URI_PATH404The requested URI does not exist.
Forbidden403Access Denied
TOO_MANY_REQUESTS429Too Many Requests.

The error response is structured in the following format.

    "logref": "ktzHgyO4TXaaVH2h-NaFxw",
    "message": "Page number cannot be negative.",
    "error": "NEGATIVE_PAGE",
    "statusCode": 400

Field nameData typeDescription
logrefStringLogging reference
messageStringHuman-readable error description
errorStringFixed error code
statusCodeIntegerHTTP status code

Unauthorized Errors

An unauthorized error might occur during using the API, which could be like the following:

  "timestamp": "2020-01-06T15:11:17.882+0000",
  "status": 403,
  "error": "Forbidden",
  "message": "Access Denied",
  "path": "/v1/tokens"

Where the timestamp and path fields may differ for this error.