Skip to main content

pymenderio

CI PyPI release workflow Coverage PyPI version Python versions License

Python client library for the Mender API.

Table of Contents

Features

  • Type-safe: Full type hints and Pydantic models for all API objects
  • Async/Sync: Both synchronous and asynchronous clients
  • Transparent pagination: All list operations return complete results
  • Clean API: Pythonic interface mirroring the Mender CLI

Installation

pip install pymenderio

Or with Poetry:

poetry add pymenderio

Quick Start

Synchronous Usage

from pymenderio import MenderClient

# Using a token file (default: ~/.cache/mender/authtoken)
with MenderClient.from_token_file() as client:
    # List all accepted devices
    devices = client.devices.list(status="accepted")
    for device in devices:
        print(f"{device.id}: {device.identity}")

    # Create a deployment
    deployment_id = client.deployments.create(
        name="Production rollout",
        artifact_name="myapp-v2.0",
        devices=["device-id-1", "device-id-2"],
    )
    print(f"Created deployment: {deployment_id}")

# Using a token directly
with MenderClient(server="https://hosted.mender.io", token="...") as client:
    releases = client.releases.list()

Asynchronous Usage

import asyncio
from pymenderio import AsyncMenderClient

async def main():
    async with AsyncMenderClient.from_token_file() as client:
        # List devices
        devices = await client.devices.list(status="accepted")
        
        # Get deployment stats
        stats = await client.deployments.stats("deployment-id")
        print(f"Success: {stats.success}, Pending: {stats.pending}")

asyncio.run(main())

API Coverage

Every API method documented below is available in both variants:

  • Sync: MenderClient method call
  • Async: same method name on AsyncMenderClient, awaited

Example pattern:

# sync
from pymenderio import MenderClient

with MenderClient.from_mender_cli() as client:
        devices = client.devices.list(status="accepted")
# async
from pymenderio import AsyncMenderClient

async with AsyncMenderClient.from_mender_cli() as client:
        devices = await client.devices.list(status="accepted")

Function parity reference by API family:

  • Devices methods (list, search, get, count, preauthorize, accept, reject, set_auth_status, get_auth_status, remove_auth_set, decommission, revoke_token, limits, license, auto_auth)
    • Sync form: client.devices.<method>(...)
    • Async form: await client.devices.<method>(...)
  • Deployments methods (list, get, create, stats, stats_list, devices, log, try_log, abort, device_history, abort_device)
    • Sync form: client.deployments.<method>(...)
    • Async form: await client.deployments.<method>(...)
  • Artifacts methods (list, get, upload, download, download_to_file, delete)
    • Sync form: client.artifacts.<method>(...)
    • Async form: await client.artifacts.<method>(...)
  • Releases methods (list, get, delete, update, set_tags, list_tags, list_update_types, list_delta_jobs, get_delta_job)
    • Sync form: client.releases.<method>(...)
    • Async form: await client.releases.<method>(...)
  • Inventory methods (list, get, count, get_device_group, set_device_group, clear_device_group)
    • Sync form: client.inventory.<method>(...)
    • Async form: await client.inventory.<method>(...)
  • Inventory groups methods (list, devices, add_devices, remove_devices, delete)
    • Sync form: client.inventory.groups.<method>(...)
    • Async form: await client.inventory.groups.<method>(...)
  • Inventory tags methods (list, set, delete)
    • Sync form: client.inventory.tags.<method>(...)
    • Async form: await client.inventory.tags.<method>(...)
  • Inventory filters v2 methods (attributes, search, list, create, get, update, delete, execute, statistics)
    • Sync form: client.filters.<method>(...)
    • Async form: await client.filters.<method>(...)
  • User administration methods (list_users, create_user, user_exists, get_user, update_user, delete_user, me, update_me, enable_2fa, disable_2fa, settings, set_settings, my_settings, set_my_settings, list_personal_access_tokens, create_personal_access_token, revoke_personal_access_token, list_roles, create_role, get_role, update_role, delete_role, list_permission_sets, create_permission_set, get_permission_set, update_permission_set, delete_permission_set)
    • Sync form: client.useradm.<method>(...)
    • Async form: await client.useradm.<method>(...)
  • Tenant administration methods (list_tenants, create_tenant, me, delete_inactive_tenant, cancel_tenant, update_child_tenant, update_plan, init_tenant_removal, set_tenant_status, billing_products, billing_info, init_card_update, confirm_card_update, register_billing_profile, billing_profile, update_billing_profile, change_subscription, subscription, preview_invoice, stripe_secret, contact_support)
    • Sync form: client.tenantadm.<method>(...)
    • Async form: await client.tenantadm.<method>(...)
  • Device connect methods (get_device, connect, check_update, send_inventory, playback, download, upload)
    • Sync form: client.deviceconnect.<method>(...)
    • Async form: await client.deviceconnect.<method>(...)
  • Device configure methods (get, set, deploy)
    • Sync form: client.deviceconfigure.<method>(...)
    • Async form: await client.deviceconfigure.<method>(...)
  • Device monitor methods (alerts, latest_alerts, config, set_alert_channel_status)
    • Sync form: client.devicemonitor.<method>(...)
    • Async form: await client.devicemonitor.<method>(...)
  • IoT manager methods (list_integrations, register_integration, remove_integration, set_integration_credentials, unregister_device_integrations, device_states, device_state, set_device_state, events)
    • Sync form: client.iot.<method>(...)
    • Async form: await client.iot.<method>(...)

Authentication helper parity:

  • Sync: login(...), login_and_save_session(...)
  • Async: await async_login(...), await async_login_and_save_session(...)

Major Functionality Blocks

  • Device Identity and Lifecycle Management:
    • Device listing/search, auth-set lifecycle, preauthorization, token revocation, auto-auth, decommission, and limits/license helpers.
  • OTA and Release Operations:
    • Deployments lifecycle, stats and logs, release metadata/tag management, artifact operations, and server-side delta generation visibility.
  • Inventory and Fleet Querying:
    • Inventory list/get/count, groups/tags management, and inventory v2 filters/search/statistics with saved filter workflows.
  • Administrative and Tenant Operations:
    • User administration (users, PATs, settings, RBAC) and tenant administration (tenant lifecycle, billing/profile/subscription, support).
  • Device Service Operations:
    • Device Connect, Device Configure, Device Monitor, and IoT Manager families for operational actions beyond OTA.
  • Typed Ergonomics and Compatibility:
    • Pydantic models for typed payloads/queries, sync+async parity, and compatibility fallbacks for evolving backend query parameters.

Devices (client.devices)

Method Description
list(status=...) List all devices
search(status=..., ids=...) Search devices by status and IDs
get(device_id) Get a single device
count(status=...) Count devices
preauthorize(preauth) Submit a preauthorized device identity
accept(device_id, auth_set_id) Accept a device
reject(device_id, auth_set_id) Reject a device
set_auth_status(device_id, auth_set_id, status) Set auth set status
get_auth_status(device_id, auth_set_id) Get auth set status
remove_auth_set(device_id, auth_set_id) Remove auth set
decommission(device_id) Decommission a device
revoke_token(token_id) Revoke device API token
limits() Get accepted device limits per tier
license() Get device license data (CSV)
auto_auth(request, signature=...) Automatically authenticate a device

Deployments (client.deployments)

Method Description
list(status=..., type=...) List all deployments
get(deployment_id) Get a single deployment
create(name=..., artifact_name=..., devices=...) Create a deployment
stats(deployment_id) Get deployment statistics
stats_list(deployment_ids) Get statistics for multiple deployments
devices(deployment_id, status=...) List devices in a deployment
log(deployment_id, device_id) Get device deployment log
try_log(deployment_id, device_id) Get deployment log or None if unavailable
abort(deployment_id) Abort a deployment
device_history(device_id, status=...) List deployment history for a device
abort_device(device_id) Abort active/pending deployments for a device

Artifacts (client.artifacts)

Method Description
list(name=..., device_type=...) List all artifacts
get(artifact_id) Get a single artifact
upload(file, description=...) Upload an artifact
download(artifact_id) Download an artifact
download_to_file(artifact_id, path) Download to a file
delete(artifact_id) Delete an artifact

Releases (client.releases)

Method Description
list(name=..., tag=...) List all releases
get(name) Get a single release
delete(name or [name,...]) Delete one or more releases
update(name, update) Update release fields (for example notes)
set_tags(name, tags) Replace tags for a release
list_tags() List all release tags
list_update_types() List all release update types
list_delta_jobs(sort=...) List server-side delta generation jobs
get_delta_job(job_id) Get delta generation job details

Inventory (client.inventory)

Method Description
list(group=..., filters=...) List all inventory devices
get(device_id) Get device inventory
count(group=..., filters=...) Count devices
get_device_group(device_id) Get device's group
set_device_group(device_id, group) Set device's group
clear_device_group(device_id) Remove from group

Groups (client.inventory.groups)

Method Description
list() List all group names
devices(name) List device IDs in a group
add_devices(name, device_ids) Add devices to a group
remove_devices(name, device_ids) Remove devices from a group
delete(name) Delete a group

Tags (client.inventory.tags)

Method Description
list(device_id) Get device tags
set(device_id, name, value) Set a tag
delete(device_id, name) Delete a tag

Inventory Filters v2 (client.filters)

Method Description
attributes() List filterable inventory attributes
search(params=...) Search devices with filter predicates
list() List saved filters
create(definition) Create a saved filter
get(filter_id) Get a saved filter definition
update(filter_id, definition) Update a saved filter
delete(filter_id) Delete a saved filter
execute(filter_id) Search devices using a saved filter
statistics() Get inventory statistics

User Administration (client.useradm)

Method Description
list_users() List users in the current tenant
create_user(user) Create a user
user_exists(email) Check whether a user exists
get_user(user_id) Get user by ID
update_user(user_id, user) Update user by ID
delete_user(user_id) Delete user by ID
me() Get current user information
update_me(user) Update current user information
enable_2fa(user_id="me") Enable 2FA for a user
disable_2fa(user_id="me") Disable 2FA for a user
settings() Get global settings and ETag
set_settings(settings, if_match=...) Set global settings
my_settings() Get current user settings and ETag
set_my_settings(settings, if_match=...) Set current user settings
list_personal_access_tokens() List personal access tokens
create_personal_access_token(request) Create a personal access token
revoke_personal_access_token(token_id) Revoke a personal access token
list_roles() List RBAC roles (v2)
create_role(role) Create RBAC role (v2)
get_role(role_id) Get RBAC role (v2)
update_role(role_id, role) Update RBAC role (v2)
delete_role(role_id) Delete RBAC role (v2)
list_permission_sets() List permission sets (v2)
create_permission_set(permission_set) Create permission set (v2)
get_permission_set(permission_set_id) Get permission set (v2)
update_permission_set(permission_set_id, permission_set) Update permission set (v2)
delete_permission_set(permission_set_id) Delete permission set (v2)

Tenant Administration (client.tenantadm)

Method Description
list_tenants() List child tenants
create_tenant(tenant) Create a child tenant
me() Get current tenant
delete_inactive_tenant(tenant_id) Remove inactive tenant
cancel_tenant(tenant_id, request) Request tenant cancellation
update_child_tenant(tenant_id, tenant) Update child tenant
update_plan(tenant_id, request) Request plan/add-on change
init_tenant_removal(tenant_id) Start asynchronous tenant removal
set_tenant_status(tenant_id, status) Set tenant status
billing_products() Get billing product info
billing_info() Get billing info summary
init_card_update() Initialize card update flow
confirm_card_update(intent_id) Confirm card update
register_billing_profile(profile) Register billing profile
billing_profile() Get billing profile
update_billing_profile(profile) Update billing profile
change_subscription(request) Request subscription change
subscription() Get current subscription
preview_invoice(request) Preview upcoming invoice
stripe_secret() Get Stripe client secret
contact_support(request) Send message to support

Device Connect (client.deviceconnect)

Method Description
get_device(device_id) Get current device connection state
connect(device_id, headers=...) Initiate websocket upgrade handshake for interactive session
check_update(device_id) Trigger check-update on device
send_inventory(device_id) Trigger send-inventory on device
playback(session_id, sleep_ms=..., headers=...) Initiate websocket upgrade handshake for session playback
download(device_id, path=...) Download file content and metadata from device
upload(device_id, path=..., fileobj=...) Upload file to device

Typed helper support:

  • upload(..., request=UploadFileRequest(...)) for structured upload metadata and optional inline bytes content.

Device Configure (client.deviceconfigure)

Method Description
get(device_id) Get device configuration
set(device_id, configuration) Replace device configuration
deploy(device_id, request=...) Trigger configuration deployment

Device Monitor (client.devicemonitor)

Method Description
alerts(device_id, ...) List alerts for a device
latest_alerts(device_id, ...) List latest alerts for a device
config(device_id) List monitor check configuration for a device
set_alert_channel_status(name, enabled=...) Enable/disable a global alert channel

Typed helper support:

  • alerts(..., query=AlertsQuery(...))
  • latest_alerts(..., query=LatestAlertsQuery(...))

IoT Manager (client.iot)

Method Description
list_integrations() List configured cloud integrations
register_integration(integration) Register a new integration
remove_integration(integration_id) Remove a configured integration
set_integration_credentials(integration_id, credentials) Replace integration credentials
unregister_device_integrations(device_id) Remove all integrations from a device
device_states(device_id) Get states for all integrations for a device
device_state(device_id, integration_id) Get state for one integration
set_device_state(device_id, integration_id, state) Replace desired state for one integration
events(integration_id=...) List integration events

Typed helper support:

  • events(..., query=EventsQuery(...)) for typed integration_id, page, and per_page
  • list_integrations(query=IntegrationsQuery(...)) for typed page and per_page

IntegrationsQuery(...) also supports additive client-side filters:

  • provider
  • scope
  • description_contains

Compatibility fallback strategy for future backend query params:

  • Use future_params={...} to pass through potential future server query params.
  • If backend rejects unknown params with HTTP 400/422, pymenderio retries with stable params when allow_unsupported_params_fallback=True (default), then applies client-side typed filters.

Credential variants for client.iot.register_integration(...) and client.iot.set_integration_credentials(...):

  • HTTP webhook credentials (type="http")
  • Azure IoT Hub shared access secret (type="sas")
  • AWS credentials (type="aws")

Typed helper support for integration registration:

  • register_integration(IntegrationCreateRequest(provider=..., credentials=..., scopes=...))

IoT event payload parsing for client.iot.events(...):

  • device-provisioned, device-decommissioned, device-status-changed map event.data to DeviceAuthEvent
  • device-inventory-changed maps event.data to DeviceInventoryEvent

Typed helper support for state updates:

  • set_device_state(..., DeviceStateUpdate(desired=...))

Authentication

Token File

By default, pymenderio reads the JWT token from the same cache path used by mender-cli: ~/.cache/mender/authtoken.

client = MenderClient.from_token_file()
# or specify a custom token file path
client = MenderClient.from_token_file("/path/to/authtoken")

mender-cli Compatible Config

pymenderio can also read mender-cli configuration from ~/.mender-clirc (JSON containing server and username) and combine it with the default token path:

client = MenderClient.from_mender_cli()

Direct Token

client = MenderClient(
    server="https://hosted.mender.io",
    token="your-jwt-token",
)

Login

from pymenderio.auth import login, write_token_file

token = login(
    server="https://hosted.mender.io",
    email="user@example.com",
    password="password",
    totp_code="123456",  # Optional 2FA code
)

# Optionally save for future use
write_token_file(token)

To persist server and username in mender-cli config format:

from pymenderio.auth import write_config_file

write_config_file(
    server="https://eu.hosted.mender.io",
    username="user@example.com",
)

For a single call that logs in and saves both config and token in mender-cli compatible locations:

from pymenderio.auth import login_and_save_session

token = login_and_save_session(
    server="https://eu.hosted.mender.io",
    email="user@example.com",
    password="password",
)

Examples

The repository includes runnable sync/async script pairs under examples.

Parity script pairs:

See EXAMPLES.md for:

  • script-by-script usage
  • live API validation results
  • model compatibility notes

See API.md for the full public SDK reference:

  • all public methods
  • all public classes
  • all public errors

API Reference

For a complete surface inventory, see API.md.

Error Handling

from pymenderio import MenderClient
from pymenderio.exceptions import (
    AuthenticationError,
    NotFoundError,
    ValidationError,
)

with MenderClient.from_token_file() as client:
    try:
        device = client.devices.get("nonexistent-id")
    except NotFoundError:
        print("Device not found")
    except AuthenticationError:
        print("Invalid or expired token")

Development

# Show available targets
make help

# Create local virtual environment and install dependencies
make venv

# Run tests in Docker
make test

# Lint and type-check in Docker
make lint
make typecheck

# Run all checks (lint, typecheck, test)
make check

# Remove local virtual environment
make venv-clean

If you prefer Poetry directly:

# Install dependencies
poetry install

# Run tests
poetry run pytest

# Type checking
poetry run mypy src/

# Linting
poetry run ruff check src/

Release

GitHub Actions workflows in .github/workflows/release-testpypi.yml and .github/workflows/release-pypi.yml handle package publishing.

One-time setup:

Release flow:

  • Publish a prerelease to TestPyPI with a prerelease tag, for example:
git tag v0.1.1-rc1
git push origin v0.1.1-rc1
  • Publish a final release to PyPI with a stable tag:
git tag v0.1.1
git push origin v0.1.1

The PyPI workflow validates that the tag version matches the package version in pyproject.toml.

License

MIT License - see LICENSE for details.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pymenderio-0.1.1.tar.gz (42.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pymenderio-0.1.1-py3-none-any.whl (50.4 kB view details)

Uploaded Python 3

File details

Details for the file pymenderio-0.1.1.tar.gz.

File metadata

  • Download URL: pymenderio-0.1.1.tar.gz
  • Upload date:
  • Size: 42.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pymenderio-0.1.1.tar.gz
Algorithm Hash digest
SHA256 ea005c53f7b60b31debc4311ce1f133fae83f2d2e65c57164361459151be8d09
MD5 b6997b9437f324cc91b268ed41b3d80a
BLAKE2b-256 4c3b34fdb2e9f184515e6475487ba607917bb04198f69c9f9a7d8357512f6799

See more details on using hashes here.

Provenance

The following attestation bundles were made for pymenderio-0.1.1.tar.gz:

Publisher: release.yml on boeboe/pymenderio

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pymenderio-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: pymenderio-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 50.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pymenderio-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b73c706aec5daea65aedbcc4261e8f01c0ad77dd44de3373e687b7f1a7e789d7
MD5 fe1f3c338eca8b1e42a92cb5392fd14c
BLAKE2b-256 d9f03efa9d406b8f74b226a62d3661cee387553dd9679ce28675cd6214533655

See more details on using hashes here.

Provenance

The following attestation bundles were made for pymenderio-0.1.1-py3-none-any.whl:

Publisher: release.yml on boeboe/pymenderio

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page