> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreflux.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Mender

> Client-mode REST API connector for Mender OTA updates: list, search, tag and group devices, accept pending devices, list releases, start a deployment to devices or a whole group, track and abort it, and ask a device to check for an update or re-send its inventory

Technical Preview

## Overview

One client-mode connector, **Mender**, for the [Mender](https://mender.io) Management APIs on hosted Mender (`https://hosted.mender.io`). Mender is an open-source over-the-air (OTA) update manager for Linux edge devices. The connector covers four of its services, all behind the same token:

* **Device inventory**: list, read and search devices, tag them and put them in groups;
* **Device authentication**: list pending devices and accept or reject them;
* **Deployments**: list releases, start a deployment to devices or to a group, read its statistics and abort it;
* **Mender Connect**: ask an online device to check for an update or re-send its inventory right now.

Use it to drive gateway updates from the plant floor: tag each gateway with its UNS location, accept a newly installed gateway when commissioning finishes, roll a release out to one line only when that line reports it is idle, and abort a rollout when an alarm fires.

| Event | Request | Notes |
| - | - | - |
| Login | `POST /v1/useradm/auth/login` | Basic `email:password`; returns a JWT as text |
| ListDevices | `GET /v1/inventory/devices?{query}` | Pass the query string in `query`, for example `group=line-1&per_page=50` |
| GetDevice | `GET /v1/inventory/devices/{device_id}` | All inventory, identity and tag attributes |
| SearchDevices | `POST /v2/inventory/filters/search` | Filter by any inventory attribute |
| AddDeviceTags | `PATCH /v1/inventory/devices/{device_id}/tags` | Body is a JSON array of `{name, value}` |
| AssignDeviceGroup | `PUT /v1/inventory/devices/{device_id}/group` | Body `{"group": "line-1"}`; returns `204` |
| ListGroups | `GET /v1/inventory/groups` | |
| ListGroupDevices | `GET /v1/inventory/groups/{group}/devices` | Device ids in one group |
| InventoryStatistics | `GET /v2/inventory/statistics` | Device counts by status |
| ListAuthDevices | `GET /v2/devauth/devices?{query}` | `query` such as `status=pending` |
| GetAuthDevice | `GET /v2/devauth/devices/{device_id}` | Identity data and auth sets (`auth_id`) |
| SetAuthStatus | `PUT /v2/devauth/devices/{device_id}/auth/{auth_id}/status` | Accept or reject a device; returns `204` |
| CountDevices | `GET /v2/devauth/devices/count?status={status}` | Always pass `status` |
| ListReleases | `GET /v2/deployments/deployments/releases?{query}` | `query` such as `per_page=20&name=press-fw` |
| ListDeployments | `GET /v2/deployments/deployments?{query}` | `query` such as `status=inprogress` |
| CreateDeployment | `POST /v1/deployments/deployments` | Deploy an artifact to a list of device ids; returns `201` |
| CreateGroupDeployment | `POST /v1/deployments/deployments/group/{group}` | Deploy to every device in a group; returns `201` |
| GetDeployment | `GET /v1/deployments/deployments/{deployment_id}` | |
| GetDeploymentStatistics | `GET /v1/deployments/deployments/{deployment_id}/statistics` | Per-status device counts |
| AbortDeployment | `PUT /v1/deployments/deployments/{deployment_id}/status` | Body `{"status": "aborted"}`; returns `204` |
| ListDeviceDeployments | `GET /v1/deployments/deployments/devices/{device_id}` | Deployment history of one device |
| CheckUpdate | `POST /v1/deviceconnect/devices/{device_id}/check-update` | Device checks for an update now (Mender Connect); `202` |
| SendInventory | `POST /v1/deviceconnect/devices/{device_id}/send-inventory` | Device re-sends its inventory now (Mender Connect); `202` |

All paths start with `/api/management`. Topics use `cloud-iot/mender/<object>/<verb>`; replies land on `<topic>/response`. For hosted Mender in the EU, change `BASE_ADDRESS` to `https://eu.hosted.mender.io`. For a self-hosted Mender Server, use your own server address.

> [!WARNING]
>
> Never commit a token. Store it as a broker secret (for example `MENDER_TOKEN`) and map it into payloads with `GET SECRET`. A Personal Access Token has every permission of the user who created it, including starting deployments to your whole fleet. Use a user with a restricted role where your plan allows it, and protect the `cloud-iot/mender/#` topics with a rule.

## Authentication

Every event except `Login` sends `Authorization: Bearer <token>`. Use a **Personal Access Token**: create it on the **My profile** page of the Mender UI, or call `POST /api/management/v1/useradm/settings/tokens` as described in [Using the APIs](https://docs.mender.io/server-integration/using-the-apis). Tokens last up to one year, and Mender revokes them when your plan or add-ons change.

Pass the token as `token` on every payload. A missing, expired or invalid token returns `401`.

`Login` exchanges `email:password` for a user JWT that expires after about a week. Pass `basic_auth` as the Base64 of `email:password`; the response is the JWT as plain text. Prefer a Personal Access Token on a broker: no password is stored, and accounts with two-factor authentication cannot use Basic login.

## Basic Syntax

```lot theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE Mender WITH TYPE REST_API
    ADD REST_API_CONFIG
        WITH BASE_ADDRESS "https://hosted.mender.io"
        WITH ENABLE_CLIENT "true"
        WITH USE_SSL "true"
        WITH REQUEST_TIMEOUT '30'
        WITH DEFAULT_CONTENT_TYPE "application/json"
        # Smoke: CountDevices (GET), ListDevices (GET), AddDeviceTags (PATCH) and CreateDeployment (POST) on a test group.
    ADD EVENT Login
        WITH SOURCE_TOPIC "cloud-iot/mender/auth/login"
        WITH DESTINATION_TOPIC "cloud-iot/mender/auth/login/response"
        WITH METHOD "POST"
        WITH ENDPOINT "/api/management/v1/useradm/auth/login"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Content-Type" "application/json"
            ADD "Authorization" "Basic {value.json.basic_auth}"
        WITH BODY "{value.json.body}"
    ADD EVENT ListDevices
        WITH SOURCE_TOPIC "cloud-iot/mender/device/list"
        WITH DESTINATION_TOPIC "cloud-iot/mender/device/list/response"
        WITH METHOD "GET"
        WITH ENDPOINT "/api/management/v1/inventory/devices?{value.json.query}"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
    ADD EVENT GetDevice
        WITH SOURCE_TOPIC "cloud-iot/mender/device/get"
        WITH DESTINATION_TOPIC "cloud-iot/mender/device/get/response"
        WITH METHOD "GET"
        WITH ENDPOINT "/api/management/v1/inventory/devices/{value.json.device_id}"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
    ADD EVENT SearchDevices
        WITH SOURCE_TOPIC "cloud-iot/mender/device/search"
        WITH DESTINATION_TOPIC "cloud-iot/mender/device/search/response"
        WITH METHOD "POST"
        WITH ENDPOINT "/api/management/v2/inventory/filters/search"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Content-Type" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
        WITH BODY "{value.json.body}"
    ADD EVENT AddDeviceTags
        WITH SOURCE_TOPIC "cloud-iot/mender/device-tags/add"
        WITH DESTINATION_TOPIC "cloud-iot/mender/device-tags/add/response"
        WITH METHOD "PATCH"
        WITH ENDPOINT "/api/management/v1/inventory/devices/{value.json.device_id}/tags"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Content-Type" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
        WITH BODY "{value.json.body}"
    ADD EVENT AssignDeviceGroup
        WITH SOURCE_TOPIC "cloud-iot/mender/device-group/assign"
        WITH DESTINATION_TOPIC "cloud-iot/mender/device-group/assign/response"
        WITH METHOD "PUT"
        WITH ENDPOINT "/api/management/v1/inventory/devices/{value.json.device_id}/group"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Content-Type" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
        WITH BODY "{value.json.body}"
    ADD EVENT ListGroups
        WITH SOURCE_TOPIC "cloud-iot/mender/group/list"
        WITH DESTINATION_TOPIC "cloud-iot/mender/group/list/response"
        WITH METHOD "GET"
        WITH ENDPOINT "/api/management/v1/inventory/groups"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
    ADD EVENT ListGroupDevices
        WITH SOURCE_TOPIC "cloud-iot/mender/group/devices"
        WITH DESTINATION_TOPIC "cloud-iot/mender/group/devices/response"
        WITH METHOD "GET"
        WITH ENDPOINT "/api/management/v1/inventory/groups/{value.json.group}/devices"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
    ADD EVENT InventoryStatistics
        WITH SOURCE_TOPIC "cloud-iot/mender/inventory/statistics"
        WITH DESTINATION_TOPIC "cloud-iot/mender/inventory/statistics/response"
        WITH METHOD "GET"
        WITH ENDPOINT "/api/management/v2/inventory/statistics"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
    ADD EVENT ListAuthDevices
        WITH SOURCE_TOPIC "cloud-iot/mender/auth-device/list"
        WITH DESTINATION_TOPIC "cloud-iot/mender/auth-device/list/response"
        WITH METHOD "GET"
        WITH ENDPOINT "/api/management/v2/devauth/devices?{value.json.query}"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
    ADD EVENT GetAuthDevice
        WITH SOURCE_TOPIC "cloud-iot/mender/auth-device/get"
        WITH DESTINATION_TOPIC "cloud-iot/mender/auth-device/get/response"
        WITH METHOD "GET"
        WITH ENDPOINT "/api/management/v2/devauth/devices/{value.json.device_id}"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
    ADD EVENT SetAuthStatus
        WITH SOURCE_TOPIC "cloud-iot/mender/auth-device/set-status"
        WITH DESTINATION_TOPIC "cloud-iot/mender/auth-device/set-status/response"
        WITH METHOD "PUT"
        WITH ENDPOINT "/api/management/v2/devauth/devices/{value.json.device_id}/auth/{value.json.auth_id}/status"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Content-Type" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
        WITH BODY "{value.json.body}"
    ADD EVENT CountDevices
        WITH SOURCE_TOPIC "cloud-iot/mender/auth-device/count"
        WITH DESTINATION_TOPIC "cloud-iot/mender/auth-device/count/response"
        WITH METHOD "GET"
        WITH ENDPOINT "/api/management/v2/devauth/devices/count?status={value.json.status}"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
    ADD EVENT ListReleases
        WITH SOURCE_TOPIC "cloud-iot/mender/release/list"
        WITH DESTINATION_TOPIC "cloud-iot/mender/release/list/response"
        WITH METHOD "GET"
        WITH ENDPOINT "/api/management/v2/deployments/deployments/releases?{value.json.query}"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
    ADD EVENT ListDeployments
        WITH SOURCE_TOPIC "cloud-iot/mender/deployment/list"
        WITH DESTINATION_TOPIC "cloud-iot/mender/deployment/list/response"
        WITH METHOD "GET"
        WITH ENDPOINT "/api/management/v2/deployments/deployments?{value.json.query}"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
    ADD EVENT CreateDeployment
        WITH SOURCE_TOPIC "cloud-iot/mender/deployment/create"
        WITH DESTINATION_TOPIC "cloud-iot/mender/deployment/create/response"
        WITH METHOD "POST"
        WITH ENDPOINT "/api/management/v1/deployments/deployments"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Content-Type" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
        WITH BODY "{value.json.body}"
    ADD EVENT CreateGroupDeployment
        WITH SOURCE_TOPIC "cloud-iot/mender/deployment/create-group"
        WITH DESTINATION_TOPIC "cloud-iot/mender/deployment/create-group/response"
        WITH METHOD "POST"
        WITH ENDPOINT "/api/management/v1/deployments/deployments/group/{value.json.group}"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Content-Type" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
        WITH BODY "{value.json.body}"
    ADD EVENT GetDeployment
        WITH SOURCE_TOPIC "cloud-iot/mender/deployment/get"
        WITH DESTINATION_TOPIC "cloud-iot/mender/deployment/get/response"
        WITH METHOD "GET"
        WITH ENDPOINT "/api/management/v1/deployments/deployments/{value.json.deployment_id}"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
    ADD EVENT GetDeploymentStatistics
        WITH SOURCE_TOPIC "cloud-iot/mender/deployment/statistics"
        WITH DESTINATION_TOPIC "cloud-iot/mender/deployment/statistics/response"
        WITH METHOD "GET"
        WITH ENDPOINT "/api/management/v1/deployments/deployments/{value.json.deployment_id}/statistics"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
    ADD EVENT AbortDeployment
        WITH SOURCE_TOPIC "cloud-iot/mender/deployment/abort"
        WITH DESTINATION_TOPIC "cloud-iot/mender/deployment/abort/response"
        WITH METHOD "PUT"
        WITH ENDPOINT "/api/management/v1/deployments/deployments/{value.json.deployment_id}/status"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Content-Type" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
        WITH BODY "{value.json.body}"
    ADD EVENT ListDeviceDeployments
        WITH SOURCE_TOPIC "cloud-iot/mender/device-deployment/list"
        WITH DESTINATION_TOPIC "cloud-iot/mender/device-deployment/list/response"
        WITH METHOD "GET"
        WITH ENDPOINT "/api/management/v1/deployments/deployments/devices/{value.json.device_id}"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
    ADD EVENT CheckUpdate
        WITH SOURCE_TOPIC "cloud-iot/mender/device/check-update"
        WITH DESTINATION_TOPIC "cloud-iot/mender/device/check-update/response"
        WITH METHOD "POST"
        WITH ENDPOINT "/api/management/v1/deviceconnect/devices/{value.json.device_id}/check-update"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
        WITH BODY ""
    ADD EVENT SendInventory
        WITH SOURCE_TOPIC "cloud-iot/mender/device/send-inventory"
        WITH DESTINATION_TOPIC "cloud-iot/mender/device/send-inventory/response"
        WITH METHOD "POST"
        WITH ENDPOINT "/api/management/v1/deviceconnect/devices/{value.json.device_id}/send-inventory"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "Authorization" "Bearer {value.json.token}"
        WITH BODY ""
```

## MQTT examples

Request bodies go under `body`, so control fields like `token`, `device_id` and `group` are not sent to Mender.

**Accept a new gateway**: list pending devices by publishing to `cloud-iot/mender/auth-device/list`:

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "token": "<Mender token>",
  "query": "status=pending"
}
```

Each device in the reply has an `id` and an `auth_sets` list. Accept it on `cloud-iot/mender/auth-device/set-status`:

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "token": "<Mender token>",
  "device_id": "<device id>",
  "auth_id": "<auth set id>",
  "body": {"status": "accepted"}
}
```

**Tag a device with its UNS location and put it in a group**: publish to `cloud-iot/mender/device-tags/add`:

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "token": "<Mender token>",
  "device_id": "<device id>",
  "body": [{"name": "uns_path", "value": "porto/line1/press-07"}, {"name": "line", "value": "line-1"}]
}
```

Then `{"token": "<token>", "device_id": "<device id>", "body": {"group": "line-1"}}` on `cloud-iot/mender/device-group/assign`. A device belongs to one group at a time.

**Find devices by inventory attribute**: publish to `cloud-iot/mender/device/search`:

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "token": "<Mender token>",
  "body": {
    "page": 1,
    "per_page": 20,
    "filters": [{"scope": "inventory", "attribute": "device_type", "type": "$eq", "value": "raspberrypi4"}]
  }
}
```

**Roll a release out to one line**: publish to `cloud-iot/mender/deployment/create-group`:

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "token": "<Mender token>",
  "group": "line-1",
  "body": {"name": "line-1 press-fw 2.4.1", "artifact_name": "press-fw-2.4.1"}
}
```

Mender returns `201` and puts the new deployment id in the `Location` header; `ListDeployments` with `"query": "status=inprogress"` also returns it. To deploy to specific devices instead, use `cloud-iot/mender/deployment/create` with `"devices": ["<device id>", ...]` in the body. Get artifact names from `cloud-iot/mender/release/list`.

**Track and abort a rollout**: publish `{"token": "<token>", "deployment_id": "<id>"}` to `cloud-iot/mender/deployment/statistics` for per-status device counts (`pending`, `downloading`, `installing`, `success`, `failure` and more). To stop it, publish `{"token": "<token>", "deployment_id": "<id>", "body": {"status": "aborted"}}` to `cloud-iot/mender/deployment/abort`.

**Ask a device to check now**: publish `{"token": "<token>", "device_id": "<device id>"}` to `cloud-iot/mender/device/check-update` or `cloud-iot/mender/device/send-inventory`. These need Mender Connect running on the device and return `202`; the device has to be online.

## Helper actions

Keep the token in a broker secret, watch for new gateways on a timer, and let operators or line logic trigger rollouts and tags with small payloads:

```lot theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ACTION MenderPollPendingDevices
ON EVERY 10 MINUTES DO
    SET "token" WITH GET SECRET "MENDER_TOKEN"
    PUBLISH TOPIC "cloud-iot/mender/auth-device/list" WITH { "token": {token}, "query": "status=pending&per_page=50" }

DEFINE ACTION MenderDeployToGroupOnRequest
ON TOPIC "ops/mender/deploy-group" DO
    SET "token" WITH GET SECRET "MENDER_TOKEN"
    SET "group" WITH GET JSON "group" IN PAYLOAD AS STRING
    SET "artifact" WITH GET JSON "artifact" IN PAYLOAD AS STRING
    PUBLISH TOPIC "cloud-iot/mender/deployment/create-group" WITH { "token": {token}, "group": {group}, "body": { "name": {artifact}, "artifact_name": {artifact} } }

DEFINE ACTION MenderTagDeviceOnRequest
ON TOPIC "ops/mender/tag-device" DO
    SET "token" WITH GET SECRET "MENDER_TOKEN"
    SET "device" WITH GET JSON "device_id" IN PAYLOAD AS STRING
    SET "uns" WITH GET JSON "uns_path" IN PAYLOAD AS STRING
    PUBLISH TOPIC "cloud-iot/mender/device-tags/add" WITH { "token": {token}, "device_id": {device}, "body": [ { "name": "uns_path", "value": {uns} } ] }
```

`MenderPollPendingDevices` publishes the pending device list to `cloud-iot/mender/auth-device/list/response` every ten minutes. `MenderDeployToGroupOnRequest` uses the artifact name as the deployment name. Restrict who can publish to `ops/mender/#` and read `cloud-iot/mender/#` with a rule: these topics can push software to devices and their payloads carry the token.

## Notes

* Errors come back on the response topic: `401` for a missing or invalid token, `403` when your plan or role does not allow the call, `404` for an unknown device, group or deployment, `400` or `422` for a bad body or an artifact that does not exist, `409` when a deployment has no target devices or a device is not connected, and `429` when rate limited.
* `CountDevices` always sends `status=`, so pass one of `accepted`, `pending`, `rejected`, `preauthorized` or `noauth`. The `query` field on list events must be URL-encoded.
* Device ids are Mender's own ids, not your device serial numbers. Find them with `SearchDevices` or `ListDevices`.
* Artifact upload, device decommissioning, configuration (`deviceconfig`), remote terminal and file transfer, and user management are not part of this template. Upload artifacts with `mender-cli` or the UI.

## Related

* [REST API Connectors](/latest/lot-language/routes/rest-api-routes)
* [REST API app templates](/latest/lot-language/routes/rest-api-apps/index)
* [balena](/latest/lot-language/routes/rest-api-apps/balena)
* [AWS IoT Core](/latest/lot-language/routes/rest-api-apps/aws-iot-core)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.